Redis中keys和scan的区别

资料来源于《Redis深度历险:核心原理和应用实践》一书

在平时线上 Redis 维护工作中,有时候需要从 Redis 实例成千上万的 key 中找出特定 前缀的 key 列表来手动处理数据,可能是修改它的值,也可能是删除 key。这里就有一个问 题,如何从海量的 key 中找出满足特定前缀的 key 列表来?

在Redis中我们可以使用keys和scan两种指令。

一、keys指令

演示:

127.0.0.1:6379> set codehole1 a
OK
127.0.0.1:6379> set codehole2 b
OK
127.0.0.1:6379> set codehole3 c
OK
127.0.0.1:6379> set code1hole a
OK
127.0.0.1:6379> set code2hole b
OK
127.0.0.1:6379> set code3hole b
OK
127.0.0.1:6379> keys *
1) "codehole1"
2) "code3hole"
3) "codehole3"
4) "code2hole"
5) "codehole2"
6) "code1hole"
127.0.0.1:6379> keys codehole*
1) "codehole1"
2) "codehole3"
3) "codehole2"
127.0.0.1:6379> keys code*hole
1) "code3hole"
2) "code2hole"
3) "code1hole"

keys指令使用非常简单,提供一个简单的正则字符串即可,但是有很明显的两个缺点。

(1)没有 offset、limit 参数,一次性吐出所有满足条件的 key,万一实例中有几百 w 个 key 满足条件,当你看到满屏的字符串刷的没有尽头时,你就知道难受了。

(2)keys 算法是遍历算法,复杂度是 O(n),如果实例中有千万级以上的 key,这个指令 就会导致 Redis 服务卡顿,所有读写 Redis 的其它的指令都会被延后甚至会超时报错,因为 Redis 是单线程程序,顺序执行所有指令,其它指令必须等到当前的 keys 指令执行完了才 可以继续。

二、scan指令

为了解决keys的问题,redis在2.8中引入了scan指令。

scan的特点:

1、复杂度虽然也是O(n),但是它是通过游标分步进行的,不会阻塞线程;

什么是游标?

2、提供 limit 参数,可以控制每次返回结果的最大条数,limit 只是一个 hint,返回的结果可多可少;

3、同 keys 一样,它也提供模式匹配功能;

4、服务器不需要为游标保存状态,游标的唯一状态就是 scan 返回给客户端的游标整数;

5、返回的结果可能会有重复,需要客户端去重复,这点非常重要;

6、遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的;

7、单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零;

// 0是cursor整数值 key99*是key的正则表达式 1000是limit hint
scan 0 match key99* count 1000

演示:

127.0.0.1:6379> scan 0 match key99* count 1000
1) "13976"
2) 1) "key9911"
 2) "key9974"
 3) "key9994"
 4) "key9910"
 5) "key9907"
 6) "key9989"
 7) "key9971"
 8) "key99"
 9) "key9966"
 10) "key992"
 11) "key9903"
 12) "key9905"
127.0.0.1:6379> scan 13976 match key99* count 1000
1) "1996"
2) 1) "key9982"
 2) "key9997"
 3) "key9963"
 4) "key996"
 5) "key9912"
 6) "key9999"
 7) "key9921"
 8) "key994"
 9) "key9956"
 10) "key9919"
127.0.0.1:6379> scan 1996 match key99* count 1000
1) "12594"
2) 1) "key9939"
 2) "key9941"
 3) "key9967"
 4) "key9938"
 5) "key9906"
 6) "key999"
 7) "key9909"
 8) "key9933"
 9) "key9992"
......
127.0.0.1:6379> scan 11687 match key99* count 1000
1) "0"
2) 1) "key9969"
 2) "key998"
 3) "key9986"
 4) "key9968"
 5) "key9965"
 6) "key9990"
 7) "key9915"
 8) "key9928"
 9) "key9908"
 10) "key9929"
 11) "key9944"

注意: 从上面的过程可以看到虽然提供的 limit 是 1000,但是返回的结果只有 10 个左右。因为这个 limit 不是限定返回结果的数量,而是限定服务器单次遍历的字典槽位数量(约等于)。 如果将limit 设置为 10,你会发现返回结果是空的,但是游标值不为零,意味着遍历还没结束。

更多的scan指令:

scan 指令是一系列指令,除了可以遍历所有的 key 之外,还可以对指定的容器集合进行遍历。 比如 zscan 遍历 zset 集合元素,hscan 遍hash字典的元素、sscan 遍历 set集合的元素。

它们的原理同 scan 都会类似的,因为 hash 底层就是字典,set 也是一个特殊的hash(所有的 value 指向同一个元素),zset 内部也使用了字典来存储所有的元素内容,所以这里不再赘述。

全部评论

相关推荐

评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务