美文网首页redis源码
redis源码4--字典Dict:关键的结构定义

redis源码4--字典Dict:关键的结构定义

作者: QaoKi | 来源:发表于2020-05-21 17:39 被阅读0次

    源码文件在 dict.h 和 dict.c 中

    哈希表节点定义

    哈希表节点使用 dictEntry 结构表示,一个dictEntry 是一个键值对

    typedef struct dictEntry {
    
      // 键
      void *key;
    
      // 值,可以是指针,可以是uint64_t 或者int64_t 
      union {
        void *val;
        uint64_t u64;
        int64_t s64;
      } v;
    
      // 指向下个哈希表节点,形成链表
      //使用链表法解决哈希冲突问题
      struct dictEntry *next;
    
    } dictEntry;
    

    哈希表定义

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

    table 属性是一个数组, 数组中的每个元素都是一个指向 dictEntry 结构的指针, 每个 dictEntry 结构保存着一个键值对。
    size 属性记录了哈希表的大小, 也即是 table 数组的大小, 而 used 属性则记录了哈希表目前已有节点(键值对)的数量。
    当为table申请内存的时候,假设要设置哈希表大小为 len,那么申请的内存大小为 len * sizeof(dictEntry *),len乘以一个指针类型所占的大小,因为 table[i] 也是一个指针。然后让 size = len
    sizemask 属性的值总是等于 size - 1 , 这个属性和哈希值一起决定一个键应该被放到 table 数组的哪个索引上面。

    下图展示了大小为4的一个哈希表的结构,键 k1和k0索引相同,也就是出现了哈希冲突,使用链表解决哈希冲突。并且,当一个新的索引相同的结点加入,总是把新结点插入链表的头部


    image.png

    字典定义

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

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

    • type 属性是一个指向 dictType 结构的指针, 每个 dictType 结构保存了一簇用于操作特定类型键值对的函数, Redis 会为用途不同的字典设置不同的类型特定函数。
    • 而 privdata 属性则保存了需要传给那些类型特定函数的可选参数。
    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;
    

    ht 属性是一个包含两个项的数组, 数组中的每个项都是一个 dictht 哈希表, 一般情况下, 字典只使用 ht[0] 哈希表, ht[1] 哈希表只会在对 ht[0] 哈希表进行 rehash 时使用。
    除了 ht[1] 之外, 另一个和 rehash 有关的属性就是 rehashidx : 它记录了 rehash 目前的进度, 如果目前没有在进行 rehash , 那么它的值为 -1

    普通状态下(没有进行rehash)的字典


    image.png

    字典迭代器

    typedef struct dictIterator {
        
      // 被迭代的字典
      dict *d;
    
      // table :正在被迭代的哈希表号码,值可以是 0 或 1 。
      // index :迭代器当前所指向的哈希表索引位置。
      // safe :标识这个迭代器是否安全
      int table, index, safe;
    
      // entry :当前迭代到的节点的指针
      // nextEntry :当前迭代节点的下一个节点
      //             因为在安全迭代器运作时, entry 所指向的节点可能会被修改,
      //             所以需要一个额外的指针来保存下一节点的位置,
      //             从而防止指针丢失
      dictEntry *entry, *nextEntry;
    
      long long fingerprint; /* unsafe iterator fingerprint for misuse detection */
    } dictIterator;
    

    如果 safe 属性的值为 1 ,那么在迭代进行的过程中,程序仍然可以执行 dictAdd 、 dictFind 和其他函数,对字典进行修改。
    如果 safe 不为 1 ,那么程序只会调用 dictNext 对字典进行迭代,而不对字典进行修改。

    特定函数

    由 struct dictType 衍生出一系列宏定义函数

    dictSlots

    返回给定字典的大小

    #define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size)
    

    dictSize

    返回字典的已有节点数量

    #define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used)
    

    dictIsRehashing

    查看字典是否正在 rehash

    #define dictIsRehashing(ht) ((ht)->rehashidx != -1)
    

    dictFreeKey

    释放给定字典节点的键

    #define dictFreeKey(d, entry) \
      if ((d)->type->keyDestructor) \
          (d)->type->keyDestructor((d)->privdata, (entry)->key)
    

    dictSetKey

    设置给定字典节点的键

    #define dictSetKey(d, entry, _key_) do { \
      if ((d)->type->keyDup) \
          entry->key = (d)->type->keyDup((d)->privdata, _key_); \
      else \
          entry->key = (_key_); \
    

    } while(0)

    dictFreeVal

    释放给定字典节点的值

    #define dictFreeVal(d, entry) \
      if ((d)->type->valDestructor) \
          (d)->type->valDestructor((d)->privdata, (entry)->v.val)
    

    dictSetVal

    设置给定字典节点的值

    #define dictSetVal(d, entry, _val_) do { \
      if ((d)->type->valDup) \
        entry->v.val = (d)->type->valDup((d)->privdata, _val_); \
      else \
        entry->v.val = (_val_); \
    } while(0)
    

    dictSetSignedIntegerVal

    将一个有符号整数设为节点的值

    #define dictSetSignedIntegerVal(entry, _val_) \
      do { entry->v.s64 = _val_; } while(0)
    

    dictSetUnsignedIntegerVal

    将一个无符号整数设为节点的值

    #define dictSetUnsignedIntegerVal(entry, _val_) \
      do { entry->v.u64 = _val_; } while(0)
    

    dictCompareKeys

    比对两个键

    #define dictCompareKeys(d, key1, key2) \
      (((d)->type->keyCompare) ? \
        (d)->type->keyCompare((d)->privdata, key1, key2) : \
        (key1) == (key2))
    

    dictHashKey

    计算给定键的哈希值

    #define dictHashKey(d, key) (d)->type->hashFunction(key)
    

    dictGetKey

    返回获取给定节点的键

    #define dictGetKey(he) ((he)->key)
    

    dictGetVal

    返回获取给定节点的值

    #define dictGetVal(he) ((he)->v.val)
    

    dictGetSignedIntegerVal

    返回获取给定节点的有符号整数值

    #define dictGetSignedIntegerVal(he) ((he)->v.s64)
    

    dictGetUnsignedIntegerVal

    返回给定节点的无符号整数值

    #define dictGetUnsignedIntegerVal(he) ((he)->v.u64)

    相关文章

      网友评论

        本文标题:redis源码4--字典Dict:关键的结构定义

        本文链接:https://www.haomeiwen.com/subject/rxpgohtx.html