全量遍历键
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;
}
}