Java代码通过Lua实现Redis的scan命令(完美代替ke
2019-12-31 本文已影响0人
Zephyr006
Java代码通过Lua实现Redis的scan命令(完美代替keys命令)
【原创,转载请注明出处】
我的 【博客园主页】 【CSDN主页】 【简书主页】
加V进Java交流群,备注Java交流:w1129574379
通过执行Lua脚本的方式,使用‘scan’命令查找redis中所有满足正则表达式的key
Redis连接客户端为letture,使用Jedis的实现代码可能略有不同,但原理一样
请先了解Redis中的scan
命令,再来阅读本文
对应的Redis命令:SCAN cursor [MATCH pattern] [COUNT count]
对应的Lua命令:eval "return redis.call('scan','0','MATCH','test','count','2')" 0
更好的写法:eval "return redis.call('scan',KEYS[1],'MATCH',ARGV[1],'count',ARGV[2])" 1 '0' 'test' '2'
maven依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.1.5.RELEASE</version>
</dependency>
几个要注意的问题:
- 注意脚本返回的返回值
ReturnType
- 构建RedisScript对象时省略了Lua语言中的
‘eval’
- 注意
redisTemplate.execute()
方法中Lua语言的参数序列化方式 - scan命令存在固有缺陷
以下为Java源码:
/**
* @return keys 满足条件的redis中的key
*/
@SuppressWarnings("rawtypes")
public static Set<String> scanAll(@NotNull String pattern) {
List execList;
Set<String> keys = new HashSet();
// 每次遍历3000个key
String count = "3000";
String cursor = "0";
// 构建RedisScript对象时省略了‘eval’
// 根据命令执行后的返回值确定resultType,有默认值,不指定可能出现返回值无法正常解析的情况
// @see org.springframework.data.redis.connection.ReturnType
RedisScript<List> redisScript = RedisScript.of(
"return redis.call('scan',KEYS[1],'MATCH',ARGV[1],'count',ARGV[2])", List.class);
// 默认使用redisTemplate针对key和value的序列化方式,这种方式序列化后的args参数(pattern、count)会导致脚本无效(命令解析不正确)
// 可以尝试修改为其他序列化方式并观察效果
RedisSerializer serializer = redisTemplate.getStringSerializer();
do {
execList = redisTemplate.execute(redisScript, serializer,
serializer, Collections.singletonList(cursor), pattern, count);
assert execList != null;
// 返回值参考执行的redis命令,也可以通过调试来确定
// 如count命令:返回值的1)表示下次要开始的游标位置,返回值2)表示满足正则表达式的key值集合
cursor = String.valueOf(execList.get(0));
keys.addAll((List<String>)execList.get(1));
// 游标值返回0,表示 整个数据集(collection)已经被完整遍历过了,称这个过程为一次完整遍历(full iteration)
} while (!"0".equals(cursor));
return keys;
}