一、什么是渐进式rehash
在Redis的具体实现中,使用了一种叫做渐进式哈希(Rehash)的机制来提高字典的缩放效率,避免 rehash 对服务器性能造成影响,假如Redis中有大量的key, 如果一次性对全部的数据进行Rehash, 可能会导致Redis在一段时间内停止服务。
在Redis中,哈希表扩容需要将哈希表0
里面的所有键值对 rehash 到哈希表1
里面, 但是, 这个 rehash 动作并不是一次性完成的, 而是分多次、渐进式地完成的。
二、渐进式rehash的详细步骤
为哈希表1
分配空间,且空间大小为哈希表0
的两倍, 让字典同时持有哈希表0
和哈希表1
两个哈希表。
在字典中维持一个索引计数器变量 rehashidx(即哈希表的下标) , 并将它的值设置为 0 , 表示 rehash 工作正式开始。
在 rehash 进行期间,每次对字典执行CRUD操作时, 程序除了执行指定的操作以外, 还会顺带将哈希表0
在 rehashidx 索引上的所有键值对 rehash 到哈希表1
, 当 rehash 工作完成之后, 程序将 rehashidx 属性的值+1。
随着字典操作的不断执行, 最终在某个时间点上,哈希表0
的所有键值对都会被 rehash 至哈希表1
, 这时程序将 rehashidx 属性的值设为 -1 , 表示 rehash 操作已完成。
三、渐进式rehash期间的CRUD操作
因为在进行渐进式 rehash 的过程中, 字典会同时使用哈希表0
和哈希表1
两个哈希表, 所以在渐进式 rehash 进行期间,字典的CRUD操作会在两个哈希表上进行, 比如要在字典里面查找一个键的话, 程序会先在哈希表0
里面进行查找, 如果没找到的话, 就会继续到哈希表1
里面进行查找, 诸如此类。
另外, 在渐进式 rehash 执行期间, 新添加到字典的键值对一律会被保存到哈希表1
里面, 而哈希表0
则不再进行任何添加操作:这一措施保证了哈希表0
包含的键值对数量会只减不增, 并随着 rehash 操作的执行而最终变成空表。
四、渐进式rehash带来的问题
渐进式rehash避免了redis阻塞,可以说非常完美,但是由于在rehash时,需要分配一个新的hash表,在rehash期间,同时有两个hash表在使用,会使得redis内存使用量瞬间突增,如果当前Redis结点的内存占用量达到maxmemory, 会触发内存淘汰机制,导致大量的Key被驱逐。