Redis 渐进式遍历键

在工作中难免有时候会遇到遍历key的情景,但是redis是单线程处理命令的,如果说我们采用keys * 的指令来进行遍历的话,那么很有可能会造成redis的单线程阻塞,因为这是一个耗时操作。

那么这个时候可以采用Redis的 scan指令,进行渐进式遍历键,也可以理解为分页遍历。防止数据量过大遍历的时间过长。

redis指令 : scan cursor match pattern count querycount

例如 : scan 0 match user* count 1000

指令中, cursor 指的是游标第一次为0,pattern指的是key的匹配正则,querycount指的是,要遍历的数量( 注意不是匹配的数据数量 )

我们先模拟10万个用户 ,插入到Redis缓存中。

    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @RequestMapping("generateUserCache")
    public String generateUserCache() {
        int num = 1;
        for (int i = 0; i < 100000; i++) {
            stringRedisTemplate.opsForValue().set("user:" + num, String.valueOf(num));
            num++;
        }
        return "SUCCESS";
    }

接下来使用scan 命令渐进式遍历键,返回的第一个数值,即下一次执行scan命令时用到的游标。

当游标为0时,则表明数据已经遍历完毕。

QQ20200610-173005@2x

再次使用scan命令遍历

QQ20200610-173251@2x

我们尝试使用SpringBoot中的RedisTemplate来实现,RedisTemplate已经封装好了方法,直接调用

@RequestMapping("scanUser")
public Integer scanUser() {
    String keyPattern = "user*";
    int queryCount = 1000;
    return stringRedisTemplate.execute((RedisCallback<Integer>) connection -> {
        int dataCount = 0;
        Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder().match(keyPattern).count(queryCount).build());
        while (cursor.hasNext()) {
            String value = new String(cursor.next());
            dataCount++;
        }
        log.info("dataCount:[{}]",dataCount);
        return dataCount;
    });
}

最后得到的结果为10万,Redis中的所有user都遍历了出来。