[redis] 数据结构 -- 字典

本文详细介绍了Redis中dict数据结构的内部机制,包括type属性和privdata属性的作用,以及渐进式rehash策略。在rehash过程中,为避免大字典导致的性能影响,Redis采用逐步扩容和缩容策略,如当元素数量达到数组长度的5倍时强制扩容,负载因子低于0.1时进行缩容。此外,set结构底层同样基于字典实现,但所有value为空。
摘要由CSDN通过智能技术生成

dict

  1. 结构

    // 哈希表结构
    typeof struct dictht {
        // 哈希表数组
        dictEntry **table;
        // 哈希表大小
        unsigned long size;
        // 哈希表大小掩码,用于计算索引值, 总是等于 size - 1
        unsigned long sizemask;
        // 该哈希表已有节点的数量
        unsigned long used;
    }
    // 哈希节点结构
    typeof struct dictEntry {
        // 键
        void *key;
        // 值
        union {
            void *val;
            uint64_t u64;
            int64_t s64;
            double d;
        } v;
        // 指向下一个哈希表节点,形成链表
        // 该指针可以将多个哈希值相同的键值对连接在一起,以此来解决 hash 冲突的问题
        struct dictEntry *next;
    } dictEntry;
    // dict 结构
    typeof  struct dict {
         // 类型特定函数
        dictType *type;
         //  私有数据
        void *privdata;
        // 哈希表
        dictht ht[2];
        // rehash索引,当rehash不在进行时值为-1
        long rehashidx;
    }
    
  2. 说明
    dict 中的 type 属性和 privdata 属性是针对不同类型的键值对,为创建多态字典而设置的;
    type 属性指向的是 dictType 结构的指针,它保存了一簇用于操作特定类型键值对的函数,而 privdata 则是保存用于传给这些函数的可选参数。
    ht[1] 只有对 ht[0] 进行 rehash 的过程是才会被使用。
    rehashidx 记录了 rehash 的进度。

  3. 渐进式 rehash
    因为对内存扩容操作会涉及到数据的迁移操作O(N),对 redis 很难承受这样耗时的过程(大字典表)。
    因此采取了 rehash 操作,其过程为:

    1. 主动 rehash(dictRehashMilliseconds 服务器定时任务) 或 被动 rehash(_dictRehashStep 负责)时,生成一个新的 ht[1],  并设置 rehashidx 为 0。
    2.  rehash 期间当有新键值时,将添加到 ht[1] 中;搜索会先从 ht[0] 中查,如果找不到再从 ht[1] 中查。
    3. rehash 过程是渐进的,默认情况下,单次最少转移空桶数量为 10 次(版本 5.0)。
    4. 当 rehash 过程完全结束,那么修改 ht[0] 指针引用,让他指向新的字典表 ht[1],并设置 rehashidx 为 -1,标记整个字典 rehash 结束。
    5. 需要注意的是,每次 CURD 操作时,如果当前为 rehash 状态,需要去完成一个桶的转移,然后才能返回(参考 dictAddRaw)。
    
  4. 扩容条件
    正常情况下,当 hash 表中元素的个数等于第一维数组的长度时,就会开始扩容,扩容的新数组是原数组大小的 2 倍。
    不过如果 Redis 正在做 bgsave,为了减少内存页的过多分离 (Copy On Write),Redis 尽量不去扩容 (dict_can_resize),
    但是如果 hash 表已经非常满了,元素的个数已经达到了第一维数组长度的 5 倍 (dict_force_resize_ratio),说明 hash 表已经过于拥挤了,这个时候就会强制扩容。
    没有在执行 BGSAVE 或 BGREWRITEAOF 命令时,负载因子大于1进行扩容
    在执行 BGSAVE 或 BGREWRITEAOF 命令时,负载因子大于5进行扩容
    拓展操作给 ht[1] 分配第一个大于等于 ht[0].used*2 的 2 的 n 次方幂的空间

  5. 缩容条件
    当 hash 表因为元素的逐渐删除变得越来越稀疏时,Redis 会对 hash 表进行缩容来减少 hash 表的第一维数组空间占用。缩容的条件是元素个数低于数组长度的 10%。缩容不会考虑 Redis 是否正在做 bgsave。
    负载因子小于 0.1 时进行缩容
    缩容操作给 ht[1] 分配第一个大于等于 ht[0].used 的 2 的 n 次方幂的空间

  6. set 的结构
    set 的结构底层实现也是字典,只不过所有的 value 都是 NULL,其它的特性和字典一模一样。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值