使用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 ...
随机推荐
- line-height超出隐藏显示的行数和垂直居中
line-height是css样式里设置行高的方法,在div中有overflow:hidden,可以隐藏超出div的内容,配合行高和div的高,可以设置该div里可以显示几行内容,div可以容纳几个行 ...
- 【Leetcode_easy】1029. Two City Scheduling
problem 1029. Two City Scheduling 参考 1. Leetcode_easy_1029. Two City Scheduling; 完
- AI - TensorFlow - 示例05:保存和恢复模型
保存和恢复模型(Save and restore models) 官网示例:https://www.tensorflow.org/tutorials/keras/save_and_restore_mo ...
- Python机器学习基础教程
介绍 本系列教程基本就是搬运<Python机器学习基础教程>里面的实例. Github仓库 使用 jupyternote book 是一个很好的快速构建代码的选择,本系列教程都能在我的Gi ...
- 查看cpu核的相关信息
查看CPU信息(型号) cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c 80 Intel(R) Xeon(R) CPU E7-4820 v3 ...
- PDO 查询mysql返回字段int变为String型解决方法
PDO 查询mysql返回字段int变为String型解决方法使用PDO查询mysql数据库时,执行prepare,execute后,返回的字段数据全都变为字符型. 例如id在数据库中是Int的,查询 ...
- Python2和3字符编码的区别
Python2和3字符编码的区别 一.字符编码应用之Python 1.1 执行Python程序的三个阶段 Python test.py(我再强调一遍,执行test.py的第一步,一定是先将文件内容从硬 ...
- Vue Prop属性(父to子)
通过Prop向子组件传递数据 第一步父组件中 <template> <div id="app"> <Users :users="users& ...
- Vue自定义指令和自定义过滤器
一.自定义指令: 自定义指令分为两种:全局自定义指令和局部自定义指令 全局指令指所有组件都可以使用,局部指令是只有在当前组件中才可以使用. 如,我们现在有个需求,当一个输入框获取焦点时,显示出一个di ...
- VS.NET(C#-2.5)_简单例子(所有控件都转换成HTML控件)
简单例子 UI设计视图 UI代码视图 <% @PageLanguage="C#" AutoEventWireup="true"CodeFile=&quo ...