redis--字典

字典

字典,又称关联数组或映射,是一种用于保存键值对的抽象数据结构。在字典中,一个键key和一个值value进行关联;字典中的每个键都是独一无二的。

字典的实现

redis的字典使用哈希表作为底层实现,一个哈希表可以有很多节点,而每个节点就保存了字典中的一个键值对。

哈希表

定义结构如下:

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

结构示意图如下:


redis--字典_第1张图片
哈希表
哈希表节点

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

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

结构图如下:

redis--字典_第2张图片
哈希表
字典

redis中的字典结构如下:

typedef struct dict {
    //类型特定函数
    dictType *type;
   //私有数据
   void *privdata;
   //哈希表
   dictht ht[2];
   //rehash索引,不进行rehash时,值为-1
   int trehashidx;
}
  • [ ] type属性是一个指向dictType结构的指针,每个dictType保存了一组用于操作特定类型键值对的函数,redis会为用途不同的字典设置不同类型特定函数。
  • [ ] privdata属性,则保存了需要传递给那些类型特定函数的可选参数。
typedef struct dicType {
    //计算哈希值得函数
    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)
    //销毁键的函数
    int (*keyDestructor) (void *privdata,void *key);
    //销毁值得函数
    void (*valDestructor) (void *privdata,void *obj);
}

普通状态下的字典如图所示:

redis--字典_第3张图片
字典结构

ht是包含两个项的数组,每个项都是一个哈希表,字典只用ht[0]哈希表,ht[1]哈希表只会在对ht[0]哈希表进行rehash时使用。当两个及以上的键值对分配到了哈希数组中的同一个索引上,成为冲突,通过单链表的形式解决

哈希算法

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

redis使用MurmurHah2算法来计算哈希值。

新节点总是在已有节点的前边。

rehash

随着操作的不断执行,哈希表的键值对会增加或者减少,为了让负载因子维持在合理的范围内,哈希表需要扩展或者收缩。

步骤如下:

  1. 为字典的ht[1]哈希表分配空间,这个空间取决于要执行的操作,以及ht[0]当前包含的键值对数量。
  • 如果是执行扩展操作,那么ht[1]的大小为:
ht [0].used*2^{n} + 1
  • 如果执行的是收缩操作,那么ht[1]的大小为:
ht [0].used*2^{n}
  1. 将保存在ht[0]中ed所有键值对rehash到ht[1]中,rehash指的是重新计算哈希值和索引,然后将键值对放到哈希表指定的位置上。
  2. 当ht[0]迁移完之后,释放ht[0],将ht[1]的值设为ht[0],并在ht[1]上创建一个空的哈希表,为下一次rehash做准备。
渐进式rehash

rehash操作不是一次性,集中式的完成,而是分多次渐进式的完成的。在rehash期间,每对字典的添加,删除,查找或者更新操作时,都会进行rehash操作;在渐进式rehash期间,新添加的键值对一律会添加到ht[1]中,而不对ht[0]进行操作。

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

你可能感兴趣的:(redis--字典)