redis指令keys指令用于列出所有满足特定正则字符串规则的key
两个缺点:
- 没有offset,limit参数,一次性吐出所有满足条件的key
- 算法是遍历算法,复杂度是O(n),如果有千万级以上的key,这个指令会导致redis卡顿,因为redis是单线程的
为了解决上诉问题redis在2.8中引入了scan,具有以下特点
- 虽然复杂度也是O(n),但是它通过游标分步进行,不会阻塞线程
- 提供limit参数,返回结果可控
- 同样具备匹配功能
- 返回结果可能重复,需要客户端去重
- 单次返回的结果为空,不一定意味着遍历结束,需要看游标值是否为0
scan参数:scan 0 match name* count 10
- cursor:返回为0时将结果中第一个整数值作为下一次遍历的cursor,一直遍历到返回的cursor值为0时结束
- key的正则模式:
- 遍历的limit hint
127.0.0.1:6379> keys *
1) "name3"
2) "a1"
3) "name1"
4) "name2"
5) "age"
6) "codehole"
7) "dd"
8) "d"
9) "name"
10) "foo"
11) "a"
127.0.0.1:6379> scan 0 match name* count 10
1) "0"
2) 1) "name3"
2) "name1"
3) "name2"
4) "name"
127.0.0.1:6379> scan 0 match name* count 2
1) "4"
2) 1) "name3"
127.0.0.1:6379>
原理:
redis的所有key都存储在一个很大的字典中,优点类似于java中的HashMap(一维数组+二维链表),每次扩容空间加倍。
scan指令返回的游标就是一维数组的位置索引,我们将这个位置成为槽(slot),每次遍历会将limit数量的槽位上挂接的所有链表元素进行模式匹配后返回,可能为空!
遍历算法:高位进位加法
渐进式rehash
java的hashmap早扩容时会一次性将旧数组下挂载的全部元素转移到新数组下面,如果hashmao中的元素特别多,线程就会出现卡顿,redis为了解决则个问题提出了渐进式rehash
原理:它会同时保存新旧两个数组,然后再定时任务中以及后续对hash的指令操作中渐渐的将旧数组中挂接的元素迁移到新数组中。这意味着要操作处于rehash中的字典,需要同事访问新旧两个数组结构,如果在旧数组下面找不到元素,还要去新数组下面去寻找。
sacn也需要考虑这个问题,对于rehash中的字段,他需要同时扫描新旧槽位,融合后返回给客户端。