您的位置:首页 > 理论基础 > 数据结构算法

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

2017-04-07 22:33 239 查看

高屋建瓴

本文分析的是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 table
int _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……
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: