字典可以理解为redis中的map。是一种保存键值对的数据结构。
键值对的常用操作包括新增,更新,和删除
如上为字典的数据结构示意图,table所指结构几乎和java中的HashMap一样,为数组加链表的方式通过分桶hash来解决键冲突。不一样的是可以看到这里的字典结构有两份,另外一份dictht1此刻table指向的是null。
redis中维护两份table的原因在于字典扩容考虑。不像java中HasnMap那样集中式的扩容,redis采用渐进式rehash。在发生rehash时,redis会逐渐将dictht0的数据rehash到dictht1中,并在完成时释放dictht0空间,将dictht1设置成dictht0,然后重新新建dictht1以备下一次rehash使用。
redis采用渐进式rehash是为其本身单线程的效率所考虑,如果当字典键太多rehash占用资源甚至会导致一段时间无法提供服务。
redis在负载因子达到一定临界时开始rehash。负载因子通过 used / size计算。
rehash开始时,根据used大小分配大于等于used*2的最小2的n次幂大小的空间给dictht1。并开始进行rehash操作。例如dictht0的used大小为4,此刻2的3次方为8即为大于等于user*2的最小2次幂,亦即此刻扩容后dictht1的大小。
在rehash过程中字典的删除,查找,更新等操作会在两个table上进行。
redis采用渐进式rehash是为了避免rehash对服务器性能造成影响。所以服务器不是一次性将dictht0的内容复制到dictht1,而是多次,渐进式的复制。为了在复制过程中维护复制过程,dict结构中维护了rehashidx这个字段,当rehash未开始或已完成时,rehashidx为-1。rehash开始时,rehashidx=0。rehashidx所指为dictht0的索引位置,表示当前rehash到的位置。redis会在后续对当前字典的添加,删除,查找,更新等操作中进行小步rehash,例如当发生查找时,rehash操作索引为rehashidx的dictht0键值对到dictht1。并将rehasidx+1。redis将rehash过程隐藏在后续的字典操作中小步渐进式进行。
是不是意味着,如果后续此字典没有操作,就会一直结束不了rehash过程呢。并不是,redis还会在后续过程中定时主动的做rehash操作。