標籤:des style blog color 使用 io 資料 cti
1、 Dict
2.1 資料結構定義dict.h
// 雜湊表結構typedef struct dictht { dictEntry **table; //雜湊表數組指標 unsigned long size; //雜湊表大小 unsigned long sizemask; //掩碼,hash時用到 unsigned long used; //已有節點的數量} dictht;// 雜湊表節點結構typedef struct dictEntry { void *key; union { void *val; uint64_t u64; int64_t s64; } v; // 值,可以是指標類型、uint64和int64 struct dictEntry *next; //指向下一節點形成一個單鏈表} dictEntry;//字典定義typedef struct dict { dictType *type; void *privdata; dictht ht[2]; int rehashidx; // 重分布標示-1標示正在重分布中 int iterators; // 重分布進度} dict;// 字典類型// 每個dictType儲存了一系列用於操作特定字典的函數,不同用途的字典type不同typedef struct dictType { // hash函數unsigned int (*hashFunction)(const void *key);// key的複製void *(*keyDup)(void *privdata, const void *key);// value的複製void *(*valDup)(void *privdata, const void *obj);// key的比較int (*keyCompare)(void *privdata, const void *key1, const void *key2);// key的銷毀void (*keyDestructor)(void *privdata, void *key);// value的銷毀 void (*valDestructor)(void *privdata, void *obj);} dictType;
2.2 hash
調用type中得hashFunction方法計算key的hash值,redis內部使用的hash演算法為Austin Appleby開發的MurmurHash2演算法
h = d->type->hashFunction(key)
計算key的索引值,先在ht[0]中尋找,如果沒有找到並且在rehash的過程中,則繼續在ht[1]中找
idx = h & d->ht[table].sizemask
對於雜湊衝突的解決,redis採取的拉鏈法,相同索引值的key會儲存在一個單鏈表中,所以確定了索引值以後還需要在對應的單鏈表中進行搜尋
while(he) { if (dictCompareKeys(d, key, he->key)) return -1; he = he->next; }
2.3 rehash
為了保證字典的使用效率,redis對字典結構採取了定期rehash的機制,因為rehash是重CPU的操作,為了避免過程中出現對外無響應的情況,這裡做了一種增量rehash的最佳化
rehash的流程如下:
1) 建立一個空得hash表,size為第一個大於等於2n的整數
unsigned long i = DICT_HT_INITIAL_SIZE;if (size >= LONG_MAX) return LONG_MAX;while(1) { if (i >= size) return i; i *= 2;}
2) 將ht[0]中的key重新計算hash和index,索引到ht[1]中,並將ht[0]中相應的索引值置為空白
3) 待ht[0]中的資料全部rehash到ht[1]中之後將ht[1]設定為ht[0],並建立一個新的ht[1]
增量rehash是對步驟2進行最佳化,每次只rehash一個index的key,並且在rehash過程中對讀寫操作做限制:
1) 讀:先查ht[0],如果沒有再在ht[1]上尋找
2) 寫:rehash過程中,新的key只向ht[1]中寫,並且會將索引所對應的所有key重新hash到ht[1]中。
最終會在某個時間點ht[0]中所有的key重新hash到ht[1]中。