最近在看Redis源码的时候,看到Redis中的字典中有一个属性rehashidx
,注解中的说明是:rehash索引,当不在进行rehash操作的时候,其值为-1
我们来看一下Redis中字典的定义
//
// dict 字典
//
typedef struct dict {
// 类型特定函数
dictType *type; // type里面主要记录了一系列的函数,可以说是规定了一系列的接口
// 私有数据
void *privdata; // privdata保存了需要传递给那些类型特定函数的可选参数
// 哈希表
dictht ht[2]; // 有两张hash表
// rehash 索引
// 并没有rehash时,值为 -1
int rehashidx; /* rehashing not in progress if rehashidx == -1 */
int iterators; // 目前正在运行的安全迭代器的数量
} dict;
那我们来看一下什么叫做rehash操作
首先说明,非rehash
状态下,我们在进行添加数据的时候,都是将数据添加到ht[0]
这个哈希表中的,而ht[1]
这个表是一个空表。
当ht[0]
中的数据会随着时间逐渐增多或者逐渐减少,这时候就会导致其中的数据过多或者过少,这时候就需要对哈希表进行相应的扩展或者收缩操作。
上面所说的操作就可以通过rehash(重新散列)操作完成,其步骤如下:
<1>. 为ht[1]
这个空表分配空间,分配的空间的大小取决于将要进行的操作是扩展还是收缩操作和ht[0]
包含的键值对的数目(dictht
中的used
属性值,即 ht[0].used
的值 )
注:dictht
的定义如下
//
// dictht 哈希表
//每个字典都使用两个哈希表,从而实现渐进式 rehash
//
typedef struct dictht { // 这是字典的头部
// 哈希表数组, 每个元素都是一条链表
dictEntry **table;
// 哈希表大小
unsigned long size;
// 哈希表大小掩码,用于计算索引值
// 总是等于 size - 1
unsigned long sizemask;
// 该哈希表已有节点的数量
unsigned long used;
} dictht;
- 当要执行的是扩展操作的时候,
ht[1]
分配的空间大小为第一个大于等于ht[0].used*2
的2n 的值。 - 当要执行的是收缩操作的时候,
ht[1]
分配的空间大小为第一个大于等于ht[0].used
的2n 的值。
<2>. 将保存在ht[0]
中的键值对通过rehash
操作迁移到ht[1]
上。
注:通过rehash
操作重新计算键的哈希值和索引值,然后将键值对迁移到ht[1]
哈希表的指定位置上。
<3>. 当ht[0]
中包含的所有的键值对都迁移到ht[1]
上之后(ht[0]
现在就是空表),释放ht[0]
所占用的空间,将ht[1]
设置为ht[0]
,并重新新建一个ht[1]
的空白哈希表,为下一次的rehash
操作做准备。
如果将ht[0]
中的数据一次性全部导入到ht[1]
中,庞大的计算量可能会导致服务器在一段时间内停止工作,所以说为了避免rehash
操作对服务器性能照成影响,我们可以分多次,渐进式的将ht[0]
中的键值对慢慢的rehash
到ht[1]
中。
上面的这种操作我们称为——渐进式rehash
哈希表渐进式rehash
的步骤如下:
<1>. 为ht[1]
分配空间,使得当前字典同时拥有ht[1]
和ht[0]
两个哈希表。
<2>. 将当前字典中的rehashidx
属性的值设置为0
,表示rehash
操作正式开始。
<3>. 在rehash
进行的期间,每次对字典进行CRUD操作的时候,程序除了执行这些指定的CRUD操作之外,还会将ht[0]
中在rehashidx
索引上的所有键值对rehash
到ht[1]
上,当前的rehash
操作完成之后,将rehashidx
的值增加一。
<4>. 随着对字典的操作不断进行,在某一时间点ht[0]
上的所有的键值对都会被rehash
到ht[1]
上,这时候程序会将rehashidx
的值设置为-1
,表示整个rehash
操作已经完成。
在进行渐进式的rehash
操作的时候,字典会同时使用ht[1]
和ht[0]
这两张哈希表,这时候对字典的RUD操作会在两个哈希表上进行。
比如说我们要查找(R)一个键的时候,会首先在ht[0]
上进行查找,如果没有查找到的话,就会继续到ht[1]
中进行查询。其他的操作(UD)亦是如此。
如果这时候对字典进行C操作的时候,新添加的键值对会统一保存到ht[1]
中,保证ht[0]
只减不增,最后变成空表。
注:CRUD是指在增加(Create)、读取查询(Retrieve)、更新(Update)和删除(Delete)几个单词的首字母简写