kv数据库

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>

几个要注意的问题:

  1. 注意脚本返回的返回值ReturnType
  2. 构建RedisScript对象时省略了Lua语言中的‘eval’
  3. 注意redisTemplate.execute()方法中Lua语言的参数序列化方式
  4. 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;
}

上一篇 下一篇

猜你喜欢

热点阅读