ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

Redis 字典的实现以及渐进式 Rehash 过程

2021-06-12 15:29:43  阅读:229  来源: 互联网

标签:Rehash rehash void Redis 渐进式 ht dict 哈希 字典


Redis 字典

Redis 字典 dict 用途有两个,首先实现数据库键空间(Key space),Redis 是一个保存键值对的数据库,数据库的键值对由字典保存,每个数据库都有一个对应的字典, 这个字典被称之为键空间(key space)。

其次字典 dict 还作为 Hash 类型键的底层实现之一,当 Hash 类型的键使用 hashtable 编码时,也使用 dict 进行存储。

Redis 采用链地址法解决 key hash 冲突问题,以头插的方式插入到冲突节点的前面。
在这里插入图片描述

底层源码

单机 Redis 默认有 16 个 redisDb ,通过 select(n) 选择使用哪个 redisDb cluster 模式下只有一个 redisDb,每个 redisDb 里面都有一个字典 dict,用于保存 set 进来的键值对。

typedef struct redisDb {
    dict *dict;                 /* The keyspace for this DB */
    dict *expires;              /* Timeout of keys with a timeout set */
    dict *blocking_keys;        /* Keys with clients waiting for data (BLPOP)*/
    dict *ready_keys;           /* Blocked keys that received a PUSH */
    dict *watched_keys;         /* WATCHED keys for MULTI/EXEC CAS */
    int id;                     /* Database ID */
    long long avg_ttl;          /* Average TTL, just for stats */
    list *defrag_later;         /* List of key names to attempt to defrag one by one, gradually. */
} redisDb;

字典 dict 里面包含两个 dictht,ht[0],ht[1],ht[1] 用于渐进式 rehash;

typedef struct dict {
    dictType *type; // 字典类型
    void *privdata; // 私有数据
    dictht ht[2];  // 哈希表[两个]
    long rehashidx; // 记录rehash 进度的标志,值为-1表示rehash未进行
    unsigned long iterators; //  当前正在迭代的迭代器数
} dict;

typedef struct dictType {
    uint64_t (*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;

哈希表 dictht 里面包含一个 table,里面的节点就是哈希表节点 dicEntry,dictEntry 里面保存着元素的地址 *val,和指向下一个 dictEntry 的指针,这个指针将哈希值相同的键值对连接在一起。

typedef struct dictht {
    dictEntry **table; // 哈希表数组
    unsigned long size; // 哈希表的大小
    unsigned long sizemask; // 哈希表大小掩码
    unsigned long used; // 哈希表现有节点的数量
} dictht;

typedef struct dictEntry {
    void *key;  // 键定义
    // 值定义
    union {
        void *val; // 自定义类型
        uint64_t u64;  // 无符号整形
        int64_t s64; // 有符号整形
        double d; // 浮点型
    } v;
    struct dictEntry *next; //指向下一个哈希表节点
} dictEntry;

所以 Redis Key 的分布,可以用下图表示:在这里插入图片描述

rehash 过程

当哈希表 dict 需要扩展或者收缩时,Redis 会进行 rehash 操作,即将 h[0] 里面的元素移到 h[1]

rehash 的过程不是一次性的,而是将 rehash 所需的计算工作分摊到每次对字典进行增删改查的操作上。

字典维护 rehashidx 字段,从 0 开始,每次将 rehashidx 上面的键值对 rehash 到 ht[1],然后 rehashidx++,最终 ht[0] 所有键值对移到 ht[1],那么 rehashidx = -1,表示 rehash 完成。

渐进式 rehash 过程中,Redis 会同时使用 ht[0] 和 ht[1] 两张表,delete,find,update 操作会在两个哈希表上运行。rehash 过程中,新增的键值对保存到 ht[1],保证 rehash 执行完成时,ht[0] 为空表,然后将 ht[1] = ht[0]。

Rehash 时机

负载因子 = 哈希表已保存节点数量 / 哈希表大小
load_factor = ht[0].used / ht[0].size

当服务器没有执行 BGSAVE or BGREWRITEAOF 命令时,若负载因子 >=1,哈希表将会执行扩展操作;

当服务器 执行 BGSAVE or BGREWRITEAOF 命令时,若负载因子 >= 5,哈希表将会执行扩展操作。

标签:Rehash,rehash,void,Redis,渐进式,ht,dict,哈希,字典
来源: https://blog.csdn.net/u013151053/article/details/117840413

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有