Redis中字典的rehsah操作

版权声明:转载请注明出处! https://blog.csdn.net/zjq_1314520/article/details/79012462

最近在看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*22n 的值。
  • 当要执行的是收缩操作的时候,ht[1]分配的空间大小为第一个大于等于ht[0].used2n 的值。

<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]中的键值对慢慢的rehashht[1]中。

上面的这种操作我们称为——渐进式rehash

哈希表渐进式rehash的步骤如下:

<1>. 为ht[1]分配空间,使得当前字典同时拥有ht[1]ht[0]两个哈希表。
<2>. 将当前字典中的rehashidx属性的值设置为0,表示rehash操作正式开始。
<3>. 在rehash进行的期间,每次对字典进行CRUD操作的时候,程序除了执行这些指定的CRUD操作之外,还会将ht[0]中在rehashidx索引上的所有键值对rehashht[1]上,当前rehash操作完成之后,将rehashidx的值增加一。
<4>. 随着对字典的操作不断进行,在某一时间点ht[0]上的所有的键值对都会被rehashht[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)几个单词的首字母简写

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页