字典:(符號表)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:
哈希表的鍵值會不聽的增多減小,爲了讓負載因子,維持在一個合理的範圍,咱們須要適當的進行擴展和收縮
ht[1]
哈希表分配空間, 這個哈希表的空間大小取決於要執行的操做, 以及 ht[0]
當前包含的鍵值對數量 (也便是 ht[0].used
屬性的值):ht[1]
的大小爲第一個大於等於 ht[0].used * 2
的 2^n (2
的 n
次方冪);ht[1]
的大小爲第一個大於等於 ht[0].used
的 2^n 2ht[0]
中的全部鍵值對 rehash 到 ht[1]
上面: rehash 指的是從新計算鍵的哈希值和索引值, 而後將鍵值對放置到 ht[1]
哈希表的指定位置上。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裏面,這樣可能保證服務器正常的運做