redis数据结构-dict

本文介绍了Redis中dict数据结构的实现原理,包括dictEntry、dictht和dict的概念,以及如何解决哈希冲突。重点阐述了dict的扩容机制——rehash,包括渐进式rehash的策略,以确保在大规模数据时仍能保持高性能。
摘要由CSDN通过智能技术生成

介绍

dict(字典)是redis中的一种数据结构,也是key-value的基础,下面介绍dict的实现原理。

数据结构

先上图
在这里插入图片描述

dictEntry

对于初学者来说,这张图比较难理解,dict的结构也确实比sds要更加复杂,那么下面我们就从最简单的开始讲起。

能看到这篇文章的同学,应该都已经知道了,redis是一个key-value数据库,一个key只对应一个value

例如:

  • key_1 -> value_1
  • key_2 -> value_2

那么一个key-value对就是一个Entry,在redis中叫作dictEntry(上图中标绿的),首先来看下dictEntry的定义

typedef struct dictEntry {
   
    void *key;  // key
    union {
   
        void *val;// value
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next; // 下一个节点
} dictEntry;

key和value都存储在dictEntry中

dictht

那么问题来了,redis中可以存储大量的dictEntry,这些dictEntry是如何被组织起来的,以及如何迅速查找到的?

redis中dictiht的数据结构(上图标黄的部分)的字段table指向了一个dictEntry数组(数组的初始大小为4),而dictEntry就会按照一定的规则被存放在table中。

例如,dictEntry(key_1 -> value_1)要存放到table中,redis首先会对"key_1"做哈希运算(这里不详细讨论),最终得到一个值n,(介于0~size-1之间),这个dictEntry将会被存放到table[n]的位置,反之,查询key_1时,也会做相同的运算,到对应的位置去获取key_1的值。

然而这里有一个问题,不同的key做完哈希后,有可能出现哈希值相同的情况,因此可能出现冲突的情况。redis的解决方式是,将哈希值相同的key所在的dictEntry用指针的方式链接起来。

在这里插入图片描述

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

看起来dictht已经实现了存储key-value的所有功能,那为什么还需要dict呢?

dict的出现,主要是为了解决dictiht扩容的问题,为什么dictiht需要扩容?原因在于,当key的数量达到一定程度时,相同哈希值得key会越来越多,链接也会越来越长,效率也会越来越低。

dict

dict的出现,正是为了解决dictiht的扩容问题,简单地说,当dictiht需要扩容时,会先创建一个2倍大小的新的dictiht,然后逐渐将数据迁移过来,迁移完成后将老的dictiht释放。

这样一来,dictEntry,dictht和dict的关系就理清了,接下来看一些细节的东西。

  • dict定义
typedef struct dict {
   
    dictType *type; // 特定于类型的处理函数
    void *privdata; // 类型处理函数的私有数据
    dictht ht[2];   // 2个哈希表
    long rehashidx; // 记录rehash状态的标志,值为-1表示rehash未进行
    int iterators; // 正在运作的迭代器数量(这里其实指的是安全迭代器的数量,安全迭代器在迭代的时候不允许rehash)
} dict;

rehash

dict的扩容操作,也叫做rehash。dict中存在两个dictht,dictht[0]和dictht[1],dictht[0]用来存储数据,dictht[1]只有在做rehash的过程中才会使用到。

dictht中存在一个指标,负载因子(used/size)当满足一下两个情况之一,需要执行rehash

  • 当负载因子>1,并且没有在做rdb dump和aof rewrite操作
  • 如果负载因子>5

相关代码

static int dict_can_resize = 1; //当
static unsigned int dict_force_resize_ratio = 5;
static int _dictExpandIfNeeded(dict *d)
{
   
    if (dictIsRehashing(d)) return DICT_OK; // 字典正在rehash,直接返回
    if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE); // 如果字典的大小为0,则将大小设置为初始值4
    // 当负载因子达到1时,就可以扩容了,不过字典还要处于可以扩容状态,如果处于不可扩容状态的话,负载因子达到5也是可以强制扩容的
    if (d->ht[0].used >= d->ht
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值