Redis源码分析(三)——Redis数据结构-字典

时间:2022-05-03
本文章向大家介绍Redis源码分析(三)——Redis数据结构-字典,主要内容包括1. 数据结构、1.2 哈希表节点、1.3 字典、2. 哈希算法、3. rehash、3.2 新哈希表大小的计算公式、3.3 rehash过程、3.4 渐进式rehash、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。

1. 数据结构

1.1 哈希表

typedef struct dictht{
  dictEntry **table;
  unsigned long size;
  unsigned long sizemask;
  unsigned long used;
} dictht;
  • table:存储节点的数组
  • size:table数组的长度
  • sizemask:size-1,用于在添加节点时计算节点在table中的位置
  • used:节点数量

1.2 哈希表节点

typedef struct dictEntry{
  void *key
  union {
    void *val;
    unit64_t u64;
    int64_t s64;
  }v;
  struct dictEntry *next
} dictEntry;
  • key:节点的key
  • union:节点的value(可以是指针、unit64_t整数、int64_t整数)
  • next:下一个节点的地址

1.3 字典

typedef struct dict {
  dictType *type
  void *privdata
  dictht ht[2]
} dict;
  • type:操作哈希表的各种函数
  • privdata:上述函数所需的入参
  • ht[2]:存储两个哈希表,一个正常使用,另一个在rehash时使用。

2. 哈希算法

  1. 计算哈希值 hash = dict->type->hashFunction(key);
  2. 计算在table数组的位置 index = hash & dict->ht[0].sizemask;
  3. 插入节点 创建新节点,并将其插入到table[index]的第一位。

3. rehash

3.1 何时进行rehash?

当加载因子(load factor)大于1或小于0.1时就要进行rehash。 加载因子计算公式:

load_factor = ht[0].used / ht[0].size

3.2 新哈希表大小的计算公式

当需要进行扩容/缩容的时候,究竟创建多大的哈希表呢?这取决于如下公式:

  • 若要进行扩容,则新的哈希表的大小=第一个大于等于h[0].used*2的2的n次方。
  • 若要进行缩容,则新的哈希表的大小=第一个大于等于h[0].used的2的n次方。

3.3 rehash过程

  1. 创建一个新的哈希表h[1],大小由上述公式计算得出;
  2. 将字典的rehashidx值从-1改为0;
  3. 依次遍历ht[0]上的所有节点,依次转移到ht[1]上去;
  4. 释放ht[0]的内存空间;

3.4 渐进式rehash

  • rehash过程中需要将所有节点迁移到新的哈希表中,如果节点个数很多的情况下,迁移的过程将非常漫长,那么程序将处于停止等待状态。所以事实上,Redis的rehash过程是分多次、分布完成的。
  • 在rehash过程中,每次对哈希表进行增删改查外,还要将ht[0][rehashidx]上的所有节点迁移到ht[1]中,并将rehashidx+1。从而几次操作后,ht[0]上的所有节点均被迁移至ht[1]中,rehash过程完成。
  • 在rehash过程中,对哈希表的添加操作均在ht[1]上完成,ht[0]只减不增。