Redis 源码--字典 P1.字典结构。

2017-10-01  本文已影响0人  namelessEcho

字典是一种进行利用key快速查找value的key-value结构。从定义上来说key应该具有唯一性,即从编程语言的层面上来说,一个字典里不能存在两个语义相同的key。同样的C也没有自己的字典实现,于是Redis实现了自己的字典。
Redis的字典是通过HashTable来实现的,相比红黑树,hash的实现真的挺简单了。Hahs表必须要解决hashode的冲突问题(hashcode的冲突并不意味中hashcode一定是相等的。),常见的解决方法是拉链法和线性探测法。
简单来说拉链法是使用一个桶(bucket)来聚集所有hashcode()冲突的key,而线性探测则是将冲突的key简单的后移。
Redis的HashTable的实现方法是拉链法。通过两个table的渐进式的hash,保证了性能不会有太大的损失。
与链表类似,Redis使用了一个dict结构来操作字典(dict)。

typedef struct dict {

    // 类型特定函数
    dictType *type;

    // 私有数据
    void *privdata;

    // 哈希表
    dictht ht[2];

    // rehash 索引
    // 当 rehash 不在进行时,值为 -1
    int rehashidx; /* rehashing not in progress if rehashidx == -1 */

    // 目前正在运行的安全迭代器的数量
    int iterators; /* number of iterators currently running */

} dict;

逐个来看一下里面的结构:第一个是dicTtype结构体,其定义如下,主要存储了所使用的各个用于HashTable操作的函数指针。

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;

void *privdata是保存的传给dictType的可选参数。

dictht ht[2]声明的两个结构体数组。

typedef struct dictht {
    
    // 哈希表数组
    dictEntry **table;

    // 哈希表大小
    unsigned long size;
    
    // 哈希表大小掩码,用于计算索引值
    // 总是等于 size - 1
    unsigned long sizemask;

    // 该哈希表已有节点的数量
    unsigned long used;

} dictht;

其中最主要的结构应该是dictEntry 数组dictEntry **table,它定义了size个保存dictEntry 的桶(解决链冲突)的数组。dictEntry是每个hash表的结点,是保存每个key-value对的实体。

typedef struct dictEntry {
    
    // 键
    void *key;

    // 值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
    } v;

    // 指向下个哈希表节点,形成链表
    struct dictEntry *next;

} dictEntry;

可以看到redis在值内部允许一个指向任意值的指针,或者是一个64位的无符号数,亦或是64的有符号数。

总的来说:在文档里是这写的:
Hash tables will auto-resize if needed

dictEntry数组会以2的指数倍的形式进行自动扩容,键冲突通过拉链法完成。

上一篇 下一篇

猜你喜欢

热点阅读