Redis中的keys命令可以列出满足特定正则表达式所有的key.命令的格式为keys pattern
但是它存在俩显著的缺点
- 没有偏移量与限制个数
- 时间复杂度是O(n)
为解决此问题,2.8版本便加入年Scan指令。
它有如下特点:
- 时间复杂度仍为O(n),且通过游标分布进行。
- 提供limit参数
- 提供模式匹配功能
- 服务器不需要为游标保存状态。
- 返回结果会有重复
- 遍历过程中若有数据修改,改动后的数据不一定会便利到。
- 单次返回的结果是空的并不意味着遍历结束,而看返回的游标值。
基本用法
scan cursor [MATCH pattern] [COUNT count]
cursor为设定的游标值。
先为Redis增添一些键值对
public class InsertData {
public static void main(String[] args) {
try(Jedis jedis = new Jedis()) {
for (int i = 0; i < 1000; i++) {
jedis.set("key"+i, String.valueOf(i));
}
}
}
}
用法示例如下所示
游标为何物?
在Redis中所有的key都类似于HashMap一样被存于一个数组(桶表)当中,而那个游标位置就是数组的索引。
某个游标值的得到元素的或多或少与该桶中挂载的元素有关。limit表示遍历的桶的数量,将这些桶进行模式匹配后一次性返回。
scan遍历顺序
它采用高位进位加法来遍历。使用此方法是考虑到字典的扩容与缩容时避免桶重复和遗漏。而且高位扩容过来的顺序是相邻的。
0000 -+1-> 1000 -+1-> 0100 -+1->1100.....
字典扩容
与HashMap类似,有一个将桶中元素高1位hash值是否为1,如果是移至高位,否保持不变。
比如一个元素的hash码为1001。在桶为8时,在1号位置,在扩容后(桶为16),在9号位置。
扩容、缩容后的遍历顺序
由于高位进位加法,rehash后的桶的位置是相邻的。
所以扩容后仅需找到需要遍历的原位置之后的即可。
缩容与扩容类似,不过由于将两个桶存到一起了,可能会重复遍历之前已经遍历过的内容,这大概就是要手动去重的原因吧。
渐近式rehash
就是会保留新旧两种桶表,旧找不到就去新的找。就跟CHM中加个转发结点似的感觉(底层实现是怎样的我不知道)。
scan还支持一些其他的指令,zscan、hscan等,原理类似
大Key扫描
如果某个Key过大,会造成集群环境下数据迁移时的卡顿。且扩容时会一次性申请更大的一块内存,也会造成卡顿。且被清除时,一次性回收,也会造成卡顿。
所以业务中尽量避免大key。
这种情况下也可以使用scan命令定位大key
在每隔100条指令休眠time秒。
redis-cli -h ip -p port --bigkeys -i time秒
!!!啊下一章原理篇了