Redis的字典由 dict.h/dict 结构如下(rehash的重点)
typedef struct dict {
//类型特性函数
dictType *type;
//私有数据
void *privdata;
//哈希表
dictht ht[2];
//rehash索引
//当rehash没有进行时为-1
int trehashidx;
}
ht 属性是一个包含两个项的数组,数组中的每个项都是一个dictht哈希表,一般情况下使用的都是ht[0]的哈希表,而ht[1]的哈希表只会在rehash的时候使用。
随着操作的进行,哈希表中的键值对会逐渐增多或减少,这时为了让哈希表负载因子位置在一个合理的范围之内就会对哈希表大小进行扩展或收缩即rehash。
rehash流程如下:
- 先为ht[1]的哈希表分配空间(分配的空间取决于要执行的操作以及当前ht[0]哈希表的键值对数量
- 如果执行的是扩展操作,那么ht[1]哈希表的大小为 第一个大于等于ht[0].used * 2的 2的n次方幂,即第一个大于等于ht[0]哈希表键值对数量两倍的2的n次方幂
- 如果执行的是 收缩操作,那么ht[1]哈希表的大小为第一个大于等于 ht[0]哈希表键值对数量的2的n次方幂
- 然后保存在ht[0]哈希表的所有键值对rehash到ht[1]哈希表上,rehash就是重新计算键值对的索引值和哈希值,将键值对放在新哈希表对应的位置上
- 当ht[0]哈希表上的所有键值对全部迁移到了ht[1]哈希表上后,释放ht[0]空间,将ht[1]设置为ht[0],并在ht[1]创建一个新的空白哈希表为下一次rehash做准备。(有点像JVM幸存者区的from区和to区,也是在每次yong GC之后清空from区,from区和to区交换)
但是Redis的rehash过程不是一次性rehash,而是渐进式rehash
渐进式rehash
就上诉流程而言
- 在为ht[1]哈希表分配空间之后,现在同时有了 ht[0] 和 ht[1]哈希表
- 最上方redis的字典结构已经给各位展示,其中有个 int 类型变量 trehashidx 表示着渐进式rehash的一个索引计数器
- 在rehash进行期间,每次对字典进行增删改查的操作,除了完成对应的操作之外,还会将ht[0]哈希表上与 trehashidx 对应索引上的键值对 rehash 到ht[1]哈希表上,当此次rehash完成之后将 trehashidx 加一
- 最后当ht[0]哈希表上的所有键值对都rehash到ht[1]哈希表上了之后,会将trehashidx 设置为 -1,表示rehash已经完成。
注意:
- 渐进式rehash的过程中,对于字典的删除、查找、修改都现在 ht[0]哈希表上进行,没有的话就去ht[1]哈希表上
但是对于增加操作的话,则是直接在ht[1]哈希表上进行,这样的话能确保ht[0]哈希表的键值对数量只会减少,最终随着rehash的完成是ht[0]哈希表变为空表。
觉得不错的小伙伴可以一键三连哦!,感谢支持!!!
更多面试题请移步 大厂面试题专栏