[redis] 數據結構 -- 字典

dict

  1. 結構

    // 哈希表結構
    typeof struct dictht {
        // 哈希表數組
        dictEntry **table;
        // 哈希表大小
        unsigned long size;
        // 哈希表大小掩碼,用於計算索引值, 總是等於 size - 1
        unsigned long sizemask;
        // 該哈希表已有節點的數量
        unsigned long used;
    }
    // 哈希節點結構
    typeof struct dictEntry {
        // 鍵
        void *key;
        // 值
        union {
            void *val;
            uint64_t u64;
            int64_t s64;
            double d;
        } v;
        // 指向下一個哈希表節點,形成鏈表
        // 該指針可以將多個哈希值相同的鍵值對連接在一起,以此來解決 hash 衝突的問題
        struct dictEntry *next;
    } dictEntry;
    // dict 結構
    typeof  struct dict {
         // 類型特定函數
        dictType *type;
         //  私有數據
        void *privdata;
        // 哈希表
        dictht ht[2];
        // rehash索引,當rehash不在進行時值爲-1
        long rehashidx;
    }
    
  2. 說明
    dict 中的 type 屬性和 privdata 屬性是針對不同類型的鍵值對,爲創建多態字典而設置的;
    type 屬性指向的是 dictType 結構的指針,它保存了一簇用於操作特定類型鍵值對的函數,而 privdata 則是保存用於傳給這些函數的可選參數。
    ht[1] 只有對 ht[0] 進行 rehash 的過程是纔會被使用。
    rehashidx 記錄了 rehash 的進度。

  3. 漸進式 rehash
    因爲對內存擴容操作會涉及到數據的遷移操作O(N),對 redis 很難承受這樣耗時的過程(大字典表)。
    因此採取了 rehash 操作,其過程爲:

    1. 主動 rehash(dictRehashMilliseconds 服務器定時任務) 或 被動 rehash(_dictRehashStep 負責)時,生成一個新的 ht[1],  並設置 rehashidx 爲 0。
    2.  rehash 期間當有新鍵值時,將添加到 ht[1] 中;搜索會先從 ht[0] 中查,如果找不到再從 ht[1] 中查。
    3. rehash 過程是漸進的,默認情況下,單次最少轉移空桶數量爲 10 次(版本 5.0)。
    4. 當 rehash 過程完全結束,那麼修改 ht[0] 指針引用,讓他指向新的字典表 ht[1],並設置 rehashidx 爲 -1,標記整個字典 rehash 結束。
    5. 需要注意的是,每次 CURD 操作時,如果當前爲 rehash 狀態,需要去完成一個桶的轉移,然後才能返回(參考 dictAddRaw)。
    
  4. 擴容條件
    正常情況下,當 hash 表中元素的個數等於第一維數組的長度時,就會開始擴容,擴容的新數組是原數組大小的 2 倍。
    不過如果 Redis 正在做 bgsave,爲了減少內存頁的過多分離 (Copy On Write),Redis 儘量不去擴容 (dict_can_resize),
    但是如果 hash 表已經非常滿了,元素的個數已經達到了第一維數組長度的 5 倍 (dict_force_resize_ratio),說明 hash 表已經過於擁擠了,這個時候就會強制擴容。
    沒有在執行 BGSAVE 或 BGREWRITEAOF 命令時,負載因子大於1進行擴容
    在執行 BGSAVE 或 BGREWRITEAOF 命令時,負載因子大於5進行擴容
    拓展操作給 ht[1] 分配第一個大於等於 ht[0].used*2 的 2 的 n 次方冪的空間

  5. 縮容條件
    當 hash 表因爲元素的逐漸刪除變得越來越稀疏時,Redis 會對 hash 表進行縮容來減少 hash 表的第一維數組空間佔用。縮容的條件是元素個數低於數組長度的 10%。縮容不會考慮 Redis 是否正在做 bgsave。
    負載因子小於 0.1 時進行縮容
    縮容操作給 ht[1] 分配第一個大於等於 ht[0].used 的 2 的 n 次方冪的空間

  6. set 的結構
    set 的結構底層實現也是字典,只不過所有的 value 都是 NULL,其它的特性和字典一模一樣。

發佈了53 篇原創文章 · 獲贊 9 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章