[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;
}
相關文章
相關標籤/搜索