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 - 1
    unsigned 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设计与实现》的读书笔记,如有错误或模糊点请在评论区指针,共同进步共同学习!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值