Redis 哈希的扩容过程是其高效性的关键所在,它采用了一种非常巧妙的渐进式 rehash 策略来避免一次性扩容带来的服务停顿。
步骤 1:准备工作
当满足扩容条件时,为 ht[1] 分配空间。新的大小根据上述规则计算。
将字典的 rehashidx 属性从 -1 设置为 0。这个设置标志着 rehash 过程的正式开始。
步骤 2:执行过程(逐步迁移)
在 rehashidx 被设置为 0 之后,每次对字典进行增、删、改、查操作时,Redis 除了执行指定的操作外,还会顺带进行以下“额外工作”:迁移一个桶:在 ht[0] 的 table[rehashidx] 位置(即第 rehashidx 个桶)上,将这个桶里的所有键值对(包括因哈希冲突形成的链表)重新计算哈希值,并放到 ht[1] 的对应新位置上。
更新索引:将 ht[0].table[rehashidx] 设置为空。
递增索引:将 rehashidx 属性的值加一(rehashidx++)。
步骤 3:结束过程
当 rehashidx 递增到等于 ht[0].size 时,意味着 ht[0] 的所有桶都已经迁移完毕。
此时,释放 ht[0] 的空间。
将 ht[1] 设置为新的 ht[0]。
为 ht[1] 创建一个新的空白哈希表,为下一次 rehash 做准备。
将 rehashidx 再次设置为 -1,标志着 rehash 过程彻底结束。
四、在此期间的操作如何执行?
由于数据分布在两个表中,所以在此期间的所有操作都需要在两个表中进行:
查找(GET):程序会先到 ht[0] 中查找,如果没找到,再到 ht[1] 中查找。
添加(SET):新的键值对会一律被保存到 ht[1] 中,这保证了 ht[0] 的键值对数量只减不增,最终会变成空表。
删除(DEL)和 更新:会同时作用于 ht[0] 和 ht[1]。