redis set 超时_使用redis事物解决stringRedisTemplate.setIfAbsent()并设置过期时间遇到的问题...

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...

}

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

5c5fee9d138fce4db409ac71219ed7d5.png

加了事务管理之后,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,然后同时写入脏数据。

最终解决方法:

方法1. 使用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...

}

方法2. 将redis版本升级到2.1以上,然后使用

489193dccd62b6a46de2bfef3dfb3441.png

直接在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 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);

}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值