redis 字典


字典:(符號表)

字典就是一個存儲kv的存儲結構,類似與c++的map,redis數據庫的底層就是使用字典實現的

除了數據庫,字典也是哈希鍵的底層實現

 

字典使用哈希表實現,哈希表中存儲的都是kv結構

typedef struct dictht {

    // 哈希表數組
    dictEntry **table;

    // 哈希表大小
    unsigned long size;

    // 哈希表大小掩碼,用於計算索引值
    // 總是等於 size - 1
    unsigned long sizemask;

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

} dictht;

sizemask和哈希值一起決定了這兒節點應該放在哪里,我們每一個哈希表節點都有一個next屬性,這個可以解決鏈表沖突的問題,使得多個鍵值一樣的可以連在一起

下面我們看一下哈希表節點的定義:

typedef struct dictEntry {

    //
    void *key;

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

    // 指向下個哈希表節點,形成鏈表
    struct dictEntry *next;

} dictEntry;

 

 

下面是字典的定義:

type主要是針對不同的類型,private是針對函數的參數

其中計算哈希值的函數就在type里面指向的

有個哈希表數組,ht[1]只有rehash的時候使用,rehashindex也是rehash的時候使用

typedef struct dict {

    // 類型特定函數
    dictType *type;

    // 私有數據
    void *privdata;

    // 哈希表
    dictht ht[2];

    // rehash 索引
    // 當 rehash 不在進行時,值為 -1
    int rehashidx; /* rehashing not in progress if rehashidx == -1 */

} dict;
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;

當加入一個鍵值的時候,我們先根據type里面的函數計算出哈希值,&mask計算出索引值,加入哈希表的指定索引中,

為了解決哈希表的沖突,我們使用拉鏈發,但是為了考慮效率,我們通常將新加入的節點放在最前面,不yongO(N)摻入

 

rehash:

哈希表的鍵值會不聽的增多減少,為了讓負載因子,維持在一個合理的范圍,我們需要適當的進行擴展和收縮

  1. 為字典的 ht[1] 哈希表分配空間, 這個哈希表的空間大小取決於要執行的操作, 以及 ht[0] 當前包含的鍵值對數量 (也即是 ht[0].used屬性的值):
    • 如果執行的是擴展操作, 那么 ht[1] 的大小為第一個大於等於 ht[0].used 2 的 2^n (2 的 n 次方冪);
    • 如果執行的是收縮操作, 那么 ht[1] 的大小為第一個大於等於 ht[0].used 的 2^n 2
  2. 將保存在 ht[0] 中的所有鍵值對 rehash 到 ht[1] 上面: rehash 指的是重新計算鍵的哈希值和索引值, 然后將鍵值對放置到 ht[1] 哈希表的指定位置上。
  3. 當 ht[0] 包含的所有鍵值對都遷移到了 ht[1] 之后 (ht[0] 變為空表), 釋放 ht[0] , 將 ht[1] 設置為 ht[0] , 並在 ht[1] 新創建一個空白哈希表, 為下一次 rehash 做准備。

 

哈希表的擴展和收縮的條件:

1:如果沒有執行BSAVE或者BGREWRITEAOF,並且負載因子大於等於1

2:如果執行BSAVE或者BGREWRITEAOF,並且負載因子大於等於5

這樣設計是因為如果執行的話,會fork出新的進程,因為遵循寫實復制,為了盡量避免寫入內存進行復制,所以將負載因子提高一些

 

如果負載因子小0.1執行收縮

 

漸進事rehash:

因為哈希表的數據可能特別的多,所有rehash不是一次完成的,是多次分批完成的,這里就用到了reashindex,最開始rehashindex=0,表示對索引值0指向的復制,結束了,開始索引值1的,rehashindx+1,這個過程中如果查找的話,會先查找ht[0]->ht[1],添加的話都會添加大1里面,這樣可能保證服務器正常的運作

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM