1、字典結構redis
Redis中字典採用hash表結構,以下:數組
typedef struct dictht { dictEntry **table; // hash表數組 unsigned long size; // hash表大小 unsigned long sizemask; // 掩碼 unsigned long used; // 已經使用的大小 } dictht;
table是一個數組,每一個元素指向一個dictEntry結構。size表示hash表大小,used表示使用的大小。一個size=4的空hash表以下:函數
dictEntry是一個key-value pair, 定義爲:ui
1 typedef struct dictEntry { 2 void *key; // key 3 union { 4 void *val; 5 uint64_t u64; 6 int64_t s64; 7 double d; 8 } v; // value 9 struct dictEntry *next; // 指向下一個key-value 10 } dictEntry;
next指針用於解決hash衝突,redis總採用直接鏈址法解決衝突。舉例:this
Redis中字典定義:spa
typedef struct dict { dictType *type; // type和privdata區別操做不一樣類型key-value void *privdata; dictht ht[2]; long rehashidx; /* rehashing not in progress if rehashidx == -1 */ int iterators; /* number of iterators currently running */ } dict;
ht[2]中通常只有ht[0]使用,ht[1]在rehash時使用,ht[1]和rehashindex使用後續介紹。指針
2、hash實現code
Redis中使用的hash函數爲MurmurHash2, 定義爲:blog
1 unsigned int dictGenHashFunction(const void *key, int len)
經過宏定義:element
1 #define dictHashKey(d, key) (d)->type->hashFunction(key)
獲取hashkey的值,以後使用:
1 idx = hashkey & d->ht[table].sizemask;
獲得hash桶的座標, 如圖:
Redis中使用直接鏈址法解決衝突,如圖:
3、Rehash
在函數_dictExpandIfNeeded中會判斷是否須要擴展hash表:
static int _dictExpandIfNeeded(dict *d) { /* Incremental rehashing already in progress. Return. */ if (dictIsRehashing(d)) return DICT_OK; /* If the hash table is empty expand it to the initial size. */ if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE); /* If we reached the 1:1 ratio, and we are allowed to resize the hash * table (global setting) or we should avoid it but the ratio between * elements/buckets is over the "safe" threshold, we resize doubling * the number of buckets. */ if (d->ht[0].used >= d->ht[0].size && (dict_can_resize || d->ht[0].used/d->ht[0].size > dict_force_resize_ratio)) { return dictExpand(d, d->ht[0].used*2); } return DICT_OK; }
能夠看出,當利用率used/size到某個比例時,開始執行hash表擴展,進行rehash。流程爲:
1 int dictRehash(dict *d, int n) { 2 int empty_visits = n*10; /* Max number of empty buckets to visit. */ 3 if (!dictIsRehashing(d)) return 0; 4 5 while(n-- && d->ht[0].used != 0) { 6 dictEntry *de, *nextde; 7 8 /* Note that rehashidx can't overflow as we are sure there are more 9 * elements because ht[0].used != 0 */ 10 assert(d->ht[0].size > (unsigned long)d->rehashidx); 11 while(d->ht[0].table[d->rehashidx] == NULL) { 12 d->rehashidx++; 13 if (--empty_visits == 0) return 1; 14 } 15 de = d->ht[0].table[d->rehashidx]; 16 /* Move all the keys in this bucket from the old to the new hash HT */ 17 while(de) { 18 unsigned int h; 19 20 nextde = de->next; 21 /* Get the index in the new hash table */ 22 h = dictHashKey(d, de->key) & d->ht[1].sizemask; 23 de->next = d->ht[1].table[h]; 24 d->ht[1].table[h] = de; 25 d->ht[0].used--; 26 d->ht[1].used++; 27 de = nextde; 28 } 29 d->ht[0].table[d->rehashidx] = NULL; 30 d->rehashidx++; 31 } 32 33 /* Check if we already rehashed the whole table... */ 34 if (d->ht[0].used == 0) {// 遷移完畢,更新ht[0] 35 zfree(d->ht[0].table); 36 d->ht[0] = d->ht[1]; 37 _dictReset(&d->ht[1]); 38 d->rehashidx = -1; 39 return 0; 40 } 41 42 /* More to rehash... */ 43 return 1; 44 }
把ht[0]上的數據逐步遷移到ht[1].
4、字典主要API