使用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...
}
这样就保证了整个流程的一致性。本因为这样就可以了,可是事实总是不尽人意,因为我在文档中发现了以下内容:
加了事务管理之后,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<Boolean> sessionCallback = new SessionCallback<Boolean>() {
List<Object> 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);
}
使用redis事物解决stringRedisTemplate.setIfAbsent()并设置过期时间遇到的问题的更多相关文章
- 如何为Redis中list中的项设置过期时间
问题 两种解决方法 有序集合 多个集合以及TTL Redis是一个伟大的工具,用来在内存中存储列表是很合适的. 不过,如果你想要快速搜索列表,同时需要让列表中每项都在一定时间后过期,应该怎么做呢? 首 ...
- Redis原子性写入HASH结构数据并设置过期时间
Redis中提供了原子性命令SETEX或SET来写入STRING类型数据并设置Key的过期时间: > SET key value EX NX ok > SETEX key value ok ...
- redis数据库如何批量删除键和设置过期时间?
我们可以借助Linux中的xargs,在终端中执行命令来实现这两个功能. 一.批量删除键 批量删除以"key"开头key的方法,需要借助Linux中的xargs,在终端中执行以下命 ...
- redis中的key设置过期时间
EXPIRE key seconds 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除. 在 Redis 中,带有生存时间的 key 被称为『易失的 ...
- redis 一二事 - 设置过期时间,以文件夹形式展示key显示缓存数据
在使用redis时,有时回存在大量数据的时候,而且分类相同,ID相同 可以使用hset来设置,这样有一个大类和一个小分类和一个value组成 但是hset不能设置过期时间 过期时间只能在set上设置 ...
- java操作Redis缓存设置过期时间
关于Redis的概念和应用本文就不再详解了,说一下怎么在java应用中设置过期时间. 在应用中我们会需要使用redis设置过期时间,比如单点登录中我们需要随机生成一个token作为key,将用户的信息 ...
- redis批量设置过期时间
Redis 中有删除单个 Key 的指令 DEL,但好像没有批量删除 Key 的指令,不过我们可以借助 Linux 的 xargs 指令来完成这个动作.代码如下: redis-cli keys &qu ...
- redis文档翻译_key设置过期时间
Available since 1.0.0. 使用開始版本号1.01 Time complexity: O(1) 时间复杂度O(1) 出处:http://blog.csdn.net/colum ...
- python中使用redis模块, 设置过期时间
# 链接数据库self.handle = redis.Redis(host=host, port=port, db=db, password=password, decode_responses=Tr ...
随机推荐
- 04点睛Spring MVC 4.1-拦截器
转发地址:https://www.iteye.com/blog/wiselyman-2214292 4.1 拦截器 拦截器实现了对每一个请求处理之前和之后进行相关的处理,类似于Servlet的filt ...
- jvm(1)---java内存结构
jvm主要由三个子系统构成:类加载子系统,运行时数据区,执行引擎 运行时数据区主要包括: 1.本地方法栈:登记native方法,执行时加载本地方法库 2.程序计数器:就是一个指针,用来存储指向下一条执 ...
- 常见问题:计算机网络/运输层/UDP
几乎不对IP增加其他东西,无连接. 优势 速度快.适合实时. 无连接建立,没有连接时延. 无连接状态. 分组首部开销小.TCP需20字节,UDP仅需8字节. 使用UDP的协议 DNS SNMP RIP ...
- KVM虚拟机两种配置的概念不同之处
KVM虚拟机配置的两种方式之间的不同之处 NAT方式 NAT模式中,让虚拟机借助NAT(网络地址转换)功能,通过宿主机器所在的网络来访问公网. NAT模式中,虚拟机的网卡和物理网卡的网络,不在同一个网 ...
- 在 .Net 项目中生成Report小记
背景 项目为WinForm + WCF 的应用,按照给定格式生成Report,显示在WinForm窗体上并可以导出为PDF和Excel文件. 分析 之前用过DevExpress For WinForm ...
- PHP中类的继承与方法重写
php中类的继承与方法重写,欢迎大神补充指点! <?php namespace _1009; class Demo5 { //实例属性 public $product; public $pric ...
- PAT(B) 1063 计算谱半径(Java)
题目链接:1063 计算谱半径 (20 point(s)) 题目描述 在数学中,矩阵的"谱半径"是指其特征值的模集合的上确界.换言之,对于给定的 n 个复数空间的特征值 { a1 ...
- 【转载】Maven入门实践
Maven 确确实实是个好东西,用来管理项目显得很方便,但是如果是通过 Maven 来远程下载 JAR 包的话,我宿舍的带宽是4兆的,4个人共用,有时候用 Maven 来远程下载 JAR 包会显得很慢 ...
- [二叉树算法]关于判断是否为BST的算法
//判断是否为BST 搜索树==二叉排序树 1.递归知最大最小值.2.先中序判是否单调 bool IsValidBST(BTNode *p,int low,int high){ if(p==NULL) ...
- 在论坛中出现的比较难的sql问题:34(递归 获取连续值问题)
原文:在论坛中出现的比较难的sql问题:34(递归 获取连续值问题) 所以,觉得有必要记录下来,这样以后再次碰到这类问题,也能从中获取解答的思路.