Java 使用lua脚本操作redis
Java 使用lua脚本操作redis
项目背景:最近项目上有一个新的需求,请求时会带有多个用户id,需要返回统一的资源,但是不能和这部分用户的历史查看的资源有重复,为了避免资源大量浪费记录了每个用户id已使用过的资源记录
遇到的问题:在并发情况下获取用户资源记录并更新的时候会有风险,存在同一个用户返回相同资源的情况
解决方案:1.给该部分内容使用redis加锁,但是在加锁时记录每个用户id,需要用到多条redis语句,没办法保证原子性。最后使用了lua脚本对这部分进行操作。
2.使用数据库行锁,但是查询记录是多个不同的表中,分别加锁并不能达到要求。
因为需要保证每次请求都必须有返回结果,所以最终选择了方案1 。将所有的用户id保存到一个set里,有新的请求时去查询是否已经存在这部分用户,如果存在循环等待一直到加上锁,如果不存在就将这部分用户更新到set中
1.准备需要用到的lua脚本
首先是下载lua脚本插件
然后在resource下新建一个目录创建lua脚本文件
最后就是脚本文件了
for i, userId in pairs(ARGV) do
local value = redis.call('SISMEMBER', KEYS[1], userId)
if value == 1 then
return 0;
end
end
for i, userId in pairs(ARGV) do
redis.call('SADD', KEYS[1], userId)
end
return 1;
因为逻辑并不复杂,所以脚本也比较简单
2.代码中使用lua脚本
使用静态代码块直接加载脚本
private final static DefaultRedisScript<Long> lock;
static {
lock = new DefaultRedisScript<>();
// 指定脚本文件
lock.setLocation(new ClassPathResource("lua/lock.lua"));
// 指定返回值
lock.setResultType(Long.class);
}
这里高版本的jdk会有一个非法反射警告,不用管他
WARNING: An illegal reflective access operation has occurred
在需要加锁的地方使用脚本
List<String> keyList = List.of(RedisKeyConstant.USER.USER_ID);
Long result = (Long) redisTemplate.opsForSet().getOperations()
.execute(lock, keyList, userIds.toArray());
代码基本上就这些了,因为是第一次使用也遇到了一些坑,简单说明一下
遇到的坑:
1.最开始遇到的肯定是脚本写法方面的坑,不过这部分网上教程很多,磕磕绊绊的基本上都没啥,第一次使用的时候肯定都会遇到
2.返回值接收。一开始就去了解了lua脚本和redis直接的数据结构转换,发现lua中的false在redis中会变成Nil,就想着没办法用boolean类型了,所以改为了整型使用Integer进行接收,结果一直报非法状态异常,一直以为是其他地方的原因,最后偶然发现其他人是用的Long类型接收才发现是数据类型的问题(最后发现boolean类型也能接收,应该是redisTemplate封装过。。。)。
其他的一些都是小问题了,比如在redis客户端对脚本进行测试的时候需要输入密码进行验证,然后就是lua脚本中ARGV必须大写才能识别
总结:总体来说功能完成了,不过学习过程中碰到很多问题都没时间去验证,有时间了再去一一填坑吧,希望能够保持长久的学习热情。
更多推荐
所有评论(0)