资讯专栏INFORMATION COLUMN

使用redis事物解决stringRedisTemplate.setIfAbsent()并设置过期时

ixlei / 2476人阅读

摘要:本因为这样就可以了,可是事实总是不尽人意,因为我在文档中发现了以下内容加了事务管理之后,的返回值竟然是,这样就没办法再进行之后的判断了。最终解决方法使用的返回值判断是否成功这里会返回事务内每一个操作的结果,如果操作失败后,会为。

spring-date-redis版本:1.6.2
场景:在使用setIfAbsent(key,value)时,想对key设置一个过期时间,同时需要用到setIfAbsent的返回值来指定之后的流程,所以使用了以下代码:

boolean store = stringRedisTemplate.opsForValue().setIfAbsent(key,value);
if(store){
  stringRedisTemplate.expire(key,timeout); 
  // todo something...  
}

这段代码是有问题的:当setIfAbsent成功之后断开连接,下面设置过期时间的代码 stringRedisTemplate.expire(key,timeout); 是无法执行的,这时候就会有大量没有过期时间的数据存在数据库。想到一个办法就是添加事务管理,修改后的代码如下:

stringRedisTemplate.setEnableTransactionSupport(true);
stringRedisTemplate.multi();
boolean store = stringRedisTemplate.opsForValue().setIfAbsent(key,value);
if(store){
  stringRedisTemplate.expire(key,timeout);   
}
stringRedisTemplate.exec();
if(store){
    // todo something...
}

这样就保证了整个流程的一致性。本因为这样就可以了,可是事实总是不尽人意,因为我在文档中发现了以下内容:

加了事务管理之后,setIfAbsent的返回值竟然是null,这样就没办法再进行之后的判断了。

好吧,继续解决:

stringRedisTemplate.setEnableTransactionSupport(true);
stringRedisTemplate.multi();
String result = stringRedisTemplate.opsForValue().get(key);
if(StringUtils.isNotBlank(result)){
    return false;
}
// 锁的过期时间为1小时
stringRedisTemplate.opsForValue().set(key, value,timeout);
stringRedisTemplate.exec();

// todo something...

上边的代码其实还是有问题的,当出现并发时,String result = stringRedisTemplate.opsForValue().get(key); 这里就会有多个线程同时拿到为空的key,然后同时写入脏数据。

最终解决方法:

使用stringRedisTemplate.exec();的返回值判断setIfAbsent是否成功

stringRedisTemplate.setEnableTransactionSupport(true);
stringRedisTemplate.multi();
stringRedisTemplate.opsForValue().setIfAbsent(lockKey,JSON.toJSONString(event));
stringRedisTemplate.expire(lockKey,Constants.REDIS_KEY_EXPIRE_SECOND_1_HOUR, TimeUnit.SECONDS);
List result = stringRedisTemplate.exec(); // 这里result会返回事务内每一个操作的结果,如果setIfAbsent操作失败后,result[0]会为false。
if(true == result[0]){
  // todo something...
}

将redis版本升级到2.1以上,然后使用


直接在setIfAbsent中设置过期时间

update :
java 使用redis的事务时不能直接用Api中的multi()和exec(),这样multi()和exec()两次使用的stringRedisTemplate不是一个connect,会导致死锁,正确方式如下:

    private Boolean setLock(RecordEventModel event) {
        String lockKey = event.getModel() + ":" + event.getAction() + ":" + event.getId() + ":" + event.getMessage_id();
        log.info("lockKey : {}" , lockKey);
        SessionCallback sessionCallback = new SessionCallback() {
            List exec = null;
            @Override
            @SuppressWarnings("unchecked")
            public Boolean execute(RedisOperations operations) throws DataAccessException {
                operations.multi();
                stringRedisTemplate.opsForValue().setIfAbsent(lockKey,JSON.toJSONString(event));
                stringRedisTemplate.expire(lockKey,Constants.REDIS_KEY_EXPIRE_SECOND_1_HOUR, TimeUnit.SECONDS);
                exec = operations.exec();
                if(exec.size() > 0) {
                    return (Boolean) exec.get(0);
                }
                return false;
            }
        };
        return stringRedisTemplate.execute(sessionCallback);
    }           
               
                                           
                       
                 
            
                     
             
               

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/73071.html

相关文章

  • 五个步骤教你理清Redis与Memcached的区别

    摘要:它们都使用来做事件循环,不过是单线程的服务器也是多线程的,只不过除了主线程以外,其他线程没有,只是会进行一些后台存储工作,而是多线程的。支持设置过期时间,即,但是内部并不定期检查数据是否过期,而是客户进程使用该数据的时候,会 欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由Super发表于云+社区专栏 memcached和redis,作为近些年最常用的缓存服务器,相...

    waterc 评论0 收藏0

发表评论

0条评论

ixlei

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<