1. 程式人生 > >《Redis設計與實現》閱讀筆記(四)--字典

《Redis設計與實現》閱讀筆記(四)--字典

hash 解決方法 針對 數據 發現 分配 aof 工作 方式

字典

字典,map,是用於保存鍵值對的抽象數據結構,是hash表實現。字典中的鍵唯一,通過鍵來操作值。Redis的數據庫使用字典來作為底層實現。

定義

Redis的字典使用哈希表作為底層實現,一個哈希表裏面由多個哈希表節點,哈希表節點保存著鍵值對。

哈希表

哈希表結構定義包含:哈希表數組,哈希表大小,哈希表掩碼,哈希表已有節點數。

1 typedef struct dictht {
2     dicEntry **table;
3     unsigned long size;
4     unsigned long sizemask;
5     unsigned long used;
6 }dictht;

table就是哈希表數組,每個元素就是一個哈希表節點的指針。

size記錄了哈希表的大小,也就是table的大小。

sizemask是哈希表的掩碼,用於計算索引值,值總是size-1。

used就是哈希表已有節點數,註意與size進行區分哈希表大小並不等於節點數。

哈希表節點

哈希表節點結構包含:鍵,值,下一哈希表節點的指針(用於解決沖突)

1 typedef struct dictEntry {
2     void *key
3     union {
4         void *val
5         uint64_t u64;
6         int64_t s64;
7 } v; 8 struct dictEntry *next; 9 } dictEntry;

next指針將哈希值相同的鍵值對連接在一起,形成鏈表,解決沖突。

字典

字典包含:一個大小為2的哈希表數組(方便rehash),rehash索引,特定類型的函數,復制鍵的函數。

1 typedef struct dict {
2     dictType *type;
3     void *privdata;
4     dictht ht[2];
5     int trehashidx;
6 } dict;

type 和privdata的作用具體並不清楚,書上介紹“針對不同類型的鍵值對,為創建多態字典而設置”。

ht即hashtable平時的哈希表節點在ht[0],rehash的時候用到ht[1]。

哈希方式

插入鍵值對時,先根據鍵計算出哈希值。再根據哈希值和哈希表的掩碼計算出應放在哈希表的哪個索引上。哈希函數為Murmurhash算法,然而並不知道具體是怎麽個實現,給自己挖個坑,有空學習下。

hash = dict->type->hashFunction(key);
index = hash & dict->ht[x].sizemask;(x為0或1,取決於實際情況)

提到哈希肯定要考慮沖突的解決方法。在上面的哈希表節點中就已經看到,在這裏使用鏈地址法解決沖突。

單向鏈表而且沒有記錄尾節點,所以插入鏈表時使用頭插入。如果插入到末尾的話還需要遍歷到鏈表末尾,消耗時間。

rehash

負載因子 = 哈希表已保存的節點數量/哈希表大小

很顯然負載因子大說明哈希表太小,為了避免沖突就要增大。而負載因子太小說明哈希表過大,浪費了空間。

我們要對哈希表進行擴展或者收縮。這個工作通過rehash來完成。

  1. 為ht[1]分配空間。
  2. 將ht[0]的鍵值對rehash到ht[1]
  3. 當ht[0]全部遷移到ht[1],釋放ht[0],讓ht[1]成為新ht[0]

從上面的過程中我們發現有三個問題:

  • 在什麽情況下我們需要對哈希表進行擴展或收縮?
  • 新空間分配策略是怎樣的?
  • rehash遷移的過程如何完成?

第一個問題:if 負載因子大於1且沒有執行BGSAVE,BGREEWRITEAOF 或者 負載因子大於5 (具體為啥這麽定我也不知道。。。第二個坑)

        執行擴展

      else if 負載因子小於0.1

        執行收縮      

第二個問題:擴展的大小為:大於等於(ht[0].used*2)的最小2的整數冪

      縮小的大小為:大於等於(ht[0].used)的最小2的整數冪

第三個問題:漸進式rehash

rehash的過程為了不堵住服務,將分成幾次完成。

  1. 為ht[1]分配空間,將rehashidx設為0(不進行rehash的時候值為-1)
  2. 每次對字典進行增刪查改,程序將同時將rehashidx索引上的所有鍵值對rehash到ht[1] ,完成後rehashidx加一。
  3. 當ht[0]上的所有鍵值對全部完成rehash,將rehashidx設為-1。rehash完成

在rehash過程中,字典的刪查改將在兩個hashtable上進行,而增加操作只會在ht[1]進行。

《Redis設計與實現》閱讀筆記(四)--字典