数据库
首页 > 数据库> > [redis]哈希表怎么实现rehash

[redis]哈希表怎么实现rehash

作者:互联网

1. 哈希表的结构设计

redis的哈希表结构如下:

typedef struct dictht{
  // 哈希表数组
  dictEntry **table;
  // 哈希表大小
  unsigned long size;
  // 哈希表大小掩码,用于计算索引
  unsigned long sizemask;
  // 该哈希表已有的节点数量
  unsigned long used;              
} dittht;

 

可以看到,哈希表是一个数组,数组的每个元素是一个指向哈希表节点的指针。

 

2. 哈希冲突

哈希表实际上是一个数组,数组里每一个元素就是一个哈希桶

当一个键值对的键经过hash函数计算后得到哈希值,再将哈希值进行取模运算,得到的结果就是该key对应的数组元素位置,也就是第几个哈希桶

什么时候会产生哈希冲突呢?

举个例子,有一个可以存放8个哈希桶的哈希表。key1经过哈希函数计算后,再将哈希值%8进行取模运算,结果值为1,那么就对应哈希桶1,类似地,key9和jey10分别对应哈希桶1和6

 

当key1和key9分配到了相同的哈希桶中,就发生了哈希冲突

因此,当有两个以上数量的key被分配到了一个哈希桶中,此时称这些key发生了冲突

 

3. 链式哈希

redis采用链式哈希来解决哈希冲突

 

链式哈希局限性也很明显,随着链表⻓度的增加,在查询这⼀位置上的数据的耗时就会增加,毕竟链表的查询的时间复杂度是 O(n)  

4. rehash

在哈希表实际使用时,redis定义了一个dict结构体,这个结构体定义了两个哈希表

typedef struct dict {
  ...
  // 两个哈希表,交替使用,用于rehash
  dictht ht[2];
  ...
} dict;

 

在正常服务请求阶段,插入的数据都会写到ht1中,此时ht2h还没有被分配空间。

随着数据的逐步增多,触发了rehash,这个过程分为三步:

这个过程看起来跟简单,但是数据迁移存在很大的问题,如果ht1的数量非常大,那么在迁移至ht2的时候,会因为大量数据的拷贝造成redis阻塞,无法服务其他请求。

 

5. 渐进式rehash

为了避免 rehash 在数据迁移过程中,因拷⻉数据的耗时,影响 redis 性能的情况,所以 redis 采⽤了渐进式 rehash,也就是将数据的迁移的⼯作不再是⼀次性迁移完成,⽽是分多次迁移。

 

6. rehash触发条件

rehash 的触发条件跟负载因⼦(load factor)有关系   负载因⼦可以通过下⾯这个公式计算
负载因子 = 哈希表已保存的节点数量/哈希表大小

 

  触发 rehash 操作的条件,主要有两个

 

标签:rehash,哈希,redis,ht2,ht1,迁移
来源: https://www.cnblogs.com/aganippe/p/16022712.html