redis中字典相關的文件爲:dict.h與dict.cgit
與其說是一個字典,道不如說是一個哈希表。github
1、數據結構redis
dictEntry安全
1 typedef struct dictEntry { 2 void *key; 3 union { 4 void *val; 5 uint64_t u64; 6 int64_t s64; 7 double d; 8 } v; 9 struct dictEntry *next; 10 } dictEntry;
dictEntry是一個kv對的單向鏈表,其中v是一個聯合體,支持數字,或者是指向一塊內存的指針。數據結構
1 /* 2 +---------------+ 3 |void *key | 4 +---------------+ 5 |union{...} v | 6 +---------------+ 7 |dictEntry *next|---+ 8 +---------------+ | 9 | 10 +---------------+ <-+ 11 |void *key | 12 +---------------+ 13 |union{...} v | 14 +---------------+ 15 |dictEntry *next| 16 +---------------+ 17 */
爲了節約篇幅,後續用如下結構表示:dom
1 /* 2 +---+ +---+ 3 |K|V|->|K|V|->NULL 4 +---+ +---+ 5 */
dicthtide
1 typedef struct dictht { 2 dictEntry **table; 3 unsigned long size; 4 /* 5 這樣寫可能更容易理解 6 const unsigned long size = 4; 7 dictEntry *table[size]; 8 */ 9 10 11 unsigned long sizemask; 12 //sizemask,始終爲size-1 13 14 unsigned long used; 15 //當前總dictEntry數量 16 } dictht;
dictht是一個hash table,總體結構大體爲:函數
1 /* 2 +----------------------+ +---> +-----------------+ +---+ 3 |dictEntry **table |---+ |dictEntry *bucket|->|K|V|->NULL 4 +----------------------+ +-----------------+ +---+ 5 |unsigned long size = 4| |dictEntry *bucket|->NULL 6 +----------------------+ +-----------------+ 7 |unsigned long sizemask| |dictEntry *bucket|->NULL 8 +----------------------+ +-----------------+ 9 |unsigned long used | |dictEntry *bucket|->NULL 10 +----------------------+ +-----------------+ 11 */
其中,table指向大小爲sizeof(dictEntry*) * size的一片內存空間,每一個dictEntry*能夠視爲一個bucket,每一個bucket下掛着一個dictEntry單向鏈表。ui
size的值始終爲2的位數,而sizemask的值始終爲size-1,其做用是決定kv對要掛在哪一個bucket上。this
舉個例子,size=4時,sizemask=3,其二進制爲 0011,若經過hash函數計算出來key對應的hash值hash_value爲5,二進制爲0101,則經過位運算 sizemask & hash_value = 0011 & 0101 = 0001,十進制爲1,則將會掛在idx = 1的bucket上。
dictType
1 typedef struct dictType { 2 uint64_t (*hashFunction)(const void *key); 3 void *(*keyDup)(void *privdata, const void *key); 4 void *(*valDup)(void *privdata, const void *obj); 5 int (*keyCompare)(void *privdata, const void *key1, const void *key2); 6 void (*keyDestructor)(void *privdata, void *key); 7 void (*valDestructor)(void *privdata, void *obj); 8 } dictType;
dictType用於自定義一些操做的方法,如拷貝key、拷貝value、銷燬key、銷燬value,比較key,以及hash函數。
dict
1 typedef struct dict { 2 dictType *type; 3 void *privdata; 4 dictht ht[2]; 5 long rehashidx; /* rehashing not in progress if rehashidx == -1 */ 6 unsigned long iterators; /* number of iterators currently running */ 7 } dict;
以前提到的dictType與dictht都是dict的成員變量。除此以外,還有privdata,是在建立dict的時候調用者傳入,用於特定操做時回傳給函數的。如:
1 #define dictFreeVal(d, entry) \ 2 if ((d)->type->valDestructor) \ 3 (d)->type->valDestructor((d)->privdata, (entry)->v.val) 4 5 #define dictSetVal(d, entry, _val_) do { \ 6 if ((d)->type->valDup) \ 7 (entry)->v.val = (d)->type->valDup((d)->privdata, _val_); \ 8 else \ 9 (entry)->v.val = (_val_); \ 10 } while(0) 11 12 #define dictSetSignedIntegerVal(entry, _val_) \ 13 do { (entry)->v.s64 = _val_; } while(0) 14 15 #define dictSetUnsignedIntegerVal(entry, _val_) \ 16 do { (entry)->v.u64 = _val_; } while(0) 17 18 #define dictSetDoubleVal(entry, _val_) \ 19 do { (entry)->v.d = _val_; } while(0) 20 21 #define dictFreeKey(d, entry) \ 22 if ((d)->type->keyDestructor) \ 23 (d)->type->keyDestructor((d)->privdata, (entry)->key) 24 25 #define dictSetKey(d, entry, _key_) do { \ 26 if ((d)->type->keyDup) \ 27 (entry)->key = (d)->type->keyDup((d)->privdata, _key_); \ 28 else \ 29 (entry)->key = (_key_); \ 30 } while(0) 31 32 #define dictCompareKeys(d, key1, key2) \ 33 (((d)->type->keyCompare) ? \ 34 (d)->type->keyCompare((d)->privdata, key1, key2) : \ 35 (key1) == (key2))
rehashidx,是與ht[2]配合實現漸進式rehash操做的。若使用一步到位的方式,當key的數量很是大的時候,rehashing期間,是會卡死全部操做的。
1 /* 2 +---------+ /+-----------+ +-->+----------+ +---+ 3 |dictType*| / |dictEntry**|---+ |dictEntry*|->|K|V|->NULL 4 +---------+ / +-----------+ +----------+ +---+ 5 |privdata | / |size | |dictEntry*|->NULL 6 +---------+/ +-----------+ +----------+ 7 |ht[2] | |sizemask | |dictEntry*|->NULL 8 +---------+\ +-----------+ +----------+ 9 |rehashidx| \ |used | |dictEntry*|->NULL 10 +---------+ \ +-----------+ +----------+ 11 |iterators| \ 12 +---------+ \+-----------+ 13 |dictEntry**|-->NULL 14 +-----------+ 15 |size | 16 +-----------+ 17 |sizemask | 18 +-----------+ 19 |used | 20 +-----------+ 21 */
2、建立
1 static void _dictReset(dictht *ht) 2 { 3 ht->table = NULL; 4 ht->size = 0; 5 ht->sizemask = 0; 6 ht->used = 0; 7 } 8 9 int _dictInit(dict *d, dictType *type, 10 void *privDataPtr) 11 { 12 _dictReset(&d->ht[0]); 13 _dictReset(&d->ht[1]); 14 d->type = type; 15 d->privdata = privDataPtr; 16 d->rehashidx = -1; 17 d->iterators = 0; 18 return DICT_OK; 19 } 20 21 dict *dictCreate(dictType *type, 22 void *privDataPtr) 23 { 24 dict *d = zmalloc(sizeof(*d)); 25 26 _dictInit(d,type,privDataPtr); 27 return d; 28 }
能夠調用dictCreate建立一個空的dict,它會分配好dict的空間,並初始化全部成員變量。在這裏把privdata傳入並保存。搜了一下整個redis源碼的dictCreate調用,看到傳入的值全爲NULL。目前的理解暫時不清楚這個變量是何時賦值的。初始化後的dict結構以下:
1 /* 2 +------------+ /+-----------+ 3 |dictType* | / |dictEntry**|-->NULL 4 +------------+ / +-----------+ 5 |privdata | / |size=0 | 6 +------------+/ +-----------+ 7 |ht[2] | |sizemask=0 | 8 +------------+\ +-----------+ 9 |rehashidx=-1| \ |used=0 | 10 +------------+ \ +-----------+ 11 |iterators=0 | \ 12 +------------+ \+-----------+ 13 |dictEntry**|-->NULL 14 +-----------+ 15 |size=0 | 16 +-----------+ 17 |sizemask=0 | 18 +-----------+ 19 |used=0 | 20 +-----------+ 21 */
剛建立好的dict是存不了任何數據的,其兩個hash table的size都爲0。這裏先說明一下resize操做:
1 #define DICT_HT_INITIAL_SIZE 4 2 3 static unsigned long _dictNextPower(unsigned long size) 4 { 5 unsigned long i = DICT_HT_INITIAL_SIZE; 6 7 if (size >= LONG_MAX) return LONG_MAX + 1LU; 8 while(1) { 9 if (i >= size) 10 return i; 11 i *= 2; 12 } 13 } 14 15 /* Expand or create the hash table */ 16 int dictExpand(dict *d, unsigned long size) 17 { 18 /* the size is invalid if it is smaller than the number of 19 * elements already inside the hash table */ 20 if (dictIsRehashing(d) || d->ht[0].used > size) 21 return DICT_ERR; 22 23 dictht n; /* the new hash table */ 24 unsigned long realsize = _dictNextPower(size); 25 26 /* Rehashing to the same table size is not useful. */ 27 if (realsize == d->ht[0].size) return DICT_ERR; 28 29 /* Allocate the new hash table and initialize all pointers to NULL */ 30 n.size = realsize; 31 n.sizemask = realsize-1; 32 n.table = zcalloc(realsize*sizeof(dictEntry*)); 33 n.used = 0; 34 35 /* Is this the first initialization? If so it's not really a rehashing 36 * we just set the first hash table so that it can accept keys. */ 37 if (d->ht[0].table == NULL) { 38 d->ht[0] = n; 39 return DICT_OK; 40 } 41 42 /* Prepare a second hash table for incremental rehashing */ 43 d->ht[1] = n; 44 d->rehashidx = 0; 45 return DICT_OK; 46 } 47 48 int dictResize(dict *d) 49 { 50 int minimal; 51 52 if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR; 53 minimal = d->ht[0].used; 54 if (minimal < DICT_HT_INITIAL_SIZE) 55 minimal = DICT_HT_INITIAL_SIZE; 56 return dictExpand(d, minimal); 57 }
_dictNextPower用於獲取當前要分配給hash table的size,獲得的值必定是2的倍數,初始值爲4。
dictExpand,從源碼註釋上看,它是爲了擴容hash table,或者建立一個。它不容許與rehashing操做同時進行,也不能強制縮容。在使用_dictNextPower獲得須要的size以後,它先是使用一個臨時變量n去分配空間,而後進行判斷,若ht[0].table的值爲NULL,則認爲是剛create出來的dict,直接把n賦值給ht[0],不然給ht[1],並開始rehashing操做。
dictResize操做就不用多說了。
3、rehashing操做
如有這樣一個dict,假設K一、K二、K三、K4計算出來的hash值分別爲0、五、二、7,使用sizemask計算出來的idx分別爲0、一、二、3
1 /* 2 +----+ 3 +->|K1|V|->NULL 4 +------------+ /+-----------+ +->+----------+ / +----+ 5 |dictType* | / |dictEntry**|--+ |dictEntry*|/ +----+ 6 +------------+ / +-----------+ +----------+ +-->|K2|V|->NULL 7 |privdata | / |size=4 | |dictEntry*|/ +----+ 8 +------------+/ +-----------+ +----------+ 9 |ht[2] | |sizemask=3 | |dictEntry*|\ +----+ 10 +------------+\ +-----------+ +----------+ +-->|K3|V|->NULL 11 |rehashidx=-1| \ |used=4 | |dictEntry*|\ +----+ 12 +------------+ \ +-----------+ +----------+ \ +----+ 13 |iterators=0 | \ +->|K4|V|->NULL 14 +------------+ \+-----------+ +----+ 15 |dictEntry**|-->NULL 16 +-----------+ 17 |size=0 | 18 +-----------+ 19 |sizemask=0 | 20 +-----------+ 21 |used=0 | 22 +-----------+ 23 */
1 static int _dictExpandIfNeeded(dict *d) 2 { 3 /* Incremental rehashing already in progress. Return. */ 4 if (dictIsRehashing(d)) return DICT_OK; 5 6 /* If the hash table is empty expand it to the initial size. */ 7 if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE); 8 9 /* If we reached the 1:1 ratio, and we are allowed to resize the hash 10 * table (global setting) or we should avoid it but the ratio between 11 * elements/buckets is over the "safe" threshold, we resize doubling 12 * the number of buckets. */ 13 if (d->ht[0].used >= d->ht[0].size && 14 (dict_can_resize || 15 d->ht[0].used/d->ht[0].size > dict_force_resize_ratio)) 16 { 17 return dictExpand(d, d->ht[0].used*2); 18 } 19 return DICT_OK; 20 }
經過函數_dictExpandIfNeeded,可知當used >= size且dict_can_resize == TRUE的時候,須要調用dictExpand進入rehashing狀態。dict_can_resize默認爲1
1 static int dict_can_resize = 1; 2 static unsigned int dict_force_resize_ratio = 5;
須要的size爲當前used * 2,即爲8。調用dictExpand以後的結構:
1 /* 2 +----+ 3 +->|K1|V|->NULL 4 +->+----------+ / +----+ 5 | |dictEntry*|/ +----+ 6 | +----------+ +-->|K2|V|->NULL 7 | |dictEntry*|/ +----+ 8 +------------+ /+-----------+ | +----------+ 9 |dictType* | / |dictEntry**|--+ |dictEntry*|\ +----+ 10 +------------+ / +-----------+ +----------+ +-->|K3|V|->NULL 11 |privdata | / |size=4 | |dictEntry*|\ +----+ 12 +------------+/ +-----------+ +----------+ \ +----+ 13 |ht[2] | |sizemask=3 | +->|K4|V|->NULL 14 +------------+\ +-----------+ +----+ 15 |rehashidx=0 | \ |used=4 | 16 +------------+ \ +-----------+ 17 |iterators=0 | \ 18 +------------+ \+-----------+ +->+----------+ 19 |dictEntry**|--+ |dictEntry*|->NULL 20 +-----------+ +----------+ 21 |size=8 | |dictEntry*|->NULL 22 +-----------+ +----------+ 23 |sizemask=7 | |dictEntry*|->NULL 24 +-----------+ +----------+ 25 |used=0 | |dictEntry*|->NULL 26 +-----------+ +----------+ 27 |dictEntry*|->NULL 28 +----------+ 29 |dictEntry*|->NULL 30 +----------+ 31 |dictEntry*|->NULL 32 +----------+ 33 |dictEntry*|->NULL 34 +----------+ 35 */
根據rehashing操做
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 uint64_t 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) { 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 }
rehashing操做將會把ht[0]裏,rehashidx的值對應的bucket下的全部dictEntry,移至ht[1],以後對rehashidx進行自增處理。當ht[0]->used爲0時,認爲ht[0]的全部dictEntry已經移至ht[1],此時return 0,不然 return 1,告訴調用者,還須要繼續進行rehashing操做。同時,rehashing時容許最多跳過10n的空bucket,就要退出流程。假設傳入的n=1,即只進行一次rehashing操做,轉換至完成以後的結構:
1 /* 2 3 +->NULL 4 +->+----------+ / 5 | |dictEntry*|/ +----+ 6 | +----------+ +-->|K2|V|->NULL 7 | |dictEntry*|/ +----+ 8 +------------+ /+-----------+ | +----------+ 9 |dictType* | / |dictEntry**|--+ |dictEntry*|\ +----+ 10 +------------+ / +-----------+ +----------+ +-->|K3|V|->NULL 11 |privdata | / |size=4 | |dictEntry*|\ +----+ 12 +------------+/ +-----------+ +----------+ \ +----+ 13 |ht[2] | |sizemask=3 | +->|K4|V|->NULL 14 +------------+\ +-----------+ +----+ 15 |rehashidx=1 | \ |used=3 | 16 +------------+ \ +-----------+ 17 |iterators=0 | \ 18 +------------+ \+-----------+ +->+----------+ +----+ 19 |dictEntry**|--+ |dictEntry*|-->|K1|V|->NULL 20 +-----------+ +----------+ +----+ 21 |size=8 | |dictEntry*|->NULL 22 +-----------+ +----------+ 23 |sizemask=7 | |dictEntry*|->NULL 24 +-----------+ +----------+ 25 |used=1 | |dictEntry*|->NULL 26 +-----------+ +----------+ 27 |dictEntry*|->NULL 28 +----------+ 29 |dictEntry*|->NULL 30 +----------+ 31 |dictEntry*|->NULL 32 +----------+ 33 |dictEntry*|->NULL 34 +----------+ 35 */
全部節點移完時:
1 /* 2 3 4 +->+----------+ 5 | |dictEntry*|->NULL 6 | +----------+ 7 | |dictEntry*|->NULL 8 +------------+ /+-----------+ | +----------+ 9 |dictType* | / |dictEntry**|--+ |dictEntry*|->NULL 10 +------------+ / +-----------+ +----------+ 11 |privdata | / |size=4 | |dictEntry*|->NULL 12 +------------+/ +-----------+ +----------+ 13 |ht[2] | |sizemask=3 | 14 +------------+\ +-----------+ 15 |rehashidx=4 | \ |used=0 | 16 +------------+ \ +-----------+ 17 |iterators=0 | \ 18 +------------+ \+-----------+ +->+----------+ +----+ 19 |dictEntry**|--+ |dictEntry*|-->|K1|V|->NULL 20 +-----------+ +----------+ +----+ 21 |size=8 | |dictEntry*|->NULL 22 +-----------+ +----------+ +----+ 23 |sizemask=7 | |dictEntry*|-->|K3|V|->NULL 24 +-----------+ +----------+ +----+ 25 |used=4 | |dictEntry*|->NULL 26 +-----------+ +----------+ 27 |dictEntry*|->NULL 28 +----------+ +----+ 29 |dictEntry*|-->|K2|V|->NULL 30 +----------+ +----+ 31 |dictEntry*|->NULL 32 +----------+ +----+ 33 |dictEntry*|-->|K4|V|->NULL 34 +----------+ +----+ 35 */
此時ht[0]->used爲0,釋放原ht[0]的hash table,把ht[1]賦值給ht[0],並設置ht[1] = NULL,最後重置rehashidx=-1,rehashing操做結束
1 /* 2 +------------+ /+-----------+ +-->+----------+ +----+ 3 |dictType* | / |dictEntry**|---+ |dictEntry*|-->|K1|V|->NULL 4 +------------+ / +-----------+ +----------+ +----+ 5 |privdata | / |size=8 | |dictEntry*|->NULL 6 +------------+/ +-----------+ +----------+ +----+ 7 |ht[2] | |sizemask=7 | |dictEntry*|-->|K3|V|->NULL 8 +------------+\ +-----------+ +----------+ +----+ 9 |rehashidx=-1| \ |used=4 | |dictEntry*|->NULL 10 +------------+ \ +-----------+ +----------+ 11 |iterators=0 | \ |dictEntry*|->NULL 12 +------------+ \+-----------+ +----------+ +----+ 13 |dictEntry**|->NULL |dictEntry*|-->|K2|V|->NULL 14 +-----------+ +----------+ +----+ 15 |size=0 | |dictEntry*|->NULL 16 +-----------+ +----------+ +----+ 17 |sizemask=0 | |dictEntry*|-->|K4|V|->NULL 18 +-----------+ +----------+ +----+ 19 |used=0 | 20 +-----------+ 21 */
rehashing操做的觸發共有兩種方式
一、定時操做
1 long long timeInMilliseconds(void) { 2 struct timeval tv; 3 4 gettimeofday(&tv,NULL); 5 return (((long long)tv.tv_sec)*1000)+(tv.tv_usec/1000); 6 } 7 8 /* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */ 9 int dictRehashMilliseconds(dict *d, int ms) { 10 long long start = timeInMilliseconds(); 11 int rehashes = 0; 12 13 while(dictRehash(d,100)) { 14 rehashes += 100; 15 if (timeInMilliseconds()-start > ms) break; 16 } 17 return rehashes; 18 }
外部傳入一個毫秒時間,在這時間內循環執行rehashing,每次執行100次。
二、操做時觸發
1 static void _dictRehashStep(dict *d) { 2 if (d->iterators == 0) dictRehash(d,1); 3 }
在插入、刪除、查找等操做時,順帶執行一次rehashing操做。值得注意的是,若是存在安全的迭代器,即d->iterators != 0,則不會進行rehashing操做
3、插入
獲取可插入新節點的bucket idx的方法:
1 static long _dictKeyIndex(dict *d, const void *key, uint64_t hash, dictEntry **existing) 2 { 3 unsigned long idx, table; 4 dictEntry *he; 5 if (existing) *existing = NULL; 6 7 /* Expand the hash table if needed */ 8 if (_dictExpandIfNeeded(d) == DICT_ERR) 9 return -1; 10 for (table = 0; table <= 1; table++) { 11 idx = hash & d->ht[table].sizemask; 12 /* Search if this slot does not already contain the given key */ 13 he = d->ht[table].table[idx]; 14 while(he) { 15 if (key==he->key || dictCompareKeys(d, key, he->key)) { 16 if (existing) *existing = he; 17 return -1; 18 } 19 he = he->next; 20 } 21 if (!dictIsRehashing(d)) break; 22 } 23 return idx; 24 }
此方法在進行查找idx以前,先進行一次判斷,是否須要rehashing操做。然後進行查找。idx的值就是經過hash函數計算出來的hash_value與sizemask作位運算的結果,而後遍歷此idx對應的bucket,若已存在相同的key,則認爲不可插入,並把對應的dictEntry用傳入的二級指針的方式傳出,供調用者使用。若不存在,則須要判斷是否正在進行rehashing操做。若在,則會對ht[1]作一次相同的操做。最終能夠獲得一個idx值,或傳出一個dictEntry。
因爲rehashing期間,將會把ht[0]的全部dictEntry依次轉移至ht[1],爲了防止新插入的dictEntry落到ht[0]已完成rehashing操做的bucket上,在rehashing期間,返回的可插入的idx必定是屬於ht[1]的。
插入方法:
1 dictEntry *dictAddRaw(dict *d, void *key, dictEntry **existing) 2 { 3 long index; 4 dictEntry *entry; 5 dictht *ht; 6 7 if (dictIsRehashing(d)) _dictRehashStep(d); 8 9 /* Get the index of the new element, or -1 if 10 * the element already exists. */ 11 if ((index = _dictKeyIndex(d, key, dictHashKey(d,key), existing)) == -1) 12 return NULL; 13 14 /* Allocate the memory and store the new entry. 15 * Insert the element in top, with the assumption that in a database 16 * system it is more likely that recently added entries are accessed 17 * more frequently. */ 18 ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0]; 19 entry = zmalloc(sizeof(*entry)); 20 entry->next = ht->table[index]; 21 ht->table[index] = entry; 22 ht->used++; 23 24 /* Set the hash entry fields. */ 25 dictSetKey(d, entry, key); 26 return entry; 27 }
若不存在相同key,則插入,不然,傳出dictEntry的指針。插入時,因爲沒有記錄每一個dictEntry鏈表的尾指針,因此使用頭插法,能夠節約插入時的時間消耗。
dictAddRaw作爲最終插入的方法,被多個方法所調用:
1 //若不存在,則插入,不然報錯 2 int dictAdd(dict *d, void *key, void *val) 3 { 4 dictEntry *entry = dictAddRaw(d,key,NULL); 5 6 if (!entry) return DICT_ERR; 7 dictSetVal(d, entry, val); 8 return DICT_OK; 9 } 10 11 //若存在,則替換value,不然插入 12 int dictReplace(dict *d, void *key, void *val) 13 { 14 dictEntry *entry, *existing, auxentry; 15 entry = dictAddRaw(d,key,&existing); 16 if (entry) { 17 dictSetVal(d, entry, val); 18 return 1; 19 } 20 auxentry = *existing; 21 dictSetVal(d, existing, val); 22 dictFreeVal(d, &auxentry); 23 return 0; 24 } 25 26 //若存在,則返回對應dictEntry,不然插入後返回新的dictEntry 27 dictEntry *dictAddOrFind(dict *d, void *key) { 28 dictEntry *entry, *existing; 29 entry = dictAddRaw(d,key,&existing); 30 return entry ? entry : existing; 31 }
對於一個剛剛create的dict:
1 /* 2 3 +------------+ /+-----------+ 4 |dictType* | / |dictEntry**|-->NULL 5 +------------+ / +-----------+ 6 |privdata | / |size=0 | 7 +------------+/ +-----------+ 8 |ht[2] | |sizemask=0 | 9 +------------+\ +-----------+ 10 |rehashidx=-1| \ |used=0 | 11 +------------+ \ +-----------+ 12 |iterators=0 | \ 13 +------------+ \+-----------+ 14 |dictEntry**|-->NULL 15 +-----------+ 16 |size=0 | 17 +-----------+ 18 |sizemask=0 | 19 +-----------+ 20 |used=0 | 21 +-----------+ 22 */
假設K一、K二、K三、K4計算出來的hash值分別爲0、五、二、7,使用sizemask計算出來的idx分別爲0、一、二、3
現調用dictAdd方法進行插入
一、插入K1
執行完dictAddRaw中的_dictKeyIndex裏的_dictExpandIfNeeded:
1 /* 2 3 +-->NULL 4 +------------+ /+-----------+ +->+----------+ / 5 |dictType* | / |dictEntry**|--+ |dictEntry*|/ 6 +------------+ / +-----------+ +----------+ +--->NULL 7 |privdata | / |size=4 | |dictEntry*|/ 8 +------------+/ +-----------+ +----------+ 9 |ht[2] | |sizemask=3 | |dictEntry*|\ 10 +------------+\ +-----------+ +----------+ +--->NULL 11 |rehashidx=-1| \ |used=0 | |dictEntry*|\ 12 +------------+ \ +-----------+ +----------+ \ 13 |iterators=0 | \ +-->NULL 14 +------------+ \+-----------+ 15 |dictEntry**|-->NULL 16 +-----------+ 17 |size=0 | 18 +-----------+ 19 |sizemask=0 | 20 +-----------+ 21 |used=0 | 22 +-----------+ 23 */
同時獲得其在ht[0]的idx = 0,且不在rehashing操做中,因而直接插入
1 /* 2 +----+ 3 +->|K1|V|->NULL 4 +------------+ /+-----------+ +->+----------+ / +----+ 5 |dictType* | / |dictEntry**|--+ |dictEntry*|/ 6 +------------+ / +-----------+ +----------+ +--->NULL 7 |privdata | / |size=4 | |dictEntry*|/ 8 +------------+/ +-----------+ +----------+ 9 |ht[2] | |sizemask=3 | |dictEntry*|\ 10 +------------+\ +-----------+ +----------+ +--->NULL 11 |rehashidx=-1| \ |used=1 | |dictEntry*|\ 12 +------------+ \ +-----------+ +----------+ \ 13 |iterators=0 | \ +-->NULL 14 +------------+ \+-----------+ 15 |dictEntry**|-->NULL 16 +-----------+ 17 |size=0 | 18 +-----------+ 19 |sizemask=0 | 20 +-----------+ 21 |used=0 | 22 +-----------+ 23 */
二、插入K二、K三、K4後:
1 /* 2 +----+ 3 +->|K1|V|->NULL 4 +------------+ /+-----------+ +->+----------+ / +----+ 5 |dictType* | / |dictEntry**|--+ |dictEntry*|/ +----- 6 +------------+ / +-----------+ +----------+ +-->|K2|V|->NULL 7 |privdata | / |size=4 | |dictEntry*|/ +----+ 8 +------------+/ +-----------+ +----------+ 9 |ht[2] | |sizemask=3 | |dictEntry*|\ +----+ 10 +------------+\ +-----------+ +----------+ +-->|K3|V|->NULL 11 |rehashidx=-1| \ |used=4 | |dictEntry*|\ +----+ 12 +------------+ \ +-----------+ +----------+ \ +----+ 13 |iterators=0 | \ +->|K4|V|->NULL 14 +------------+ \+-----------+ +----+ 15 |dictEntry**|-->NULL 16 +-----------+ 17 |size=0 | 18 +-----------+ 19 |sizemask=0 | 20 +-----------+ 21 |used=0 | 22 +-----------+ 23 */
三、此時如有一個K5,計算出來的hash值爲8,則:
i.所以刻不在rehashing操做,因此不用作處理
ii.執行完dictAddRaw中的_dictKeyIndex裏的_dictExpandIfNeeded:
1 /* 2 +----+ 3 +->|K1|V|->NULL 4 +->+----------+ / +----+ 5 | |dictEntry*|/ +----+ 6 | +----------+ +-->|K2|V|->NULL 7 | |dictEntry*|/ +----+ 8 +------------+ /+-----------+ | +----------+ 9 |dictType* | / |dictEntry**|--+ |dictEntry*|\ +----+ 10 +------------+ / +-----------+ +----------+ +-->|K3|V|->NULL 11 |privdata | / |size=4 | |dictEntry*|\ +----+ 12 +------------+/ +-----------+ +----------+ \ +----+ 13 |ht[2] | |sizemask=3 | +->|K4|V|->NULL 14 +------------+\ +-----------+ +----+ 15 |rehashidx=0 | \ |used=4 | 16 +------------+ \ +-----------+ 17 |iterators=0 | \ 18 +------------+ \+-----------+ +->+----------+ 19 |dictEntry**|--+ |dictEntry*|->NULL 20 +-----------+ +----------+ 21 |size=8 | |dictEntry*|->NULL 22 +-----------+ +----------+ 23 |sizemask=7 | |dictEntry*|->NULL 24 +-----------+ +----------+ 25 |used=0 | |dictEntry*|->NULL 26 +-----------+ +----------+ 27 |dictEntry*|->NULL 28 +----------+ 29 |dictEntry*|->NULL 30 +----------+ 31 |dictEntry*|->NULL 32 +----------+ 33 |dictEntry*|->NULL 34 +----------+ 35 */
同時獲得其在ht[1]的idx=0
iii.插入
1 /* 2 +----+ 3 +->|K1|V|->NULL 4 +->+----------+ / +----+ 5 | |dictEntry*|/ +----+ 6 | +----------+ +-->|K2|V|->NULL 7 | |dictEntry*|/ +----+ 8 +------------+ /+-----------+ | +----------+ 9 |dictType* | / |dictEntry**|--+ |dictEntry*|\ +----+ 10 +------------+ / +-----------+ +----------+ +-->|K3|V|->NULL 11 |privdata | / |size=4 | |dictEntry*|\ +----+ 12 +------------+/ +-----------+ +----------+ \ +----+ 13 |ht[2] | |sizemask=3 | +->|K4|V|->NULL 14 +------------+\ +-----------+ +----+ 15 |rehashidx=0 | \ |used=4 | 16 +------------+ \ +-----------+ 17 |iterators=0 | \ 18 +------------+ \+-----------+ +->+----------+ +----+ 19 |dictEntry**|--+ |dictEntry*|-->|K5|V|->NULL 20 +-----------+ +----------+ +----+ 21 |size=8 | |dictEntry*|->NULL 22 +-----------+ +----------+ 23 |sizemask=7 | |dictEntry*|->NULL 24 +-----------+ +----------+ 25 |used=1 | |dictEntry*|->NULL 26 +-----------+ +----------+ 27 |dictEntry*|->NULL 28 +----------+ 29 |dictEntry*|->NULL 30 +----------+ 31 |dictEntry*|->NULL 32 +----------+ 33 |dictEntry*|->NULL 34 +----------+ 35 */
四、此時如有一個K6,計算出來的hash值爲16,則:
i.此時已處理rehashing操做,執行一步:
1 /* 2 3 +-->NULL 4 +->+----------+ / 5 | |dictEntry*|/ +----+ 6 | +----------+ +-->|K2|V|->NULL 7 | |dictEntry*|/ +----+ 8 +------------+ /+-----------+ | +----------+ 9 |dictType* | / |dictEntry**|--+ |dictEntry*|\ +----+ 10 +------------+ / +-----------+ +----------+ +-->|K3|V|->NULL 11 |privdata | / |size=4 | |dictEntry*|\ +----+ 12 +------------+/ +-----------+ +----------+ \ +----+ 13 |ht[2] | |sizemask=3 | +->|K4|V|->NULL 14 +------------+\ +-----------+ +----+ 15 |rehashidx=1 | \ |used=3 | 16 +------------+ \ +-----------+ 17 |iterators=0 | \ 18 +------------+ \+-----------+ +->+----------+ +----+ +----+ 19 |dictEntry**|--+ |dictEntry*|-->|K1|V|->|K5|V|->NULL 20 +-----------+ +----------+ +----+ +----+ 21 |size=8 | |dictEntry*|->NULL 22 +-----------+ +----------+ 23 |sizemask=7 | |dictEntry*|->NULL 24 +-----------+ +----------+ 25 |used=2 | |dictEntry*|->NULL 26 +-----------+ +----------+ 27 |dictEntry*|->NULL 28 +----------+ 29 |dictEntry*|->NULL 30 +----------+ 31 |dictEntry*|->NULL 32 +----------+ 33 |dictEntry*|->NULL 34 +----------+ 35 */
ii.執行完dictAddRaw中的_dictKeyIndex裏的_dictExpandIfNeeded,因已在進行rehashing,因此不作任何處理,只返回其在ht[1]的idx 0
iii.頭插法將K6插入
1 /* 2 3 +-->NULL 4 +->+----------+ / 5 | |dictEntry*|/ +----+ 6 | +----------+ +-->|K2|V|->NULL 7 | |dictEntry*|/ +----+ 8 +------------+ /+-----------+ | +----------+ 9 |dictType* | / |dictEntry**|--+ |dictEntry*|\ +----+ 10 +------------+ / +-----------+ +----------+ +-->|K3|V|->NULL 11 |privdata | / |size=4 | |dictEntry*|\ +----+ 12 +------------+/ +-----------+ +----------+ \ +----+ 13 |ht[2] | |sizemask=3 | +->|K4|V|->NULL 14 +------------+\ +-----------+ +----+ 15 |rehashidx=1 | \ |used=3 | 16 +------------+ \ +-----------+ 17 |iterators=0 | \ 18 +------------+ \+-----------+ +->+----------+ +----+ +----+ +----+ 19 |dictEntry**|--+ |dictEntry*|-->|K6|V|->|K1|V|->|K5|V|->NULL 20 +-----------+ +----------+ +----+ +----+ +----+ 21 |size=8 | |dictEntry*|->NULL 22 +-----------+ +----------+ 23 |sizemask=7 | |dictEntry*|->NULL 24 +-----------+ +----------+ 25 |used=3 | |dictEntry*|->NULL 26 +-----------+ +----------+ 27 |dictEntry*|->NULL 28 +----------+ 29 |dictEntry*|->NULL 30 +----------+ 31 |dictEntry*|->NULL 32 +----------+ 33 |dictEntry*|->NULL 34 +----------+ 35 */
以上爲正常插入時的狀況,key已存在,或是調用另外兩個方法的狀況與之大同小異,再也不作過多敘述。
4、查找
1 dictEntry *dictFind(dict *d, const void *key) 2 { 3 dictEntry *he; 4 uint64_t h, idx, table; 5 6 if (d->ht[0].used + d->ht[1].used == 0) return NULL; /* dict is empty */ 7 if (dictIsRehashing(d)) _dictRehashStep(d); 8 h = dictHashKey(d, key); 9 for (table = 0; table <= 1; table++) { 10 idx = h & d->ht[table].sizemask; 11 he = d->ht[table].table[idx]; 12 while(he) { 13 if (key==he->key || dictCompareKeys(d, key, he->key)) 14 return he; 15 he = he->next; 16 } 17 if (!dictIsRehashing(d)) return NULL; 18 } 19 return NULL; 20 }
一樣,若在rehashing期間,則執行一次。首先在ht[0]裏查找,計算出hash值對應ht[0]的idx,取得其bucket,而後遍歷之,找到與指定key相同的dictEntry。若ht[0]中找不到指定的key,且正在進行rehashing操做,則去ht[1]以相同方式也查找一次。
redis額外提供一個,根據key只獲取其value的方法:
1 void *dictFetchValue(dict *d, const void *key) { 2 dictEntry *he; 3 4 he = dictFind(d,key); 5 return he ? dictGetVal(he) : NULL; 6 }
key不存在時返回NULL
5、刪除
刪除方法:
1 static dictEntry *dictGenericDelete(dict *d, const void *key, int nofree) { 2 uint64_t h, idx; 3 dictEntry *he, *prevHe; 4 int table; 5 6 if (d->ht[0].used == 0 && d->ht[1].used == 0) return NULL; 7 8 if (dictIsRehashing(d)) _dictRehashStep(d); 9 h = dictHashKey(d, key); 10 11 for (table = 0; table <= 1; table++) { 12 idx = h & d->ht[table].sizemask; 13 he = d->ht[table].table[idx]; 14 prevHe = NULL; 15 while(he) { 16 if (key==he->key || dictCompareKeys(d, key, he->key)) { 17 /* Unlink the element from the list */ 18 if (prevHe) 19 prevHe->next = he->next; 20 else 21 d->ht[table].table[idx] = he->next; 22 if (!nofree) { 23 dictFreeKey(d, he); 24 dictFreeVal(d, he); 25 zfree(he); 26 } 27 d->ht[table].used--; 28 return he; 29 } 30 prevHe = he; 31 he = he->next; 32 } 33 if (!dictIsRehashing(d)) break; 34 } 35 return NULL; /* not found */ 36 }
查找方式與dictFind相同。找到以後,由調用者指定是否要銷燬此dictEntry,若不銷燬,則要把對應指針傳出來,給外部使用。
此方法被兩個接口所調用:
1 int dictDelete(dict *ht, const void *key) { 2 return dictGenericDelete(ht,key,0) ? DICT_OK : DICT_ERR; 3 } 4 5 dictEntry *dictUnlink(dict *ht, const void *key) { 6 return dictGenericDelete(ht,key,1); 7 }
dictDelete就不用多說了,直接刪除對應dictEntry。關於爲何須要dictUnlink,源碼的註釋上寫道,若是有某種操做,須要先查找指定key對應的dictEntry,而後對其作點操做,接着就直接刪除,在沒有dictUnlink的時候,須要這樣:
1 entry = dictFind(...); 2 // Do something with entry 3 dictDelete(dictionary,entry);
實際須要查找兩次。而在有dictUnlink的狀況下:
1 entry = dictUnlink(dictionary,entry); 2 // Do something with entry 3 dictFreeUnlinkedEntry(entry);
只須要一次查找,配合專門的刪除操做,便可。
1 void dictFreeUnlinkedEntry(dict *d, dictEntry *he) { 2 if (he == NULL) return; 3 dictFreeKey(d, he); 4 dictFreeVal(d, he); 5 zfree(he); 6 }
6、銷燬
清空一個hash table的方法
1 int _dictClear(dict *d, dictht *ht, void(callback)(void *)) { 2 unsigned long i; 3 4 /* Free all the elements */ 5 for (i = 0; i < ht->size && ht->used > 0; i++) { 6 dictEntry *he, *nextHe; 7 8 if (callback && (i & 65535) == 0) callback(d->privdata); 9 10 if ((he = ht->table[i]) == NULL) continue; 11 while(he) { 12 nextHe = he->next; 13 dictFreeKey(d, he); 14 dictFreeVal(d, he); 15 zfree(he); 16 ht->used--; 17 he = nextHe; 18 } 19 } 20 /* Free the table and the allocated cache structure */ 21 zfree(ht->table); 22 /* Re-initialize the table */ 23 _dictReset(ht); 24 return DICT_OK; /* never fails */ 25 }
兩層循環,分別遍歷全部bucket與單bucket裏全部dictEntry進行釋放。關於這裏的 (i&65535) == 0的判斷,_dictClear方法僅在相同文件的方法dictEmpty與dictRelease調用
1 void dictRelease(dict *d) 2 { 3 _dictClear(d,&d->ht[0],NULL); 4 _dictClear(d,&d->ht[1],NULL); 5 zfree(d); 6 } 7 8 void dictEmpty(dict *d, void(callback)(void*)) { 9 _dictClear(d,&d->ht[0],callback); 10 _dictClear(d,&d->ht[1],callback); 11 d->rehashidx = -1; 12 d->iterators = 0; 13 }
dictRelease不用多說,傳入的callback爲NULL。而dictEmpty,搜索redis源碼全部文件的調用,
ccx@ccx:~/Desktop/redis-5.0.7/redis-5.0.7/src$ grep dictEmpty * -r blocked.c: dictEmpty(c->bpop.keys,NULL); db.c: dictEmpty(server.db[j].dict,callback); db.c: dictEmpty(server.db[j].expires,callback); dict.c:void dictEmpty(dict *d, void(callback)(void*)) { dict.h:void dictEmpty(dict *d, void(callback)(void*)); replication.c: dictEmpty(server.repl_scriptcache_dict,NULL); sentinel.c: dictEmpty(server.commands,NULL);
僅db.c裏傳了callback進來,對應的方法爲
1 long long emptyDb(int dbnum, int flags, void(callback)(void*));
繼續搜索:
ccx@ccx:~/Desktop/redis-5.0.7/redis-5.0.7/src$ grep emptyDb * -r cluster.c: emptyDb(-1,EMPTYDB_NO_FLAGS,NULL); db.c:long long emptyDb(int dbnum, int flags, void(callback)(void*)) { db.c: emptyDbAsync(&server.db[j]); db.c:/* Return the set of flags to use for the emptyDb() call for FLUSHALL*/ db.c: server.dirty += emptyDb(c->db->id,flags,NULL); db.c: server.dirty += emptyDb(-1,flags,NULL); debug.c: emptyDb(-1,EMPTYDB_NO_FLAGS,NULL); debug.c: emptyDb(-1,EMPTYDB_NO_FLAGS,NULL); lazyfree.c:void emptyDbAsync(redisDb *db) { replication.c: * data with emptyDb(), and while we load the new data received as an replication.c:/* Callback used by emptyDb() while flushing away old data to load*/ replication.c: emptyDb( server.h:long long emptyDb(int dbnum, int flags, void(callback)(void*)); server.h:void emptyDbAsync(redisDb *db);
真正調用的地方傳入的也是NULL,並不知道是拿來作什麼用的...
ps:用grep查找只是方便cv過來....
7、迭代器操做
數據結構:
1 typedef struct dictIterator { 2 dict *d; 3 long index; 4 int table, safe; 5 dictEntry *entry, *nextEntry; 6 /* unsafe iterator fingerprint for misuse detection. */ 7 long long fingerprint; 8 } dictIterator;
根據源碼註釋可知,若是是個安全的迭代器,即safe == 1,則在迭代中能夠調用dictAdd、dictFind等方法,不然只能調用dictNext。
index表示,ht[table]對應的bucket的idx。
獲取迭代器:
1 dictIterator *dictGetIterator(dict *d) 2 { 3 dictIterator *iter = zmalloc(sizeof(*iter)); 4 5 iter->d = d; 6 iter->table = 0; 7 iter->index = -1; 8 iter->safe = 0; 9 iter->entry = NULL; 10 iter->nextEntry = NULL; 11 return iter; 12 } 13 14 dictIterator *dictGetSafeIterator(dict *d) { 15 dictIterator *i = dictGetIterator(d); 16 17 i->safe = 1; 18 return i; 19 }
剛獲取的迭代器並不指向具體哪一個dictEntry
next操做:
1 dictEntry *dictNext(dictIterator *iter) 2 { 3 while (1) { 4 if (iter->entry == NULL) { 5 dictht *ht = &iter->d->ht[iter->table]; 6 if (iter->index == -1 && iter->table == 0) { 7 if (iter->safe) 8 iter->d->iterators++; 9 else 10 iter->fingerprint = dictFingerprint(iter->d); 11 } 12 iter->index++; 13 if (iter->index >= (long) ht->size) { 14 if (dictIsRehashing(iter->d) && iter->table == 0) { 15 iter->table++; 16 iter->index = 0; 17 ht = &iter->d->ht[1]; 18 } else { 19 break; 20 } 21 } 22 iter->entry = ht->table[iter->index]; 23 } else { 24 iter->entry = iter->nextEntry; 25 } 26 if (iter->entry) { 27 /* We need to save the 'next' here, the iterator user 28 * may delete the entry we are returning. */ 29 iter->nextEntry = iter->entry->next; 30 return iter->entry; 31 } 32 } 33 return NULL; 34 }
對於一個新的迭代器,首次調用時,會根據是否安全,作不一樣操做。安全的迭代器會給dict裏的計數器+1,不安全的將會記錄本字典的指紋。以後會遍歷ht[0],取到第一個非NULL的dictEntry。當ht[0]遍歷完且取不到非NULL的dictEntry時,若是正在進行rehashing操做,則會去ht[1]裏找。
如:
1 /* 2 3 +-------------------------+ 4 +----|dict * | 5 | +-------------------------+ 6 | |long index | 7 | +-------------------------+ 8 | |int table | 9 | +-------------------------+ 10 | |int safe | 11 | +-------------------------+ 12 | |dictEntry *entry |->NULL 13 | +-------------------------+ 14 | |dictEntry *entrynextEntry|->NULL 15 | +-------------------------+ 16 | |long long fingerprint | 17 | +-------------------------+ 18 | 19 | 20 | 21 | +-->NULL 22 | +->+----------+ / 23 | | |dictEntry*|/ +----+ 24 | | +----------+ +-->|K2|V|->NULL 25 | | |dictEntry*|/ +----+ 26 +--->+------------+ /+-----------+ | +----------+ 27 |dictType* | / |dictEntry**|--+ |dictEntry*|\ +----+ 28 +------------+ / +-----------+ +----------+ +-->|K3|V|->NULL 29 |privdata | / |size=4 | |dictEntry*|\ +----+ 30 +------------+/ +-----------+ +----------+ \ +----+ 31 |ht[2] | |sizemask=3 | +->|K4|V|->NULL 32 +------------+\ +-----------+ +----+ 33 |rehashidx=1 | \ |used=3 | 34 +------------+ \ +-----------+ 35 |iterators=0 | \ 36 +------------+ \+-----------+ +->+----------+ +----+ +----+ 37 |dictEntry**|--+ |dictEntry*|-->|K1|V|->|K5|V|->NULL 38 +-----------+ +----------+ +----+ +----+ 39 |size=8 | |dictEntry*|->NULL 40 +-----------+ +----------+ 41 |sizemask=7 | |dictEntry*|->NULL 42 +-----------+ +----------+ 43 |used=3 | |dictEntry*|->NULL 44 +-----------+ +----------+ 45 |dictEntry*|->NULL 46 +----------+ 47 |dictEntry*|->NULL 48 +----------+ 49 |dictEntry*|->NULL 50 +----------+ 51 |dictEntry*|->NULL 52 +----------+ 53 */
遍歷順序爲,K2,K3,K4,K1,K5。
迭代器銷燬:
1 void dictReleaseIterator(dictIterator *iter) 2 { 3 if (!(iter->index == -1 && iter->table == 0)) { 4 if (iter->safe) 5 iter->d->iterators--; 6 else 7 assert(iter->fingerprint == dictFingerprint(iter->d)); 8 } 9 zfree(iter); 10 }
與首次執行next操做相對應,若爲safe的迭代器,要給dict的計算減1,不然要校驗期間dict的指紋是否發生了變化 。
指紋的計算:
1 long long dictFingerprint(dict *d) { 2 long long integers[6], hash = 0; 3 int j; 4 5 integers[0] = (long) d->ht[0].table; 6 integers[1] = d->ht[0].size; 7 integers[2] = d->ht[0].used; 8 integers[3] = (long) d->ht[1].table; 9 integers[4] = d->ht[1].size; 10 integers[5] = d->ht[1].used; 11 12 /* We hash N integers by summing every successive integer with the integer 13 * hashing of the previous sum. Basically: 14 * 15 * Result = hash(hash(hash(int1)+int2)+int3) ... 16 * 17 * This way the same set of integers in a different order will (likely) hash 18 * to a different number. */ 19 for (j = 0; j < 6; j++) { 20 hash += integers[j]; 21 /* For the hashing step we use Tomas Wang's 64 bit integer hash. */ 22 hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1; 23 hash = hash ^ (hash >> 24); 24 hash = (hash + (hash << 3)) + (hash << 8); // hash * 265 25 hash = hash ^ (hash >> 14); 26 hash = (hash + (hash << 2)) + (hash << 4); // hash * 21 27 hash = hash ^ (hash >> 28); 28 hash = hash + (hash << 31); 29 } 30 return hash; 31 }
對於不安全的迭代器,在迭代過程當中,不容許操做任何修改dict的操做,是隻讀的,不會發生迭代器失效的問題。對於安全的迭代器,在進行操做本節點的時候,redis中記錄了當前迭代的bucket idx,以及當前dictEntry的next節點。若是隻是add操做,即便是用了頭插法把新dictEntry插在本節點以前,對迭代器自己並無影響。若是是delete了本節點,迭代器中還記錄了next節點的位置,調用next時直接取就好。若是next爲空,則能夠認爲當前bucket遍歷完了,取下一個bucket就好了。固然,若是在add/delete等操做的時候,進行了rehashing操做,那麼當前迭代器裏記錄的next,在rehashing以後,可能就不是當前節點新位置的next了。因此在使用安全迭代器的時候,禁止了rehashing操做。
8、其它操做
dict還支持其它的一些操做。
一、隨機獲取一個key,能夠用於一些隨機操做的dictGetRandomKey
二、隨機獲取n個key:dictGetSomeKeys
三、scan操做
關於scan操做,redis採用了一個很巧妙的方法,保證了在開始scan時未刪除的元素必定能遍歷到,又能保證儘可能少地重複遍歷。
這裏直接給個傳送門,這裏講得很好:
https://blog.csdn.net/gqtcgq/article/details/50533336
redis 5.0.7 下載連接
http://download.redis.io/releases/redis-5.0.7.tar.gz
源碼閱讀順序參考:
https://github.com/huangz1990/blog/blob/master/diary/2014/how-to-read-redis-source-code.rst
其它參考: