Redis数据结构底层(二):Dict

Redis 中的 dict 数据结构(字典)是一个重要的数据结构,用于存储键值对。其底层实现是通过哈希表来实现的,结合了哈希表和链表的方式来处理键值对的存储和检索。

组成源码:

Dict其实是由dict、dictht、dictEntry三部分组成的,它们的源码分别是:

dict(字典):

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;
​
typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2];
    long rehashidx;
    int iterators; 
} dict;

我们可以发现1个dict有2个dictht,这个在后面讲增量rehash有大用!

dictht(全局哈希表):

typedef struct dictht {
    dictEntry **table;
    unsigned long size;
    unsigned long sizemask;
    unsigned long used;
} dictht;

dictEntry(哈希节点):

typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;
} dictEntry;

它们之间的关系用图可以表示为:

image-20240708122830647

Dict特点:

使用Dic可以实现O(1)的平均复杂度的查找、插入和删除操作,在处理大量键时表现地尤为出色。redis数据结构中的set、zset中不允许出现重复数据的特性可以通过Dic去实现。

Dict与哈希冲突:

哈希表必定会存在哈希冲突的问题,在redis底层是使用链地址法解决哈希冲突问题的,并且采用的是头插法

image-20240708123442816

Dict的Rehash:

Dict的Rehash分为两种,一种是扩容,一种是缩容。

在讲rehash条件之前,我先讲一下哈希表的加载因子。

加载因子

哈希表的加载因子表示哈希表中已存储元素与哈希表容量的比值。加载因子的大小直接影响哈希表的性能和空间利用率。

  • 加载因子 = 哈希表中已存储元素数量 / 哈希表容量

加载因子的作用:

  1. 性能影响:加载因子控制着哈希表的平均填充程度,合适的加载因子能够保持哈希表的操作效率。

  2. 空间利用率:通过加载因子调整哈希表的填充程度,可以在保持效率的同时避免空间浪费。

Rehash条件:

扩容:

  • 哈希表的加载因子>=1,并且没有执行bgsave等后台进程

  • 哈希表的加载因子>5

  • 扩容后的新size大小为第一个大于等于dict.ht[0].used+1的2^n

缩容:

  • 每次删除元素时进行检查,当负载因子<0.1,会对哈希表进行收缩

  • 缩容后的新size大小为第一个大于等于dict.ht[0].used的2^n(不能小于4)

增量式Rehash(渐进式Rehash)过程与优势:

过程:

(1) 默认有两个全局哈希表,另一个哈希表容量是当前的2倍。

(2) 防止一次性拷贝带来的阻塞,而采用渐进式拷贝。处理一次请求,顺带拷贝一个DictEntry

(3) 释放原先哈希表空间,等待下次扩容时用。

优势:

  1. 减少阻塞

    • 由于数据迁移是逐渐进行的,渐进式 Rehash 能够减少对整个哈希表的阻塞时间,降低系统在数据迁移过程中的影响。

  2. 复杂度分摊

    • 将数据迁移过程分解为多个小步骤,避免了一次性大规模数据搬迁的复杂性和阻塞,使得操作更加平滑。

  3. 操作原子性

    • 在渐进式 Rehash 过程中,Redis 保证了对哈希表的操作是原子性的,避免由于并发操作导致数据一致性问题。

总结:

渐进式 Rehash 是 Redis 中对 dict 数据结构进行扩容或缩小时的一种优化策略,通过逐步迁移数据,减少系统阻塞时间,保证操作的稳定性和性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值