Redis源码学习简记(三)dict哈希原理与个人理解

Redis采用分治策略实现哈希桶扩容,通过新旧两张表逐步迁移数据,减少一次性扩容带来的性能影响。文章介绍了dict数据结构、dictCreate、dictInit、dictResize等关键函数,特别是dictRehash的分步更新操作,以及dictAdd、dictDelete等添加与删除操作。此外,还提及了dictFingerprint在迭代器有效性中的作用。
摘要由CSDN通过智能技术生成

Redis哈希的实现比较难理解,为了提升性能,采用分治的思想。当哈希桶需要扩容的时候,并不是直接全部更新,而是定义两个哈希表,新表旧表。当处于扩容状态时,每进行一次插入或查找操作就进行一次更新操作,即把原始表的一个数据移到新的表中。当所有数据迁移完成后,新表变成旧表,旧表变成新表。这样在扩容的过程就变成了分步进行。减少了因扩容而产生的长时间等待。具体的实现,代码见,仔细看了代码后,了解会深很多。

先来看看dict的数据结构

数据结构

typedef struct dict { //哈希的数据结构
    dictType *type; //dictType 其实是hash的一些函数操作,见dictType详细描述
    void *privdata;   //私有数据。
    dictht ht[2];    //前面所说的两个表,ht[0] 为旧表 ht[1]为新表
    long rehashidx; /* rehashing not in progress if rehashidx == -1 */ //不处于扩容状态是为-1  其余时刻为扩容进行到的下标。
    unsigned long iterators; /* number of iterators currently running */  //迭代器的数量
} dict;
typedef struct dictType {  //type  用户自定义的一些函数
    uint64_t (*hashFunction)(const void *key);    //hash函数
    void *(*keyDup)(void *privdata, const void *key);//key复制
    void *(*valDup)(void *privdata, const void *obj);//val复制函数
    int (*keyCompare)(void *privdata, const void *key1, const void *key2);//两个key值比较
    void (*keyDestructor)(void *privdata, void *key);//key的析构函数
    void (*valDestructor)(void *privdata, void *obj);//val的析构函数
} dictType;
typedef struct dictht { //每一张表的数据结构 
    dictEntry **table;  //使用的是拉链法。
    unsigned long size;//哈希表的大小
    unsigned long sizemask;//长度-1 为了方便获取元素的index
    unsigned long used;//哈希表已经使用的大小
} dictht;
typedef struct dictEntry { //每一个hash元素的结构
    void *key;
    union {            //联合体存储double int uint 或者是别的数据的指针
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next; //拉链法中的下一个节点
} dictEntry;
typedef struct dictIterator { //dict的迭代器
    dict *d;  //迭代器对应的表
    long index;//table中的下标,标记table[index]
    int table, safe;//table 判断是表th[0] 还是th[1] safe 判断是是否为安全
    dictEntry *entry, *nextEntry;//当前拉链的元素与下一个元素
    /* unsafe iterator fingerprint for misuse detection. */
    long long fingerprint;//不安全的迭代器,即正在更新的中的表的迭代器,使用指纹来标记。
} dictIterator;

整个dict的图,一样是垃圾字体。



看完了数据结构,让我们来看看源代码实现吧。挑了一些重要的来看。

dictCreate dict创建

dict *dictCreate(dictType *type,
        void *privDataPtr)
{
    dict *d = zmalloc(sizeof(*d));  //机智的使用*d 则sizeof 为d的类型,不是指针的大小。

    _dictInit(d,type,privDataPtr);//利用该函数对新建的hash表进行初始化。
    return d;
}

_dictInit 接上面的初始化

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;
}
static void _dictReset(dictht *ht)
{
    ht->table = NULL;
    ht->size = 0;
    ht->sizemask = 0;
    ht->used = 0;
}

dictResize  扩容入口,接下来就是艰难的开始了,下面是一些的重要实现方法。

/* R
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值