基本說明
- 字典,是一種用於保存鍵值對的抽象數據結構。
- 字典在redis中的應用相當廣泛,
- 比如redis的數據庫就是使用字典來作爲底層實現。
比如我執行命令:
set msg “hello world”
實際上就是保存在數據庫的字典裏面的。 - 哈希鍵的底層實現也是字典。
- 比如redis的數據庫就是使用字典來作爲底層實現。
字典的實現
Redis的字典使用哈希表作爲底層實現,一個哈希表裏面有很多哈希表節點,每個哈希表節點就保存了字典中的一個鍵值對。
首先是哈希表節點
typedef struct dictEntry{
//鍵
void *key;
//值
union{
void *val;
uint64_t u64;
int64_t s64;
}v;
//指向下一個哈希表節點,形成一個鏈表
struct dictEntry *next;
}dictEntry;
- 每個哈希表節點都保存一個鍵值對
- key表示鍵,而v屬性則保存着鍵值對中的值,其中鍵值對的值可以是一個指針,或者是一個uint64_t整數,又或者是一個int64_t整數(共佔一段最大的內存)。
哈希表結構
typedef struct dictht{
//哈希表數組
dictEntry **table;
//哈希表的大小
unsigned long size;
//哈希表大小掩碼,用於計算索引值,總是等於size-1
unsigned long sizemask;
//哈希表已有節點的數量
unsigned long used;
}dictht;
- table屬性是一個數組,每一個指向一個dict.h/dictEntry結構的指針,每個dictEntry結構保存一個鍵值對和一個指向下一個鍵值對的指針。
- size記錄了哈希表的大小,即table數組的大小。
- used記錄了哈希表目前已有節點的數量
字典結構
typedef struct dictType{
//計算哈希值的函數
unsigned int (*hashFuntion)(const void *key);
//複製鍵的函數
void *(*keyDup)(void *privdata,const void *key);
//複製值的函數
void *(*valDup)(void *privdata,const void *obj);
//對比鍵的函數
void (*keyCompare)(void *privdata,const void *key1,const void *key2);
//銷燬鍵的函數
void (*keyDestructor)(void *privdata,const void *key1,const void *key2);
//銷燬值的函數
void (*valDestructor)(void *privdata,void *obj);
}dictType;
typedef struct dict{
//類型特定的函數
dictType *type;
//私有數據
void *privdata;
//哈希表
dictht ht[2];
//refresh索引,當refresh不在進行時,值爲-1
int trehashidx;
}dict;
- type屬性是一個指向dictType結構的指針,每一個dictType結構保存了一簇用於操作特定鍵值對的函數,redis會爲用途不同的字典設置不同的類型特定的函數。
- privdata屬性則保存了需要傳給那些特定函數的可選參數。
- ht保存兩個項的數組,數組中的每個項都是一個dictht哈希表,一般情況下,字典只使用ht[0]哈希表,ht[1]哈希表只會在對ht[0]哈希表進行rehash時使用
- rehashidx記錄了rehash的進度,如果目前沒有在進行rehash,那麼它的值爲-1.
哈希算法
- 將一個新的鍵值對添加字典裏面,首先是用hash算法計算哈希值和索引值:
hash = dict->type->hashFunction(k0); - 假設計算出的hash值爲8,那麼程序會繼續使用語句:
index = hash&dict->ht[0].sizemask = 8 & 3 = 0
refresh操作
- 爲字典ht[1]哈希表分配空間,空間大小爲第一個大於等於ht[0].used*2的2^n
- 然後將ht[0]上的值都映射到th[1]中,之後釋放ht[0],再把ht[0]指向ht[1],ht[1]指向空白哈希表
漸進式refresh
當refresh時,不併不是一下子轉移的,比如當鍵值對太多的時候,這個時候就需要使用標誌rehashhidx,步驟如下:
- 爲ht[0]分配空間,讓字典同時次優ht[0]和ht[1]兩個哈希表
- 在字典維持一個索引計數器變量rehashidx,並將它的值設置爲0,表示rehash工作正式開始
- 在rehash進行期間,每次對字典執行添加,刪除,查找或者更新操作時,程序除了執行指定的操作以外,還會順帶將rehashidx索引值加一,代表已經轉移到第幾個鍵值對數組了
- 全部轉移完成後,rehashidx值變爲-1
在rehash操作的期間,如果有增刪改查,就會先在ht[0]查找,如果沒有再到ht[1]中尋找。