redis 的全局就是使用的是key-value形式存储,使用的也就是他底层的数据结构dict字典。字典内部存储和java中的hashmap差不多,其底层都是通过数组和链表实现的。
在dict中我们所存储的key就是底下的数组下标,数组下表是通过计算hash值出来的。正是因为有了hash冲突也就有了链表。
在使用scan的时候我们其中scan的游标就是数组的下标,因为在存储的时候进行的是计算hash后进行存储,所以在数组上不是顺序存储,所以在一段数组上有可能有值也有可能没有值。也有可能一个slot上有多个值。所以这就是scan为什么会在增量式的过程中出现多个和0个的原因,如下图
如果说按数组的下标顺序便利下去那要是扩容了怎么办,因为扩容之后需要进行进行重新hash,数组下标的位置就会改变,那么这个过程中我们我们在扩容之前scan返回的游标就不准确了吗?
redis处理扩容下表的方案是:它不是从第一维数组的第 0 位一直遍历到末尾,而是采用
了高位进位加法来遍历。之所以使用这样特殊的方式进行遍历,是考虑到字典的扩容和缩容
时避免槽位的遍历重复和遗漏。
选择高位进位加法的主要原因还是他进行扩容的特点,和hashMap的差不多,采用的是:
*Java 中的 HashMap 有扩容的概念,当 loadFactor 达到阈值时,需要重新分配一个新的
2 倍大小的数组,然后将所有的元素全部 rehash 挂到新的数组下面。rehash 就是将元素的
hash 值对数组长度进行取模运算,因为长度变了,所以每个元素挂接的槽位可能也发生了变
化。又因为数组的长度是 2n(我们在进行扩容的时候其容量都为2n的原因) 次方,所以取模运算等价于位与操作。
抽象一点说,假设开始槽位的二进制数是 xxx,那么该槽位中的元素将被 rehash 到
0xxx 和 1xxx(xxx+8) 中。 如果字典长度由 16 位扩容到 32 位,那么对于二进制槽位
xxxx 中的元素将被 rehash 到 0xxxx 和 1xxxx(xxxx+16) 中。*
如下图就是缩容和扩容的一个对比
所以说redis采用了高位进位加法来遍历的。
Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2018 群英 版权所有 茂名市群英网络有限公司
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号-36 粤公网安备 44090202000006号 粤工商备P091701000595