redis的字典(Hash)

时间: 2023-07-09 admin 互联网

redis的字典(Hash)

redis的字典(Hash)

字典(Hash)

Redis的字典是使用HashTable作为底层实现,一个哈希表存储多个键值对节点。

字典结构

typedef struct dict {//类型特定函数dictType *type;//私有数据void *private;//哈希表dictht ht[2];//rehash索引in trehashidx;//当没有进行rehash时,为-1
} dict;
  • type是一个在指向dictType结构的指针,每个dictType结构保存了一簇用于操作特定键值对的函数,该字段是为多态字典做准备。
  • private属性保存了需要传给dictType中函数的可选参数。
  • ht是一个包含两个项的哈希表的数组,当rehash时需要用到第二个作临时存储

Hash表结构

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

table 是一个数组,数组中每个元素都是一个指向哈希表节点(dictEntry)结构的指针。

Hash表节点结构

typedef struct dictEntry {//键void *key;//值union{void *val;//可以是指针uint64_tu64;//无符号整数int64_ts64;//带符号整数} v;//指向下个哈希表节点,形成链表(解决哈希冲突)struct dicEntry *next;
} dictEntry;

三者关系

字典------>哈希表------>哈希表节点

哈希算法

下面的代码解释了字典如何根据key计算索引值index

//1、使用字典设置的哈希函数,计算键key的哈希值
hash = dict->type->hashFunction(key)
//2、根据hash值与哈希表掩码相与得到索引值
index = hash & dict->ht[x].sizemask;

解决哈希冲突

当不同的键被分配到同一个哈希表数组索引上(哈希冲突),redis采用链地址法来解决,每一个哈希节点都有一个*next指针,用了指向下一个哈希节点。

  • 因为没有一个指向链表尾结点的指针,所以哈希表采用头插入的方式(时间复杂度为O(1)),将新插入的节点插在其他链表节点的首部(如下两图)。


rehash

负载因子==ht[0].used/ht[0].size

  • 什么时候会执行哈希表的扩展与收缩?

    当以下条件之一满足时,会对哈希表进行扩展操作

    • 服务器没有在执行bgsave(用于在后台异步保存当前数据库的数据到磁盘)与bgrewriteaof(用于异步执行一个 AOF(AppendOnly File) 文件重写操作)命令时,并且哈希表的负载因子大于等于1
    • 服务器正在执行bgsave与bgrewriteaof命令,并且哈希表的负载因子大于5(因为每一个哈希表数组节点可能存在大于一个节点,所以会大于1)

    当哈希表的负载因子小于0.1时,程序自动开始对哈希表执行收缩

  • 如何执行哈希表的扩展与收缩?

    • 为字典的ht[1]分配内存空间,大小取决于ht[0]的大小,计算如下

      • 如果执行的是扩展操作,ht[1]的大小为第一个大于等于ht[0].used*22^n(2的n次方幂)

      • 如果执行的是收缩操作,ht[1]的大小为第一个大于等于ht[0].used2^n(2的n次方幂)


  • 将保存在ht[0]上的所有键值对重新计算哈希值(rehash)放到ht[1]上

  • 迁移完成后,释放ht[0]并将ht[1]设置为ht[0],并新建一个ht[1]空白哈希表为下一次rehash做准备

  • rehash并不是一次完成的,而是渐进式完成,步骤如下

  • 为ht[1]分配空间,字典同时持有ht[0]和ht[1]两个哈希表

  • 在字典中位置索引计数器变量rehashix,将他的值设置为0

  • 在rehash期间,每次对字典执行添加、删除、查找或者更新操作时,程序出了执行指定的操作之外,还会将ht[0]在rehashidx索引上的所有键值对rehash到ht[1],完成后将rehashidx自增1

  • 当某个时间点上ht[0]的所有键值对都被rehash成功,程序将rehashidx设为-1

rehash过程中,字典会同时使用两个哈希表,删除,查找,更新操作会在两个表中查找,新增则会直接更新到ht[1]


该文章是学习《redis设计与实现》的读书笔记,如有错误或模糊点请在评论区指针,共同进步共同学习!