Redis源码:dict数据结构(实现)

来源:互联网 发布:中国 美国 差距 知乎 编辑:程序博客网 时间:2024/04/29 06:18

高屋建瓴

本文分析的是src/dict.c文件。

从结构上来说,可以分为:
1. 私有函数(以下划线开头的一般都是,一般都是一些辅助函数);
2. 公开API。

从功能来说,可以分为:
1. dict初始化、析构;
2. 元素操作:查找、删除、插入、替换/修改值、清空数据;
3. 遍历dict,迭代器相关(比如需要持久化哈希表的数据时就很有用);
4. 扩展哈希表(也就是增量扩展哈希表);
5. 哈希函数(这些需要数学知识才能懂,个人觉得开箱即用就好了);
6. 随机化函数……(目前不知道有什么用)。

读完之后,我个人觉得,重点是理解好incremental rehashing是怎么做的以及哈希表的基本操作,其它的都很容易了。

创建与销毁dict

先回顾一下dict的定义,这对理解初始化和析构有好处:

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;

可以看到,其中除了基础类型(long和int)之外,还有指针和一个dictht数组。一般指针初始化为NULL就好了,然后rehashidx的初始值应该是-1,iterators的初始值应该是0,然后我的一些想法写成了注释放在代码里。

关于创建和初始化,涉及到下面三个函数:

// 其实我觉得这个函数名应该改为_dicthtInit才对,// 因为既不是对dict修改(而是对dictht进行修改),// 也不是Reset操作(而是初始化操作)static void _dictReset(dictht *ht){    ht->table = NULL;    ht->size = 0;    ht->sizemask = 0;    ht->used = 0;}/* Create a new hash table */// 其实我到现在还不知道privdata是什么……待补充dict *dictCreate(dictType *type,        void *privDataPtr){    // 注意Redis对malloc封装了,这使得可以自己选择不同的malloc方案    dict *d = zmalloc(sizeof(*d));    _dictInit(d,type,privDataPtr);    return d;}/* Initialize the hash table */// 上面这行注释也有问题,其实是Initialize the dict……// 机灵的朋友赶紧做pull request,说不定真的可以被accept// 还有很多地方的代码风格不统一,比如函数传参时多个参数之间只有逗号没有空格int _dictInit(dict *d, dictType *type,        void *privDataPtr){    _dictReset(&d->ht[0]);    _dictReset(&d->ht[1]);    d->type = type;    d->privdata = privDataPtr;    d->rehashidx = -1;    d->iterators = 0;    return DICT_OK;}

关于dict的销毁,涉及到下面两个函数,关键知识点是,如何释放动态创建的内容。

/* Destroy an entire dictionary */// 噢...注释又错了,应该是Destroy an entire dict hash tableint _dictClear(dict *d, dictht *ht, void(callback)(void *)) {    unsigned long i;    /* Free all the elements */    // 讲道理,ht->used > 0这个判断应该是已经够了的,i < ht->size还需要吗?(为了更安全?)    for (i = 0; i < ht->size && ht->used > 0; i++) {        dictEntry *he, *nextHe;        if (callback && (i & 65535) == 0) callback(d->privdata);        if ((he = ht->table[i]) == NULL) continue;        // 释放一条链表        // 需要先保存下一个结点,因为当前这个结点在赋值之前会被free掉        while(he) {            nextHe = he->next;            dictFreeKey(d, he);            dictFreeVal(d, he);            zfree(he);            ht->used--;            he = nextHe;        }    }    /* Free the table and the allocated cache structure */    zfree(ht->table);    /* Re-initialize the table */    _dictReset(ht);    return DICT_OK; /* never fails */}/* Clear & Release the hash table */// 值得注意的是,只有动态创建的数据需要自己释放// 还有,它的编程风格,一下子有空格,一下子没有,but not big deal.void dictRelease(dict *d){    _dictClear(d,&d->ht[0],NULL);    _dictClear(d,&d->ht[1],NULL);    zfree(d);}

增量扩展

later……

1 0
原创粉丝点击