深入redis內部--字典實現


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;
}

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM