Redis中dictht和dict的实现

 更新时间:2026年08月31日 09:13:46   作者:Arva .  
本文主要介绍了Redis中dictht和dict的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

dictht (字典哈希表)

dictht 是 “Dictionary Hash Table” 的缩写,它代表一个最基础的哈希表结构。

它的定义如下(源码在 src/dict.h):

typedef struct dictht {
    dictEntry **table;      // 指向一个 bucket 数组(指针的指针)
    unsigned long size;     // 哈希表的大小(bucket 数组的长度)
    unsigned long sizemask; // 用于计算索引值的掩码,总是等于 size-1
    unsigned long used;     // 哈希表中已有节点的数量
} dictht;

对每个字段的解释:

  • table:这是一个数组指针,数组中的每个元素都是一个 dictEntry *(指向键值对节点的指针)。这个数组就是哈希表的“buckets”(桶)。
  • size:table 数组的长度。这个值总是 2 的幂(例如 4, 8, 16, 32 …)。这是为了能使用高效的位运算 hash & sizemask 来代替取模运算 hash % size。
  • sizemask:掩码,其值恒等于 size - 1。计算键的索引时,用哈希值 & sizemask 得到的结果必定落在 [0, size-1] 的范围内,正好是数组的索引。
  • used:当前哈希表中实际存储了多少个键值对(dictEntry 节点)。used/size 就是哈希表的负载因子(Load Factor),这个值是触发哈希表扩容或缩容的关键。

一个 dictht 的结构示意图:

        dictht
+------------------+
|      table       |----->  [dictEntry*] -> [dictEntry] -> [dictEntry] -> NULL   (index 0)
|                  |        [dictEntry*] -> NULL                                 (index 1)
|     size: 4      |        [dictEntry*] -> [dictEntry] -> NULL                  (index 2)
|  sizemask: 3     |        [dictEntry*] -> [dictEntry] -> [dictEntry] -> NULL   (index 3)
|     used: 8      |
+------------------+

(上图展示了一个大小为 4 的哈希表,存储了 8 个元素,说明发生了哈希冲突,并用链表法解决)

2. 核心概念:dict (字典)

dict 是 Redis 字典的主结构体,它封装了两个 dictht 和一些管理功能,使得哈希表能够平滑地扩容和缩容(Rehashing)。

它的定义如下:

typedef struct dict {
    dictType *type; // 类型特定函数,为实现多态而存在
    void *privdata; // 私有数据,传递给类型特定函数的可选参数
    dictht ht[2];   // 两个哈希表,为什么是两个?请看下面的解释
    long rehashidx; // Rehashing 的进度,如果不是 -1 则表示正在进行 rehash
    int16_t pauserehash; // Rehashing 暂停标志,>0 时表示 rehash 被暂停
} dict;

对每个字段的详细解释:

  • type 和 privdata:这两个成员是为了实现多态而存在的。dictType 结构体包含了一系列函数指针(如哈希函数 hashFunction、键复制函数 keyDup、值复制函数 valDup 等)。这使得 Redis 的字典可以存储不同类型的键和值,只需为不同的用途设置不同的 dictType 即可(例如用于存储数据库键值对的字典和用于存储键过期时间的字典,它们的函数指针不同)。
  • ht[2]:这是最关键的部分。一个 dict 结构包含了两个 dictht 哈希表。在通常情况下,所有的数据只存放在 ht[0] 中,ht[1] 为 NULL。只有在进行 Rehashing(重新哈希) 时,才会同时使用两个哈希表。
  • rehashidx:Rehashing 的进度指示器。
    • 当 rehashidx == -1 时,表示没有在进行 Rehashing。
    • 当 rehashidx >= 0 时,表示正在进行 Rehashing,并且它的值表示 ht[0] 中当前正在被 rehash 的桶的索引(例如,rehashidx=3 表示 ht[0] 的索引 3 处的所有节点都已迁移到 ht[1])。
  • pauserehash:表示 rehash 是否被暂停。大于 0 时表示暂停。这是为了在某些操作(如持久化 BGSAVE)时避免 rehash 带来过多的内存写入,保证性能。

3. dictht 和 dict 的关系与协作:Rehashing

dictht 是静态的存储单元,而 dict 是动态的管理器。它们最精妙的协作体现在 Rehashing(重新散列) 过程中。

为什么需要 Rehashing?

随着操作不断执行,哈希表的负载因子 used/size 会变得过大(冲突增多,效率下降)或过小(浪费内存)。为了维持高效性能,需要在适当的时候对哈希表进行扩容或缩容。

Rehashing 的过程(渐进式重新散列)

  1. 触发:当满足一定条件时(如负载因子 > 1 且没有在进行 BGSAVE,或 > 5 等),触发扩容。为 ht[1] 分配空间,大小为第一个大于等于 ht[0].used * 2 的 2 的幂(例如 used=5,则新 size 为 16)。如果是缩容,则大小为第一个大于等于 ht[0].used 的 2 的幂(2的幂取模的时候直接与很方便)。
  2. 将 dict 的 rehashidx 从 -1 设置为 0,表示 Rehashing 正式开始。
  3. 渐进式迁移:Redis 不会一次性将 ht[0] 的所有键值对都迁移到 ht[1](如果表很大,这会阻塞服务器很长时间)。而是采用分而治之的策略,将迁移工作分散到后续的每次增、删、改、查命令中。
    • 每当对字典进行任何操作时,除了执行指定的操作,还会顺带将 ht[0] 在 rehashidx 索引上的整个 bucket 链表迁移到 ht[1] 中。
    • 迁移完成后,将 rehashidx 的值加 1。
  4. 完成:当 rehashidx 递增到 ht[0].size 的值时,意味着 ht[0] 的所有数据都已迁移完毕。此时,释放 ht[0] 的空间,将 ht[1] 设置为新的 ht[0],并为 ht[1] 创建一个新的空白哈希表以备下次使用。最后,将 rehashidx 重置为 -1。

在 Rehashing 期间如何查找?
查找一个键时,会同时查找 ht[0] 和 ht[1] 两个表,先查 ht[0],如果没找到再查 ht[1]。

总结与对比

特性dictht (字典哈希表)dict (字典)
角色数据存储单元,一个静态的哈希表数组字典管理器,一个动态的、封装好的字典对象
核心功能存储键值对数据,处理哈希冲突(链表法)管理两个 dictht,实现类型多态,控制 Rehashing 流程
包含关系被 dict 所包含包含两个 dictht (ht[0] 和 ht[1])
Rehashing是 Rehashing 操作的对象是 Rehashing 过程的控制器(通过 rehashidx)
类比像是房子的毛坯房(只有房间结构)像是房子的业主+装修队(负责管理、维护、扩建房子)

简单来说:

  • dictht 是砖瓦和水泥,是存储数据的基础结构。
  • dict 是建筑师和施工队,它使用两块地皮(ht[0] 和 ht[1]),聪明地、逐步地将旧房子(ht[0])的砖瓦拆下来,在新地皮上建成一个更大的新房子(ht[1]),并且在整个施工期间,旧房子仍然可以正常使用。

这种“一个管理器 + 两个存储单元”的设计是 Redis 字典实现高效、稳定且能够渐进式扩容的关键。

dict的rehash

渐进式rehash(防止主进程被长时间阻塞)

到此这篇关于Redis中dictht和dict的实现的文章就介绍到这了,更多相关Redis dictht和dict内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Linux服务器安装redis数据库图文教程

    Linux服务器安装redis数据库图文教程

    Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。这篇文章主要介绍了Linux服务器安装redis数据库图文教程,需要的朋友可以参考下
    2018-03-03
  • 线上Redis一直报连接超时该如何解决

    线上Redis一直报连接超时该如何解决

    这篇文章主要为大家详细介绍了项目开发时如果出现线上Redis一直报连接超时的问题该如何解决,文中的示例代码简洁易懂,需要的小伙伴可以借鉴一下
    2023-08-08
  • redis数据结构之String详解

    redis数据结构之String详解

    Redis以String为基础类型,因C字符串效率低、非二进制安全等问题,采用SDS动态字符串实现高效存储,通过RedisObject封装,支持多种编码方式(如RAW、EMBSTR、INT)和内存优化策略,灵活管理数据结构与内存使用
    2025-08-08
  • Redis教程(十):持久化详解

    Redis教程(十):持久化详解

    这篇文章主要介绍了Redis教程(十):持久化详解,本文讲解了Redis提供了哪些持久化机制、RDB机制的优势和劣势、AOF机制的优势和劣势、其它等内容,需要的朋友可以参考下
    2015-04-04
  • Redis常用数据类型命令实例汇总

    Redis常用数据类型命令实例汇总

    这篇文章主要介绍了Redis常用数据类型命令实例汇总,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • Redis热点Key问题的完整解决方案:

    Redis热点Key问题的完整解决方案:

    热点Key就像双十一爆款商品,1%的Key承担了90%的流量!本文将用电商案例,教你如何智能分流、化解压力,并通过代码示例讲解的非常详细,需要的朋友可以参考下
    2025-08-08
  • redis实现排行榜的简单方法

    redis实现排行榜的简单方法

    这篇文章主要给大家介绍了关于redis实现排行榜的简单方法,文中通过示例代码介绍的非常详细,对大家学习或者使用redis具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-08-08
  • 利用Redis进行数据缓存的项目实践

    利用Redis进行数据缓存的项目实践

    在实际的业务场景中,Redis 一般和其他数据库搭配使用,用来减轻后端数据库的压力,本文就介绍了利用Redis进行数据缓存的项目实践,具有一定的参考价值,感兴趣的可以了解一下
    2022-06-06
  • Redis 哨兵与集群脑裂问题及其解决

    Redis 哨兵与集群脑裂问题及其解决

    本文主要介绍了Redis 哨兵与集群脑裂问题及其解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-04-04
  • redis中RedissonLock如何实现等待锁的

    redis中RedissonLock如何实现等待锁的

    本文主要介绍了redis中RedissonLock如何实现等待锁的,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11

最新评论