redis 字典

字典:(符號表)c++

字典就是一個存儲kv的存儲結構,相似與c++的map,redis數據庫的底層就是使用字典實現的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;

 

 

下面是字典的定義:ui

type主要是針對不一樣的類型,private是針對函數的參數spa

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

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

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裏面,這樣可能保證服務器正常的運做

相關文章
相關標籤/搜索