redis基本数据结构(2)

字典dict

dict的主要实现从低到高依赖以下三个数据结构:

//具体的hash表的一项,用指正组成一个链表
typedef struct dictEntry {
    void *key;//键
    //值,联合体,可以使多种类型的值
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;//hash冲突时使用开链发解决冲突,指向下一个节点
} dictEntry;
//dict结构体中的hash表的描述结构体
typedef struct dictht {
    dictEntry **table;//指向dictEntry*的指针的数组
    unsigned long size;//hash表的大小,即数组长度
    unsigned long sizemask;//hash表大小的掩码,用于计算索引
    unsigned long used;//hash表已有的节点数
} dictht;
//字典的主结构体
typedef struct dict {
    dictType *type;
    //如果这个字典有定义特殊的处理函数,则这个指针指向包含那些函数的指针的结构体
    void *privdata;//私有数据
    dictht ht[2];//hash表,当表长度不够rehash时需要用到第二张表
    long rehashidx;
    int iterators; 
} dict;

redis里的字典在底层是用hash表来实现的,整个hash表的每一项的指针是一个地址连续的数组,要传入字典的参数根据相应的hash函数计算后存储在这个数组的某一项中,如果出现了hash冲突,则使用开链法解决,用struct dictEntry *next来组成一个链表,详细结构如下:
这里写图片描述

冲突解决与rehash
在使用开链法解决冲突问题的时候,链表的插入采用的是头插法(速度上的考虑)。
随着操作的不断进行,hash表保存的键值对一般会不断地增加,为了让表的负载因子维持在一个合理的范围之内,就需要用rehash对整个hash表进行调整。
rehash的步骤一般分为三步:
1. 为dict的ht[1]分配空间,空间一般为ht[0]的两倍
2. 通过重新计算hash的映射,把ht[0]里的项映射到ht[1]中
3. 映射完成后,释放ht[0]再把ht[1]赋给ht[0]。
由于rehash的整个过程会消耗大量时间,可能会导致整个redis在一段时间内停止服务,所以redis采取的是渐进式的rehash策略。在这种策略下,系统同时维持ht[0]和ht[1]两个hash表,然后在后台每隔n毫秒执行一次rehash的服务。rehash服务把ht[0]上的数据项rehash到ht[1]上,并做一下标识,直到rehash完毕。
rehash期间对dict的操作
由于rehash期间同时存在两个hash表,所以需要特别考虑rehash期间对hash表的操作。rehash期间,如果存在对hash表的更新、查找和删除操作,则需要同时在两个表中进行;如果存在对hash表的插入操作,则只在ht[1]中进行。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值