- redis哈希表结构 使用链地址方法来解决键冲突的,冲突后追加到链表后面。
- redis中有两个哈希表,其中一个时正常使用的 ht[0],另一个是在扩容或者收缩时才需要的ht[1],一般为空
- redis中的哈希表也是有 负载因子的
负载因子 = 哈希表已保存节点数量 / 哈希表大小(桶的数量)
load factor = ht[0].used / ht[0].size - redis扩容时机:
1.服务器未执行BGSAVE / BAREWRITEAOF 时,哈希表的负载因子 >= 1
2 .服务器正在执行BGSAVE / BAREWRITEAOF 时,哈希表的负载因子 >= 5
3 . 当负载因子小于 0.1时,执行收缩 - redis扩容,收缩大小
扩容:ht[1]大小为 >= ht[0].used * 2 的 2^n
收缩:ht[1]大小为 >= ht[0].used 的 2^n
扩容步骤
- 先将保存在ht[0]中的所有键值对 rehash到ht[1]中:rehash就是重新计算key的hash值和索引值,然后将键值对放置到ht[1]哈希表的指定位置上
- 当ht[0]包含的所有键值对都迁移到了ht[1]后(ht[0]变为空表),释放ht[0],将ht[1]设置为ht[0],并将ht[1]新建一个空白哈希表,为下一次rehash做准备。
渐进式rehash
-如果哈希表中存在这几百万,几千万,几亿的数据时,那么一次性将这些键值对全部rehash到ht[1]的话,会导致一段事件内redis服务器停止对外服务
步骤:
- 为ht[1]分配空间,让字典同时拥有两个哈希表
- 在字典中维持一个rehashidx变量设为0,表示rehash正式开始
- 在rehash期间,每次对字典进行添加,删除,查找或者更新操作时,程序除了执行指定的操作外,还会顺带将将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1],当rehash工作完成后,程序将rehashidx属性+1
- 随着字典操作不断执行,最终在某个时间点上,ht[0]的所有键值对都会rehash至ht[1],这时程序会将rehash属性的值设为-1,表示rehash操作已完成
好处:
渐进式rehash的好处在于他采取分而治之的方式,将rehash键值对所需要的计算工作均摊到对字典的每个添加、删除、查找和更新操作上,从而避免集中式rehash而带来的庞大的计算量。
rehash期间的操作:
- 渐进式rehash期间,字典的删除、查找、更新等操作会在两个哈希表上进行。例如:要在字典里面找一个key,程序会先在ht[0]里进行查找,如果没找到,就会继续到ht[1]中进行查找
- 渐进式rehash期间,新添加到字典的键值对一律会保存到ht[1]里,ht[0]则不会进行任何添加操作,这保证了ht[0]包含的键值对数量只减不增,并伴随这rehash操作的执行,最终变成空表