Redis學習筆記——dict

Redis裏字典(dict)主要用在兩個場景:
一、保存數據庫的鍵值對
二、Hash類型的底層實現之一(另外一種是經過壓縮列表實現)數據庫

數據結構定義

字典的定義以下:數組

/*
 * 字典
 *
 * 每一個字典使用兩個哈希表,用於實現漸進式 rehash
 */
typedef struct dict {

    // 特定於類型的處理函數
    dictType *type;

    // 類型處理函數的私有數據
    void *privdata;

    // 哈希表(2 個)
    dictht ht[2];

    // 記錄 rehash 進度的標誌,值爲 -1 表示 rehash 未進行
    int rehashidx;

    // 當前正在運做的安全迭代器數量
    int iterators;

} dict;

(以上註釋來自《Redis設計與實現》)
哈希表dictht的定義以下:安全

/*
 * 哈希表
 */
typedef struct dictht {

    // 哈希表節點指針數組(俗稱桶,bucket)
    dictEntry **table;

    // 指針數組的大小
    unsigned long size;

    // 指針數組的長度掩碼,用於計算索引值
    unsigned long sizemask;

    // 哈希表現有的節點數量
    unsigned long used;

} dictht;

哈希表用鏈地址法來解決碰撞。這裏table是個二級指針,每一個指針指向了對應索引的節點連成的鏈表。
哈希表節點定義以下:數據結構

/*
 * 哈希表節點
 */
typedef struct dictEntry {

    // 鍵
    void *key;

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

    // 鏈日後繼節點
    struct dictEntry *next;

} dictEntry;

哈希表性能問題

哈希表的性能取決於大小(dictht的size屬性)與保存節點數量(dictht的used屬性)之間的比率:
一、哈希表的大小與節點數量,比率在 1:1 時,哈希表的性能最好;
二、若是節點數量比哈希表的大小要大不少的話,那麼哈希表就會退化成多個鏈表,哈希表自己的性能優點便不復存在。函數

rehash解決性能問題

之因此前面定義dict的時候,用了兩個哈希表dictht,是由於,在哈希表的節點比較多的時候,在第一個哈希表ht[0]上查找比較慢,這時,就須要對哈希表從新調整,也就是rehash。
關於rehash,有如下幾點須要注意:
一、建立一個更大的哈希表,而後賦給ht[1],將ht[0]上的節點移動到ht[1]上,移動結束後,將ht[1]設置爲ht[0]。
二、rehash分主動和被動,主動是指,Redis的常規服務定時去檢查而且rehash,被動是指,用戶在添加新鍵值對時觸發rehash。
三、當哈希表比較大的時候(表大,節點多),進行rehash可能會比較慢,這個時候,rehash不是一步阻塞完成的,是「漸進式」的,也就是分幾回完成,也就是,我此次移動1個索引的所有節點,下次常規服務裏移動另外一個索引的節點,以這種方式來完成rehash。
四、在rehash的過程當中,會同時使用到兩個哈希表,新添加進來的節點是放在ht[1]上。我的猜想是由於,ht[1]是rehash的最終結果,若是添加在ht[0]上,還須要多作一步,把ht[0]上的移動到ht[1]上。
五、在查找刪除的時候,會同時操做ht[0]和ht[1],由於在rehash的過程當中,哈希表分佈在這兩個表上。性能

收縮字典防止浪費資源

當比值 used*100/size 小於10的時候,也就是字典的填充率小於10%的時候,就收縮字典,收縮過程與rehash過程相似,不過ht[1]比ht[0]小。收縮是由常規服務檢查觸發的,用戶不會觸發收縮。ui

參考:
一、《Redis設計與實現》設計

相關文章
相關標籤/搜索