指令使用
scan cursor keyMatch limit
参数解释
cursor -- 遍历游标,第一次遍历时为0,每次scan都会返回一个游标值,直到遍历到返回游标值为0
keyMatch -- 扫描的关键字,可支持正则模式
limit --
limit hint
会按照大概这个limit的数量返回值,默认值为10
127.0.0.1:6379> scan 0 match key99* count 1000
1) "13976"
2) 1) "key9911"
2) "key9974"
3) "key9994"
...
127.0.0.1:6379> scan 13976 match key99* count 1000
1) "1996"
2) 1) "key9982"
2) "key9997"
3) "key9963"
...
127.0.0.1:6379> scan 1996 match key99* count 1000
1) "12594"
2) 1) "key9939"
2) "key9941"
3) "key9967"
...
注意,Redis在2.8版本才支持scan
指令
Scan 相比 Keys 具备有以下特点
复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程
提供 limit 参数,可以控制每次返回结果的最大条数,limit 只是一个 hint,返回的结果可多可少
同 keys 一样,它也提供模式匹配功能
服务器不需要为游标保存状态,游标的唯一状态就是 scan 返回给客户端的游标整数
返回的结果可能会有重复,需要客户端去重复,这点非常重要
遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的
单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零
Scan扫描原理
Redis的所有数据结构的key都是存在一个字典中的,类似于Java的HasMap,都是一维数组 + 链表,数组大小总是2的N次方,扩容一次数组大小翻倍,即2的N+1次方.
在不考虑扩容的情况下,Scan的遍历规则可以是按照数组下标顺序,依次访问对应的链表元素,并一次性返回链表中匹配的元素,这就是limit
限制只是大概的原因.而Scan返回的游标就是数组的下标.但现实情况是redis的key字典需扩容,所以redis区别于普通的从0到末尾的遍历方式,而是采用了高位进位加法
来遍历.
高位进位加法
指按照二进制的高位向低位扫描的
比如右边的顺序, 0000,1000,0100,1100,0010,1010,0110,1110,0001...
字典扩容
假设当前的字典的数组长度由 8 位扩容到 16 位,那么 3 号槽位 011 将会被 rehash 到 3 号槽位和 11 号槽位,也就是说该槽位链表中大约有一半的元素还是 3 号槽位(因为原来的元素时模8,现在是模16),其它的元素会放到 11 号槽位,11 这个数字的二进制是 1011,就是对 3 的二进制 011 增加了一个高位 1。
遍历图
redis的扩容是渐进式的,会同时存在两个数组,客户端查询时也是需遍历这两个新旧数组
参考文章
https://juejin.im/book/5afc2e5f6fb9a07a9b362527/section/5b3d97d9e51d4519634f8512