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

高屋建瓴

本文分析的是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……

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值