zoukankan      html  css  js  c++  java
  • Redis(一)字典

      Redis 的字典相当于 Java 的 HashMap.

    一、Redis 字典的实现

      Redis 的字典底层是 哈希表实现。

    1.1、哈希表

      Redis 字典的哈希表结构定义

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

      table : table属性是一个数组,数组中的每个元素都指向一个dictEntry结构的指针,每个dictEntry结构保存着一个键值对。

      size : size属性记录了哈希表的大小,即table数组的大小,而used属性则记录了哈希表目前已有键值对的数量。

      sizemask : 用于计算索引值,sizemask属性的值总是等于size-1,这个属性和哈希值一起决定一个键应该被放到table数组的哪个索引上。

    下图为一个长度为 4 的字典

        

    1.2 哈希表节点

       哈希表节点使用dictEntry结构表示,每个dictEntry结构都保存一个键值对:

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

      Key : key属性保持着键值对中的键,

      V : v属性则保存着键值对中的值,其中键值对中的值可以是一个指针,或者是一个整数。 

      next : next属性是指向另一个哈希表节点的指针,这个指针可以将多个哈希值相同的键值对连接在一起,来解决键冲突问题(以链表的方式解决冲突问题)。  

    如下图表示一个完成的哈希表:

      

    1.3 字典

       Redis中的字典结构如下:

    typedef struct dict {
    
        // 类型特定函数
        dictType *type;
    
        // 私有数据
        void *privdata;
    
        // 哈希表
        dictht ht[2];
    
        // rehash 索引
        // 当 rehash 不在进行时,值为 -1
        int rehashidx; /* rehashing not in progress if rehashidx == -1 */
    
    } dict;

      type属性 和 privdata属性是针对不同类型的键值对,而创建多态字典而设置的:

      type : 是一个指向dictType结构的指针,每个dictType结构保存了一组用于操作特定类型键值对的函数,Redis会为用途不同的字典设置不同类型的特定函数。

      privadata : 保存了需要传给那些类型特定函数的可选参数。

      ht : 是一个包含了两个项的数组,数组中每个项都是一个dictht哈希表,一般情况下,字典只使用ht[0]哈希表,而ht[1]哈希表只对ht[0]哈希表进行rehash时使用。

      rehashidx : 积累了rehash目前的进度,如果没有进行rehash,则它的值为-1

    一个普通状态下的字典结构:

        

    二、Redis 哈希算法

      将一个新的键值对添加到字典里面的时候,程序需要先根据键值对上面的键来计算出哈希值和索引值,然后再根据索引值,将包含新键值对的哈希表节点放到哈希数组的指定索引上面。

      Redis计算哈希值和索引值的方法如下:

    # 使用字典设置的哈希函数,计算键 key 的哈希值
    hash = dict->type->hashFunction(key);
    
    # 使用哈希表的 sizemask 属性和哈希值,计算出索引值
    # 根据情况不同, ht[x] 可以是 ht[0] 或者 ht[1]
    index = hash & dict->ht[x].sizemask;

      下面举例说明一个完整的添加键值对<k0, v0>过程:

      1、首先程序会先使用语句hash = dict->type->hashFunction(k0);计算的处k0的哈希值。

        Redis使用 MurmurHash2 算法来计算键的哈希值。

      2、假设计算出的哈希值为8,则程序继续index = hash & dict->ht[0].sizemask = 8 & 3 = 0;计算得到k0的索引值为0,这表示包含这个键值对的节点应该放置到哈希表数组的索引0位置上。

         

     三、解决哈希冲突

      Redis哈希表使用链地址法来解决键冲突,每个哈希表节点都有一个next指针,多个哈希表节点可以用next构成一个单向链表,被分配到同一个索引上的节点可以用这个单向链表连接起来,从而解决键冲突问题。

      因为dictEntry节点组成的链表没有指向链表表尾的指针,为了考虑速度,程序总是将新节点添加到链表的表头位置(这样添加节点的时间复杂度为O(1))。

      下面的例子说明一个解决键冲突的实例:

      一个包含两个键值对的哈希表

          

       使用 链表解决 k1 和 k2 的冲突

        

     

    【1】: https://www.jianshu.com/p/bfecf4ccf28b

  • 相关阅读:
    使用C#实现DHT磁力搜索的BT种子后端管理程序+数据库设计(开源)
    便携版WinSCP在命令行下同步文件夹
    ffmpeg (ffprobe)分析文件关键帧时间点
    sqlite删除数据或者表后,回收数据库文件大小
    ubuntu 20.04下 freeswitch 配合 fail2ban 防恶意访问
    ffmpeg使用nvenc编码的结论记录
    PC版跑跑卡丁车 故事模式 亚瑟传说章节 卡美洛庆典 2阶段 心灵之眼 攻略
    There was an error loading or playing the video
    Nvidia RTX Voice 启动报错修复方法
    火狐浏览器 关闭跨域限制
  • 原文地址:https://www.cnblogs.com/Jomini/p/13920651.html
Copyright © 2011-2022 走看看