[redis]dict和rehash

1、底层结构剖析

咱们来自顶向下来分析redis内部字典的数据结构html

img

dict

typedef struct dict {
    dictType *type; //类型函数指针 这个结构体包含了一组处理特定类型的函数
    void *privdata; //私有数据 传给特定类型的函数
    dictht ht[2]; //哈希表
    long rehashidx; //rehash的进度 -1则为没有进行rehash
    unsigned long iterators; /* number of iterators currently running */
} dict;

dictht

哈希表,只使用 ht[0] ht[1] 用于 rehash的临时空间redis

typedef struct dictht {
    dictEntry **table; //哈希表数组 这是个数组 数组元素为 dictEntry指针 dictEntry保存了键值对
    
    unsigned long size;//table数组的大小
    unsigned long sizemask;//用于计算索引 size-1
    unsigned long used; //已经分配的键值对数量
} dictht;

计算索引数组

h = dictHashKey(key) & n.sizemask;

dictEntry

存放键值对的结构体数据结构

typedef struct dictEntry {
    void *key; //键
    
    //值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next; //下一个节点 由于哈希表用拉链法解决hash碰撞
} dictEntry;

dictType

typedef struct dictType {
    uint64_t (*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;



2、拉链法解决hash碰撞

能够参考http://www.javashuo.com/article/p-rhsonhiq-dc.html函数

redis在发生碰撞后,将节点采用 头插法 连接到链表后面,这样就将插入节点的时间复杂度下降到 O(1)ui




3、关于rehash

为何要rehash?指针

键的数量可能会不断改变,增长键值对的话碰撞太多,形成查找效率的底下,若是键值对减小太多,那么空间可能会太大,形成数组空间的浪费。因此应该适当的 rehash ,重新分配空间code

什么时候进行

  1. redis会根据 used的值进行rehash,一旦达到了阀值,那么就开始rehash,借助ht[1]来进行htm

  2. 在redis建立子进程进行RDB、AOF备份的时候,不会进行rehashblog



渐进式rehash

为了不影响主进程处理请求,redis采用 渐进式rehash策略即在插入或者删除键的时候进行rehash,所以须要rehashidx来表示rehash的进度

可是这里带来一个问题,渐进式rehash那么若是须要插入或者删除键这么安排呢?

redis在插入的时候不会在旧的ht[0]上操做,而且在删除键的时候须要在ht[0]、ht[1]中都寻找键,这样就保证了ht[0]只减小不增长,直到ht[0]所有rehash到ht[1]



4、重要函数解析

dictAdd

给字典添加键值对

static int dictAdd(dict *ht, void *key, void *val) {
    int index;
    dictEntry *entry;

    /* Get the index of the new element, or -1 if
     * the element already exists. */
    if ((index = _dictKeyIndex(ht, key)) == -1) //获取键的hashIndex
        return DICT_ERR;

    /* Allocates the memory and stores key */
    entry = malloc(sizeof(*entry)); //分配键值对空间
    entry->next = ht->table[index]; //头插法
    ht->table[index] = entry;

    /* Set the hash entry fields. */
    dictSetHashKey(ht, entry, key);
    dictSetHashVal(ht, entry, val);
    ht->used++;
    return DICT_OK;
}
相关文章
相关标签/搜索