Redis学习笔记—遍历键

全量遍历键

keys pattern

查看所有的key、“J”开头的key、“T”或者“J”开头的key

127.0.0.1:6379> keys *
1) "Tom"
2) "Jerry"
3) "hello"
4) "Java"
127.0.0.1:6379> keys J*
1) "Jerry"
2) "Java"
127.0.0.1:6379> keys [T,J]*
1) "Tom"
2) "Jerry"
3) "Java"

如果redis数据量数据量有大量键, keys命令有可能造成redis阻塞,如果不清楚有多少键的情况下避免在生产环境使用keys命令

渐进式遍历

Redis从2.8版本后,提供了一个新的命令scan,它能有效的解决keys命令存在的问题。和keys命令执行时会遍历所有键不同,scan采用渐进式遍历的方式来解决keys命令可能带来的阻塞问题,每次scan命令的时间复杂度是O(1),但是要真正实现keys的功能,需要执行多次scan。

Redis存储键值对实际使用的是hashtable的数据结构,那么每次执行scan,可以想象成只扫描一个字典中的一部分键,直到将字典中的所有键遍历完毕

scan cursor [MATCH pattern] [COUNT count]
  • cursor是必需参数,实际上cursor是一个游标,第一次遍历从0开始,每次scan遍历完都会返回当前游标的值,直到游标值为0,表示遍历结束
  • match pattern是可选参数,它的作用的是做模式的匹配,这点和keys的模式匹配很像
  • count number是可选参数,它的作用是表明每次要遍历的键个数,默认值是10,此参数可以适当增大

把刚刚6666端口实例清空(flushdb命令),然后添加英文字母26个key用作测试

127.0.0.1:6666> flushdb
OK
127.0.0.1:6666> keys *
(empty list or set)
127.0.0.1:6666> mset a a b b c c d d e e f f g g h h i i j j k k l l m m n n o o p p q q r r s s t t u u v v w w x x y y z z 
OK

第一次执行scan0,返回结果分为两个部分:第一个部分1就是下次scan需要的cursor,第二个部分是10个键:

127.0.0.1:6666> scan 0
1) "1"
2)  1) "u"
    2) "w"
    3) "g"
    4) "a"
    5) "b"
    6) "m"
    7) "z"
    8) "q"
    9) "i"
   10) "y"

使用新的cursor=“1”,scan 1 返回下一次的cursor=“29”是个键

127.0.0.1:6666> scan 1
1) "29"
2)  1) "n"
    2) "e"
    3) "t"
    4) "f"
    5) "c"
    6) "s"
    7) "h"
    8) "x"
    9) "o"
   10) "j"

使用得到的新的cursor=“29”,scan 29 返回结果cursor变成0,说明所有的键都已经遍历过了

127.0.0.1:6666> scan 29
1) "0"
2) 1) "p"
   2) "v"
   3) "r"
   4) "l"
   5) "k"
   6) "d"

除了scan以外,Redis提供了面向哈希类型、集合类型、有序集合的扫描遍历命令,解决诸如hgetall、smembers、zrange可能产生的阻塞问题,对应的命令分别是hscan、sscan、zscan,它们的用法和scan基本类似,下面以sscan为例子进行说明,当前集合有两种类型的元素,例如分别以old:user和new:user开头,先需要将old:user开头的元素全部删除,可以参考如下伪代码:

String key = "myset";
//定义 pattern
String pattern = "old:user*";
//游标每次从 0 开始
String cursor = "0";
while (true) {
    //获取扫描结果
    ScanResult scanResult = redis.sscan(key, cursor, pattern);
    List elements = scanResult.getResult();
    if (elements != null && elements.size() > 0) {
    //批量删除
        redis.srem(key, elements);
    }
    //获取新的游标
    cursor = scanResult.getStringCursor();
    //如果游标为 0 表示遍历结束
    if ("0".equals(cursor)) {
        break;
    }
}

发表评论

邮箱地址不会被公开。 必填项已用*标注