redis底层数据结构之Dict

该文详细介绍了Redis中Dict的内部结构,包括哈希表、哈希节点和字典,以及它们如何实现键值对的映射。Dict使用了双哈希表结构,当负载因子超过特定阈值时会进行扩容或收缩。扩容和收缩通过渐进式rehash完成,确保在不影响正常操作的同时平滑过渡。在rehash过程中,新增操作直接写入新哈希表,其他操作会同时在两个表中进行,直到旧表数据完全迁移。
摘要由CSDN通过智能技术生成

 redis是一个键值型数据库,而键与值的映射关系正是通过Dict来实现的

Dict由三部分组成,分别是哈希表(DictHashTable),哈希节点(DictEntry)与字典(Dict)

 

整体Dict结构如下: 

 

Dict中的 HashTable就是数组结合单链表实现的(默认容量为4),当集合中元素较多时,必然导致哈希冲突,链表过长,则查询效率会大大降低。因此Dict在每次新增键值对时都会检查负载因子

(LoadFactor = used / size),当满足下列条件时会进行扩容

1.哈希表的LoadFactor >= 1,并且服务器没有执行BGSAVE或者BGREWRITEAOF等后台进程

2.哈希表的LoadFactor >= 5

扩容后的大小为第一个大于等于 used+1 的2^n

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

不管是进行扩容还是进行收缩,都必定会创建新的哈希表,导致哈希表的size和sizemask变化,而key的查询与sizemask有关,因此必须对哈希表中的每一个key重新计算索引,插入新的哈希表,这个过程称为rehash。rehash过程如下:

计算新 hash 表的 realeSize ,值取决于当前要做的是扩容还是收缩:
如果是扩容,则新 size 为第一个大于等于 dict.ht[0].used + 1 2^n
如果是收缩,则新 size 为第一个大于等于 dict.ht[0].used 2^n  (不得小于 4
按照新的 realeSize 申请内存空间,创建 dictht ,设置dictht中的值后赋值给 dict.ht[1]
设置 dict.rehashidx = 0 ,标示开始 rehash
dict.ht[0] 中的每一个 dictEntry rehash 到dict.ht[1]
每次执行新增、查询、修改、删除操作时,都检查一下 dict.rehashidx 是否大于 -1 ,如果是则将 dict.ht[0].table[ rehashidx ] entry 链表 rehash dict.ht[1] ,并且将 rehashidx ++ 。直至 dict.ht[0] 的所有数据都 rehash dict.ht[1]
dict.ht[1] 赋值给 dict.ht[0] ,给 dict.ht[1] 初始化为空哈希表,释放原来的 dict.ht[0] 的内存
rehashidx 赋值为 -1 ,代表 rehash 结束
rehash 过程中,新增操作,则直接写入 ht [1] ,查询、修改和删除则会在 dict.ht[0] dict.ht[1] 依次查找并执行。这样可以确保 ht [0] 的数据只减不增,随着 rehash 最终为空
总结:
1.Dict中包含了两个哈希表,平常用ht[0],ht[1]用来rehash
2.底层是数组加链表
Dict的伸缩:
1.当LoadFactor > 1并且没有子进程任务或LoadFactor > 5,Dict扩容
2.当LoadFactor < 0.1 时,Dict收缩
3.扩容大小第一个大于等于 used+1 的2^n
4.收缩大小为第一个大于等于  used+1 的2^n
5.Dict采用渐进式的rehash,每次访问Dict时进行一次rehash,将数组一个角标下的元素(可能是一个链表)复制到 ht[1] 中
6.rehash时 ht[0] 只减不增,新增操作只在ht[1]中进行,其他操作在两个哈希表中都有可能进行
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值