咱們來自頂向下來分析redis內部字典的數據結構html
typedef struct dict { dictType *type; //類型函數指針 這個結構體包含了一組處理特定類型的函數 void *privdata; //私有數據 傳給特定類型的函數 dictht ht[2]; //哈希表 long rehashidx; //rehash的進度 -1則爲沒有進行rehash unsigned long iterators; /* number of iterators currently running */ } dict;
哈希表,只使用 ht[0] ht[1] 用於 rehash的臨時空間redis
typedef struct dictht { dictEntry **table; //哈希表數組 這是個數組 數組元素爲 dictEntry指針 dictEntry保存了鍵值對 unsigned long size;//table數組的大小 unsigned long sizemask;//用於計算索引 size-1 unsigned long used; //已經分配的鍵值對數量 } dictht;
計算索引數組
h = dictHashKey(key) & n.sizemask;
存放鍵值對的結構體數據結構
typedef struct dictEntry { void *key; //鍵 //值 union { void *val; uint64_t u64; int64_t s64; double d; } v; struct dictEntry *next; //下一個節點 由於哈希表用拉鍊法解決hash碰撞 } dictEntry;
typedef struct dictType { uint64_t (*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;
能夠參考http://www.javashuo.com/article/p-rhsonhiq-dc.html函數
redis在發生碰撞後,將節點採用 頭插法 連接到鏈表後面,這樣就將插入節點的時間複雜度下降到 O(1)ui
爲何要rehash?指針
鍵的數量可能會不斷改變,增長鍵值對的話碰撞太多,形成查找效率的底下,若是鍵值對減小太多,那麼空間可能會太大,形成數組空間的浪費。因此應該適當的 rehash ,重新分配空間code
redis會根據 used的值進行rehash,一旦達到了閥值,那麼就開始rehash,藉助ht[1]來進行htm
在redis建立子進程進行RDB、AOF備份的時候,不會進行rehashblog
爲了不影響主進程處理請求,redis採用 漸進式rehash策略,即在插入或者刪除鍵的時候進行rehash,所以須要rehashidx來表示rehash的進度,
可是這裏帶來一個問題,漸進式rehash那麼若是須要插入或者刪除鍵這麼安排呢?
redis在插入的時候不會在舊的ht[0]上操做,而且在刪除鍵的時候須要在ht[0]、ht[1]中都尋找鍵,這樣就保證了ht[0]只減小不增長,直到ht[0]所有rehash到ht[1]
給字典添加鍵值對
static int dictAdd(dict *ht, void *key, void *val) { int index; dictEntry *entry; /* Get the index of the new element, or -1 if * the element already exists. */ if ((index = _dictKeyIndex(ht, key)) == -1) //獲取鍵的hashIndex return DICT_ERR; /* Allocates the memory and stores key */ entry = malloc(sizeof(*entry)); //分配鍵值對空間 entry->next = ht->table[index]; //頭插法 ht->table[index] = entry; /* Set the hash entry fields. */ dictSetHashKey(ht, entry, key); dictSetHashVal(ht, entry, val); ht->used++; return DICT_OK; }