博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Redis源码阅读-Dict哈希字典
阅读量:6230 次
发布时间:2019-06-21

本文共 6968 字,大约阅读时间需要 23 分钟。

Dict和Java中的HashMap很相似,都是数组开链法解决冲突。

 

但是Redis为了高性能, 有很多比较微妙的方法,例如 数组的大小总是2的倍数,初始大小是4。

rehash并不是一次就执行完,而是分多次执行。每次执行一部分。其中rehashidx表示现在hash到哪一个桶啦,-1表示现在并没有rehash.

dict包含两个dicttable, 编号为0,1,  dictht0是直接存储哈希表的地方, dictht1在rehash中用到,当rehashidx不为-1时, 查找key,同时在dictht1和dictht0中查找。

 

 

 

数据结构

 

typedef struct dictEntry {    void *key;    union {        void *val;        uint64_t u64;        int64_t s64;        double d;    } v;    struct dictEntry *next;} dictEntry;typedef struct dictType {    unsigned int (*hashFunction)(const void *key);    void *(*keyDup)(void *privdata, const void *key);    void *(*valDup)(void *privdata, const void *obj);    int (*keyCompare)(void *privdata, const void *key1, const void *key2);    void (*keyDestructor)(void *privdata, void *key);    void (*valDestructor)(void *privdata, void *obj);} dictType;/* This is our hash table structure. Every dictionary has two of this as we * implement incremental rehashing, for the old to the new table. */typedef struct dictht {    dictEntry **table;    unsigned long size;    unsigned long sizemask;    unsigned long used;} dictht;typedef struct dict {    dictType *type;    void *privdata;    dictht ht[2];    long rehashidx; /* rehashing not in progress if rehashidx == -1 */    int iterators; /* number of iterators currently running */} dict;/* If safe is set to 1 this is a safe iterator, that means, you can call * dictAdd, dictFind, and other functions against the dictionary even while * iterating. Otherwise it is a non safe iterator, and only dictNext() * should be called while iterating. */typedef struct dictIterator {    dict *d;    long index;    int table, safe;    dictEntry *entry, *nextEntry;    /* unsafe iterator fingerprint for misuse detection. */    long long fingerprint;} dictIterator;typedef void (dictScanFunction)(void *privdata, const dictEntry *de);

 

查找key

 

dictEntry *dictFind(dict *d, const void *key){    dictEntry *he;    unsigned int h, idx, table;    if (d->ht[0].size == 0) return NULL; /* We don't have a table at all */    if (dictIsRehashing(d)) _dictRehashStep(d);    h = dictHashKey(d, key);    for (table = 0; table <= 1; table++) {        idx = h & d->ht[table].sizemask;        he = d->ht[table].table[idx];        while(he) {            if (dictCompareKeys(d, key, he->key))                return he;            he = he->next;        }        if (!dictIsRehashing(d)) return NULL;    }    return NULL;}

 

redis的rehash是增量rehash,每次rehash一部分

rehash过程:

1. 从 dictht0的table 0到----N-1查找不为NULL的位置(非空桶)

2. 对该位置的链表进行处理, hash到dictht 1的table 1中。

rehash的函数,设置了n参数,表示要处理的非空桶的个数,但是在函数内部设置了最多访问10*n个空桶。

int dictRehash(dict *d, int n) {    int empty_visits = n*10; /* Max number of empty buckets to visit. */    if (!dictIsRehashing(d)) return 0;    while(n-- && d->ht[0].used != 0) {        dictEntry *de, *nextde;        /* Note that rehashidx can't overflow as we are sure there are more         * elements because ht[0].used != 0 */        assert(d->ht[0].size > (unsigned long)d->rehashidx);        while(d->ht[0].table[d->rehashidx] == NULL) {            d->rehashidx++;            if (--empty_visits == 0) return 1;        }        de = d->ht[0].table[d->rehashidx];        /* Move all the keys in this bucket from the old to the new hash HT */        while(de) {            unsigned int h;            nextde = de->next;            /* Get the index in the new hash table */            h = dictHashKey(d, de->key) & d->ht[1].sizemask;            de->next = d->ht[1].table[h];            d->ht[1].table[h] = de;            d->ht[0].used--;            d->ht[1].used++;            de = nextde;        }        d->ht[0].table[d->rehashidx] = NULL;        d->rehashidx++;    }    /* Check if we already rehashed the whole table... */    if (d->ht[0].used == 0) {        zfree(d->ht[0].table);        d->ht[0] = d->ht[1];        _dictReset(&d->ht[1]);        d->rehashidx = -1;        return 0;    }    /* More to rehash... */    return 1;}

 

 

 

 

和adlist一样,dict也有迭代器

 

迭代方法如下:

dictEntry *dictNext(dictIterator *iter){    //对表的桶进行遍历,直到找到一个非空桶,返回    while (1) {        if (iter->entry == NULL) {            dictht *ht = &iter->d->ht[iter->table];            if (iter->index == -1 && iter->table == 0) {                if (iter->safe)                    iter->d->iterators++;                else                    iter->fingerprint = dictFingerprint(iter->d);//对dict进行指纹            }            iter->index++;            //如果迭代到表的最后一个桶,就判断要不要迭代第二个表            if (iter->index >= (long) ht->size) {                if (dictIsRehashing(iter->d) && iter->table == 0) {                    iter->table++;                    iter->index = 0;                    ht = &iter->d->ht[1];                } else {                    break;                }            }            iter->entry = ht->table[iter->index];        } else {            iter->entry = iter->nextEntry;        }        if (iter->entry) {            /* We need to save the 'next' here, the iterator user             * may delete the entry we are returning. */            iter->nextEntry = iter->entry->next;            return iter->entry;        }    }    return NULL;}

Dict的API如下:

/* API *//* 字典创建, type参数制定各类对字典的自定义函数,会初始化dictht, dict */dict *dictCreate(dictType *type, void *privDataPtr);int dictExpand(dict *d, unsigned long size);/* 添加键值对,内部调用addRaw和setvalue ,如果已经存在,返回NULL*/int dictAdd(dict *d, void *key, void *val);/* 添加键 ,如果已经存在,返回NULL*/dictEntry *dictAddRaw(dict *d, void *key);/* 添加一个key,如果存在,直接设置value,设置key的value */int dictReplace(dict *d, void *key, void *val);/* 添加一个key,如果存在,直接返回 */dictEntry *dictReplaceRaw(dict *d, void *key);/* 删除一个节点,需要free那个节点 */int dictDelete(dict *d, const void *key);/* 删除一个节点,不需要free那个节点 */int dictDeleteNoFree(dict *d, const void *key);/* 删除dict*/void dictRelease(dict *d);/* 查找key*/dictEntry * dictFind(dict *d, const void *key);/* 查找key的value*/void *dictFetchValue(dict *d, const void *key);/* 将dict的size设置和元素数量一样,但是符合2的倍数*/int dictResize(dict *d);dictIterator *dictGetIterator(dict *d);dictIterator *dictGetSafeIterator(dict *d);dictEntry *dictNext(dictIterator *iter);void dictReleaseIterator(dictIterator *iter);dictEntry *dictGetRandomKey(dict *d);unsigned int dictGetSomeKeys(dict *d, dictEntry **des, unsigned int count);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(callback)(void*));void dictEnableResize(void);void dictDisableResize(void);int dictRehash(dict *d, int n);/* rehash,设置一个最长时间*/int dictRehashMilliseconds(dict *d, int ms);void dictSetHashFunctionSeed(unsigned int initval);unsigned int dictGetHashFunctionSeed(void);unsigned long dictScan(dict *d, unsigned long v, dictScanFunction *fn, void *privdata);

 

上面的API很多函数内部都会判断当前是不是还在rehash状态,如果是,就rehash一步。

 

在rehash前,会判断是不是有迭代器存在,如果有迭代器存在,就不rehash

static void _dictRehashStep(dict *d) {

if (d->iterators == 0) dictRehash(d,1);
}

 

转载于:https://www.cnblogs.com/stonehat/p/4855781.html

你可能感兴趣的文章
App 卸载记录
查看>>
JavaScript变量和作用域
查看>>
开源SIP服务器加密软件NethidPro升级
查看>>
《别做正常的傻瓜》的一些读书心得
查看>>
作业:实现简单的shell sed替换功能和修改haproxy配置文件
查看>>
Altium 拼板方法以及 注意的 地方
查看>>
PMP考试的过与只是
查看>>
Apache Pulsar中的地域复制,第1篇:概念和功能
查看>>
python pip install 出现 OSError: [Errno 1] Operation not permitted
查看>>
oracle12C 重做日志
查看>>
zookeeper与kafka安装部署及java环境搭建(发布订阅模式)
查看>>
从源码分析scrollTo、scrollBy、Scroller方法的区别和作用
查看>>
聊聊单元測试(一)——EasyMock
查看>>
使用 Chrome 来调试你的 Android App
查看>>
ObjectOutputStream和ObjectInputStream
查看>>
微服务架构的设计模式
查看>>
【C++】继承时构造函数和析构函数
查看>>
android 点击屏幕关闭 软键盘
查看>>
开发中三个经典的原则
查看>>
nodejs基础 -- NPM 使用介绍
查看>>