3.redis学习笔记-字典

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/jun8148/article/details/82427833

3. 字典

3.1. 字典的概念

字典,又名符号表、关联数组或映射,是用来保存键值对的抽象数据结构。字典中的键唯一,且可以根据键对其与之关联的值进行改查删操作等,也可以根据键来删除整个键值对。

3.2. 字典的应用

  • 作为Redis数据库的底层,对数据库增删改查是在字典的基础上进行的。
  • 是哈希键底层的实现之一
  • ….还有很多

3.3. 字典的实现

3.3.1. 哈希表

字典是使用哈希表作为底层实现的。

哈希表的实现 dict.h/dictht结构:

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

属性介绍:

  • table 是一个数组,数组中的每个元素指向dict.h/dictEntry结构的指针,每个dictEntry 结构都保存着一个键值对。
  • size记录着哈希表的大小,也就是table数组的大小。
  • used表示哈希表已有的节点(键值对)的数量。
  • sizemask值永远等于size-1,这个值和哈希值决定一个键应该放到table数组的哪一个索引上。

3.3.2. 哈希表节点

dictEntry:

typedef struct dictEntry{
    // 键
    void *key;
    // 值
    union{
        void *val;
        uint64_t u64;
        int64_t s64;
    }v;
    // 指向下一个哈希表节点,形成链表
    struct dictEntry *next;
}dictEntry;

属性解释:

  • key保存键。
  • v保存值,这个值可以是uint64_t 整数,也可以是uint64_t整数,也可以是int64_t整数。
  • next属性指向另一个哈希表节点,可以将多个哈希值相同的键值对连接在一起,解决键冲突问题。

3.3.3. 字典

dict.h/dict结构如下:

typedef struct dict {
    // 类型特定函数
    dictType *type;
    // 私有数据
    void *privdate;
    // 哈希表
    dictht ht[2];
    // rehash索引
    // 当rehash不在进行时,值为-1
    int trehashidx;
}dict;
typedef struct dictType{
    // 计算哈希值的函数
    unsigned int (*hashFunction)(const void *key);
    // 复制键的函数
    void *(*keyDup)(void *privdata, const void *key);
    // 复制值的函数
    void *(*valDup)(void *privdata, const void *obj);
    // 对比键的函数
    int (*keyCompare)(void *privdata, const void *key1, const void *key2);
    // 销毁键的函数
    void (*keyDestructor)(void *privdata, void *key);
    // 销毁值的函数
    void (*valDestructor)(void *privdata, void *obj);
}dictType;

属性解释:

  • type是指向dictType结构的指针,每个dictType结构保存了一簇用于操作特定类型键值对的函数。
  • privdata属性保存需要传给那些特定函数的可选参数。
  • ht是一个包含两项的数组,每一个项都是dictht哈希表,一般情况下只使用ht[0]ht[1]只是在对ht[0]哈希表进行rehash时使用。
  • rehashidx记录rehash的进度,如果没有正在进行rehash,则值为-1。

3.4. 哈希算法

当我们要将新的键值对添加到字典里,这个时候需要先根绝键计算出哈希值和索引值,后根据索引值,将新键值对的哈希表节点放到哈希表数组的指定索引上。

3.4.1. Redis计算哈希值和索引值

  • 哈希值的计算

    根据字典设置的哈希函数,计算key的哈希值

    hash = dict->type->hashFunction(key);
  • 索引值的计算

    使用哈希表的sizemask属性和哈希值,计算出索引值

    // x根据情况的不同,取0或1
    index = hash & dict->ht[x].sizemask;

3.5. 解决键 冲突

当多个键都被分配到了哈希表数组的同一个索引上,就会产生键冲突。在Redis哈希表中采用链地址法来解决键冲突,即每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成单向链表,被分配到同一个索引上的多个节点可以通过这个单向链表连接起来,从而解决键冲突。

3.6. rehash

哈希表的键值对的数量在不断变化,为了让哈希表的负载因子维持在合理的范围,当哈希表保存的键值对数量太多或者太少的时候,程序会对哈希表的大小进行拓展或者收缩,即通过rehash(重新散列)操作来实现。

Redis的执行rehash步骤:

  1. 根据要执行的操作和ht[0]当前包含的键值对数量(ht[0].used值)为ht[1](之前就介绍了ht[1]是为了实现rehash)分配合适的空间大小,

    • 如果执行的是扩展操作,那么 ht[1]的大小为第一个大于等于ht[0].used*2的2^n^。
    • 如果执行的是收缩操作,那么ht[1]的大小为第一个大于等于ht[0].used的2^n^。

    举个例子:如果used属性值为3,那么扩展操作,ht[1]的大小为8(2^3^),收缩操作的话,ht[1]的大小为4(2^2^)。

  2. 将保存在ht[0]中的键值对rehash到ht[1]上,即重新计算键的哈希值和索引值,然后将键值对放置到ht[1]的指定位置上。

  3. 当上面操作结束后,ht[0]为空表,释放ht[0],将ht[1]设置为ht[0],并在ht[1]新创建一个空白哈希表,为下一次rehash做准备。

什么时候会进行哈希表的扩展和压缩?

  1. 当下列条件满足时,程序会自动对哈希表进行扩展操作。
  • 服务器目前没有在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因大于等于1。

  • 服务器目前正在执行BGSAVE命令或者BGREWRITEAOF命令,并且哈希表的负载因子大于等于5。

负载因子的计算公式

负载因子 = 哈希表已保存节点的数量 / 哈希表大小
load_factor = ht[0].used / ht[0].size

例如:对于一个大小为4,包含四个键值对的哈希表来说,负载因子的大小为 1 
  1. 当满足哈希表的负载因子小于0.1时,程序会对哈希表进行收缩操作。

3.7 渐进式rehash

上面所说的扩展和收缩哈希表是分多次、渐进式完成的,这样才能保证在保存的键值对数量多的时候,不会因为计算量大,导致服务器停止服务。

渐进式rehash步骤:

  1. 为ht[1]分配空间,让字典同时拥有ht[0] 、ht[1]两个哈希表。
  2. 在字典中维持一个索引计数器变量rehashidx,并将其值设为0,表示rehash操作正在进行。
  3. 在rehash进行期间,每次对字典执行增删改查操作时,程序除了执行指定操作之外,还将ht[0]哈希表在rehashidx索引上的所有键值对rehash到ht[1]上,当rehash工作完成,程序执行 rehashidx ++ 。
  4. 直到ht[0]所有键值对都被rehash到ht[1]上,将rehashidx值设为-1,表示rehash操作结束。

渐进式rehash的好处在于采取分而治之的方式,将rehash键值对所需的计算工作均摊到对字典的每个增删改查操作上,避免计算量大导致服务器停止。

注:在执行渐进式rehash操作的时候,如果要查找到一个键的话,程序会先在ht[0]上查,如果咩查到,会查ht[1]。

3.8. API

函数 作用 时间复杂度
dictCreate 创建一个新的字典 O(1)
dictAdd 将给定的键值对添加到字典上 O(1)
dictReplace 将给定的键值对添加到字典里,如果已经存在,那么用新值替换旧值 O(1)
dictFetchValue 返回给定键的值 O(1)
dictGetRandomKey 从字典中随机返回一个键值对 O(1)
dictDelete 从字典中删除给定键所对应的键值对 O(1)
dictRelease 释放给定字典,以及字典中包含的所有键值对 O(N),N为字典包含的键值对数量

猜你喜欢

转载自blog.csdn.net/jun8148/article/details/82427833
今日推荐