redis的字典定義和實現在dict.h和dict.c文件中。
1.字典結構
typedef struct dict { dictType *type; //定義了字典需要的函數 void *privdata; dictht ht[2]; //哈希表結構 int rehashidx; //下一個需要擴容的字典編號,若rehashidx == -1 則不會進行重新散列。 int iterators; //當前正在運行的迭代器數目 } dict;
其中涉及到數據結構,如下所示:
1.1 字典類型,包含了一系列字典所需要用到的函數
typedef struct dictType { unsigned int (*hashFunction)(const void *key); //hash函數 void *(*keyDup)(void *privdata, const void *key); //鍵復制 void *(*valDup)(void *privdata, const void *obj); //值復制 int (*keyCompare)(void *privdata, const void *key1, const void *key2); //key比較 void (*keyDestructor)(void *privdata, void *key); //key析構 void (*valDestructor)(void *privdata, void *obj); //value析構 } dictType;
1.2 哈希表結構,每個字典有兩個哈希表。當哈希表擴容時實現散列。
typedef struct dictht { dictEntry **table; unsigned long size; //桶的大小,是2的指數 unsigned long sizemask; //sizemask=size-1,方便取模(i%sizemask 開放鏈地址法處理hash沖突)。 unsigned long used; //哈希表中的記錄數 } dictht;
1.3 dictEntry為字典的條目,其定義如下:
typedef struct dictEntry { void *key; // 鍵 union { //值的共用體 void *val; uint64_t u64; int64_t s64; } v; struct dictEntry *next; } dictEntry;
2. 字典的遍歷--字典遍歷器
typedef struct dictIterator { dict *d; int table, index, safe; dictEntry *entry, *nextEntry; long long fingerprint; /* unsafe iterator fingerprint for misuse detection */ } dictIterator;
注意:當safe=1時,該遍歷器是安全的,即字典可以在遍歷的同時執行dictAdd, dictFind, 和別的函數。否則遍歷器是不安全的,遍歷時只能執行dictNext()。
迭代器提供了遍歷字典中所有元素的方法,通過dicGetIterator()獲得迭代器后,使用dictNext(dictIterator *)獲得下一個元素。遍歷的過程,先從ht[0]開始,依次從第一個桶table[0]開始遍歷桶中的元素,然后遍歷table[1],'*** ,table[size],若正在擴容,則會繼續遍歷ht[1]中的桶。遍歷桶中元素時,依次訪問鏈表中的每一個元素。
3.宏定義函數
#define dictFreeVal(d, entry) \ if ((d)->type->valDestructor) \ (d)->type->valDestructor((d)->privdata, (entry)->v.val) #define dictSetVal(d, entry, _val_) do { \ if ((d)->type->valDup) \ entry->v.val = (d)->type->valDup((d)->privdata, _val_); \ else \ entry->v.val = (_val_); \ } while(0) #define dictSetSignedIntegerVal(entry, _val_) \ do { entry->v.s64 = _val_; } while(0) #define dictSetUnsignedIntegerVal(entry, _val_) \ do { entry->v.u64 = _val_; } while(0) #define dictFreeKey(d, entry) \ if ((d)->type->keyDestructor) \ (d)->type->keyDestructor((d)->privdata, (entry)->key) #define dictSetKey(d, entry, _key_) do { \ if ((d)->type->keyDup) \ entry->key = (d)->type->keyDup((d)->privdata, _key_); \ else \ entry->key = (_key_); \ } while(0) #define dictCompareKeys(d, key1, key2) \ (((d)->type->keyCompare) ? \ (d)->type->keyCompare((d)->privdata, key1, key2) : \ (key1) == (key2)) #define dictHashKey(d, key) (d)->type->hashFunction(key) #define dictGetKey(he) ((he)->key) #define dictGetVal(he) ((he)->v.val) #define dictGetSignedIntegerVal(he) ((he)->v.s64) #define dictGetUnsignedIntegerVal(he) ((he)->v.u64) #define dictSlots(d) ((d)->ht[0].size+(d)->ht[1].size) #define dictSize(d) ((d)->ht[0].used+(d)->ht[1].used) #define dictIsRehashing(ht) ((ht)->rehashidx != -1)
4. 字典提供的api,有字典的創建,增加、刪除、修改記錄,還有迭代器(前面已經介紹)和自動擴容(下面介紹)。
dict *dictCreate(dictType *type, void *privDataPtr); int dictExpand(dict *d, unsigned long size); int dictAdd(dict *d, void *key, void *val); dictEntry *dictAddRaw(dict *d, void *key); int dictReplace(dict *d, void *key, void *val); dictEntry *dictReplaceRaw(dict *d, void *key); int dictDelete(dict *d, const void *key); int dictDeleteNoFree(dict *d, const void *key); void dictRelease(dict *d); dictEntry * dictFind(dict *d, const void *key); void *dictFetchValue(dict *d, const void *key); int dictResize(dict *d); dictIterator *dictGetIterator(dict *d); dictIterator *dictGetSafeIterator(dict *d); dictEntry *dictNext(dictIterator *iter); void dictReleaseIterator(dictIterator *iter); dictEntry *dictGetRandomKey(dict *d); void dictPrintStats(dict *d); unsigned int dictGenHashFunction(const void *key, int len); unsigned int dictGenCaseHashFunction(const unsigned char *buf, int len); void dictEmpty(dict *d); void dictEnableResize(void); void dictDisableResize(void); int dictRehash(dict *d, int n); int dictRehashMilliseconds(dict *d, int ms); void dictSetHashFunctionSeed(unsigned int initval); unsigned int dictGetHashFunctionSeed(void);
5.外部定義變量
/* 哈希表類型*/ extern dictType dictTypeHeapStringCopyKey; extern dictType dictTypeHeapStrings; extern dictType dictTypeHeapStringCopyKeyValue;
6. 自動擴容
Redis使用標識dict_can_resize來記錄字典是否可以擴容,可以使用dictEnableResize()方法和dictDisableResize()來改變此標識。使用dictResize()來擴容,但需要首先判斷是否允許擴容及是否正在擴容。若可以擴容,則調用dictExpand()擴容,然后調用dictRehashMilliseconds()啟動擴容,並指定擴容過程中記錄的copy速度。請看程序:
6.1 dictResize()
/* Resize the table to the minimal size that contains all the elements, * but with the invariant of a USED/BUCKETS ratio near to <= 1 */ int dictResize(dict *d) { int minimal; if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR; minimal = d->ht[0].used; if (minimal < DICT_HT_INITIAL_SIZE) minimal = DICT_HT_INITIAL_SIZE; return dictExpand(d, minimal); }
6.2 dictExpand()
/* Expand or create the hash table */ int dictExpand(dict *d, unsigned long size) { dictht n; /* the new hash table */ unsigned long realsize = _dictNextPower(size); /* the size is invalid if it is smaller than the number of * elements already inside the hash table */ if (dictIsRehashing(d) || d->ht[0].used > size) return DICT_ERR; /* Allocate the new hash table and initialize all pointers to NULL */ n.size = realsize; n.sizemask = realsize-1; n.table = zcalloc(realsize*sizeof(dictEntry*)); n.used = 0; /* Is this the first initialization? If so it's not really a rehashing * we just set the first hash table so that it can accept keys. */ if (d->ht[0].table == NULL) { d->ht[0] = n; return DICT_OK; } /* Prepare a second hash table for incremental rehashing */ d->ht[1] = n; d->rehashidx = 0; return DICT_OK; }
6.3
/* Rehash for an amount of time between ms milliseconds and ms+1 milliseconds */ int dictRehashMilliseconds(dict *d, int ms) { long long start = timeInMilliseconds(); int rehashes = 0; while(dictRehash(d,100)) { rehashes += 100; if (timeInMilliseconds()-start > ms) break; } return rehashes; }