Redis中好玩的算法-高位进位法

Redis中好玩的算法-高位进位法

Redis大家都比较熟悉,在项目中使用的比较多,应用场景也很广泛,包括缓存,分布式锁,队列等等.Redis不但本身提供了很强大的功能,其中涉及的算法也很巧妙,下面就介绍一种高位进位法.

在平时线上 Redis 维护工作中,有时候需要从 Redis 实例成千上万的 key 中找出特定前缀的 key 列表来手动处理数据,可能是修改它的值,也可能是删除 key。那么就存在一个问题,我们怎么获取Redis中所有的key呢?Redis给我们提供了keys命令,但是这个命令有缺陷,我们都知道Redis是单线程的,那么执行keys命令之后Redis-Server会一直阻塞在keys命令,如果Redis中key值达到了上千万,那么可想而知,Redis还能快速的响应用户的请求么?所以Redis在2.8版本中提供了一个更高级的scan命令.

Scan命令

scan基本语法:scan cursor [MATCH parttern] [COUNT count],

cursor:cursor表示游标的意思,可以简单理解为分页的页码,每次执行命令后会返回给你一个cursor,下次再遍历时从返回位置开始遍历;

[MATCH parttern]表示是否进行key值的模式匹配;

[COUNT count]表示是否限制数量,可以简单理解为limit,但是返回的数量是不固定的,这个与Redis的存储结构有关.

Redis字典结构

在 Redis 中所有的 key 都存储在一个很大的字典中,这个字典的结构和 Java 中的 HashMap 一样,是一维数组 + 二维链表结构,第一维数组的大小总是 2^n(n>=0),扩容一次数组大小空间加倍,也就是 n++.

img

scan 指令返回的游标就是第一维数组的位置索引,我们将这个位置索引称为槽 (slot).如果不考虑字典的扩容缩容,直接按数组下标挨个遍历就行了.limit 参数就表示需要遍历的槽位数,所以返回的结果取决于索引下挂接链表的长度,有些槽位可能是空的,还有些槽位上挂接的链表上的元素可能会有多个.每一次遍历都会将 limit 数量的槽位上挂接的所有链表元素进行模式匹配过滤后,一次性返回给客户端

Scan遍历顺序

如果不考虑扩容与缩容,那么无论是从前遍历还是从后遍历都可以获取所有的key值,但是有扩容,缩容后就需要考虑遍历的准确性,是否存在重复遍历,是否存在遗漏的遍历.如果我们按照低位加法,即从前向后遍历,当扩容或者缩容时进行的rehash操作使得数据分散到不同的槽位,这就有可能发生重复遍历与遗漏遍历的情况.

img

观察这张图,我们发现采用高位进位加法的遍历顺序,rehash 后的槽位在遍历顺序上是相邻的。

假设当前要即将遍历 110 这个位置 (橙色),那么扩容后,当前槽位上所有的元素对应的新槽位是 0110 和 1110(深绿色),也就是在槽位的二进制数增加一个高位 0 或 1。这时我们可以直接从 0110 这个槽位开始往后继续遍历,0110 槽位之前的所有槽位都是已经遍历过的,这样就可以避免扩容后对已经遍历过的槽位进行重复遍历。

再考虑缩容,假设当前即将遍历 110 这个位置 (橙色),那么缩容后,当前槽位所有的元素对应的新槽位是 10(深绿色),也就是去掉槽位二进制最高位。这时我们可以直接从 10 这个槽位继续往后遍历,10 槽位之前的所有槽位都是已经遍历过的,这样就可以避免缩容的重复遍历。不过缩容还是不太一样,它会对图中 010 这个槽位上的元素进行重复遍历,因为缩融后 10 槽位的元素是 010 和 110 上挂接的元素的融合。

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值