redis scan 命令指南

redis scan 命令指南

1. 模糊查询键值

redis 中模糊查询keykeysscan等,一下是一些具体用法。

-- 命令用法:keys [pattern]
keys name*  -- 查询以name开始的key

-- 命令用法:scan cursor [match pattern] [COUNT count]
scan 0 match name* 

更多命令请参考:http://doc.redisfans.com/key/scan.html

2. keys 注意事项

虽然 keys的速度非常快,但是在一个大的数据库中,使用它还是可能造成性能问题,如果你需要从一个数据集中查找特定的key,你最好还是用 Redis 集合结构(set)来代替。

也就是说,keys 命令在生产环境不可以随便用,因为keys 会锁住 redis,并增加redis 的cpu 占用,所以很多公司的redis都禁用了这个命令。

scan就不会,因为它每次执行只返回少量的元素,所以这个命令可以用于生产环境,而不会像keyssmembers命令一样,当数据库很大时,可能会锁住数秒,这对10000Qps的redis来说是毁灭性的伤害。

3. scan 使用方式

这里使用redisTemplate来执行Redis命令,具体例子如下:

-- 1.单次查询
(ScanPageResult) redisTemplate.execute((RedisCallback<ScanPageResult>) conn -> {
            MultiKeyCommands commands = (MultiKeyCommands) conn.getNativeConnection();
            ScanParams scanParams = new ScanParams();
            scanParams.count(CommonConst.BATCH_SIZE_200);
            scanParams.match(pattern);
            ScanPageResult result = new ScanPageResult();

            ScanResult<String> scanResult = commands.scan(cursor, scanParams);
            Set<String> keys = Sets.newHashSet();
            if (scanResult.getStringCursor() != null) {
                keys.addAll(scanResult.getResult());
                if (!"0".equals(scanResult.getStringCursor())) {
                    result.setNextCursor(scanResult.getStringCursor());
                }
            }
            result.setKeys(keys);
            return result;
        });
        
-- ScanPageResult 是自己构造的对象,存储返回的keys和cursor

-- 2. 查询所有
		do {
            result = CacheUtil.scanForPage(pattern, result.getNextCursor());
            if (result == null) {
                break;
            }
            Set<String> keys = result.getKeys();
				doSomething(keys);
        } while (!"0".equals(result.getNextCursor()));

稍微解释一下,因为scan命令只会返回少量数据,而不是所有数据,所以它还需要返回一个:记录上次查询到的位置标识,这个在redis里被称为cursor(游标)。
所以下次再次查询的时候需要传入上一次返回的cursor继续查询,直到cursor=0为止,标识迭代结束,查询完毕。

一般第一次查询传入的cursor=0,作为初始查询,然后根据结果判断是否进行下一次查询。

3.1 scan 命令的保证

因为是增量式迭代查询,以保证查询所有的结果,所以,在查询间隔中新增的key,不一定会被返回。

另外,因为新增或删除key都会改变redis key的索引,所以,多次查询也会有重复的元素出现,所以使用scan命令,一定需要保证业务处理可重复执行

然而因为增量式命令仅仅使用游标来记录迭代状态, 所以这些命令带有以下缺点:
同一个元素可能会被返回多次。 处理重复元素的工作交由应用程序负责, 比如说, 可以考虑将迭代返回的元素仅仅用于可以安全地重复执行多次的操作上。
如果一个元素是在迭代过程中被添加到数据集的, 又或者是在迭代过程中从数据集中被删除的, 那么这个元素可能会被返回, 也可能不会, 这是未定义的(undefined)。

3.2 并发执行多个迭代

在同一时间, 可以有任意多个客户端对同一数据集进行迭代, 客户端每次执行迭代都需要传入一个游标, 并在迭代执行之后获得一个新的游标, 而这个游标就包含了迭代的所有状态, 因此, 服务器无须为迭代记录任何状态。

3.3 使用错误的游标进行增量式迭代

使用间断的(broken)、负数、超出范围或者其他非正常的游标来执行增量式迭代并不会造成服务器崩溃, 但可能会让命令产生未定义的行为。

未定义行为指的是, 增量式命令对返回值所做的保证可能会不再为真。

只有两种游标是合法的

  • 在开始一个新的迭代时, 游标必须为 0 。
  • 增量式迭代命令在执行之后返回的, 用于延续(continue)迭代过程的游标。

4. 参考

scan 命令

在RedisTemplate中使用scan代替keys指令

用redis的scan命令代替keys命令,以及在spring-data-redis中遇到的问题

posted @ 2020-06-03 21:36  爬行  阅读(3496)  评论(0编辑  收藏  举报