Redis数据结构-Dict

一、前言:Redis 的"心脏"------Dict

如果说 SDS 是 Redis 的基石,那么 Dict(字典) 就是其跳动的心脏 。它是 Redis 实现几乎所有高级数据结构的核心引擎:

  • 数据库本身:就是一个巨大的 Dict,Key 是用户定义的键,Value 是各种数据类型对象。
  • Hash 类型:底层直接使用 Dict 存储 field-value 对。
  • Set 类型:当元素非整数或数量过大时,底层转为 Dict(Value 为 NULL)。
  • ZSet 类型:Dict 用于存储 member 到 score 的映射,实现 O(1) 查询。

💡 核心价值 :
Dict 是 Redis 高性能读写的基石,其精妙的哈希表实现和"渐进式 Rehash"机制,解决了大数据量下扩容导致的服务卡顿问题!

本文将带你:

  • 拆解 Dict 的三层嵌套结构
  • 揭秘"渐进式 Rehash"如何做到无感扩容
  • 分析 Redis 如何在持久化期间智能控制内存增长

二、Dict 是什么?三层结构大起底

Redis 的 Dict 并非一个简单的哈希表,而是一个包含双哈希表、支持渐进式 Rehash 的复杂结构。

2.1 核心组件

Dict 由三个核心结构体组成:

1. dictEntry:哈希节点
cpp 复制代码
typedef struct dictEntry {
    void *key;                  // 键
    union {                     // 值(支持多种类型)
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next;     // 链表指针,解决哈希冲突
} dictEntry;
  • 作用:存储单个键值对。
  • 冲突解决 :采用链地址法(Separate Chaining) ,next 指针构成单向链表。
2. dictht:哈希表
cpp 复制代码
typedef struct dictht {
    dictEntry **table;          // 哈希桶数组(指针数组)
    unsigned long size;         // 哈希表大小(桶的数量,总是2的幂)
    unsigned long sizemask;     // 掩码 = size - 1,用于计算索引
    unsigned long used;         // 哈希表中已有节点的数量
} dictht;
  • 关键点 :size 总是 2 的幂 ,使得 index = hash & sizemask 可以代替取模运算,极大提升性能。
3. dict:字典主体
cpp 复制代码
typedef struct dict {
    dictType *type;             // 类型特定函数(如哈希函数、键比较函数等)
    void *privdata;             // 私有数据,传递给 type 中的函数
    dictht ht[2];               // **两个哈希表!用于渐进式 Rehash**
    long rehashidx;             // Rehash 索引,-1 表示未在 Rehash
    unsigned long iterators;    // 当前正在运行的安全迭代器数量
} dict;
  • 灵魂所在 :ht[2] 和 rehashidx 共同实现了渐进式 Rehash。

2.2 内存布局图

复制代码
+------------------+
|      dict        |
| +--------------+ |     +------------------+     +------------------+
| |   ht[0]      | |---->|   dictht (旧)    |---->| dictEntry 数组   |
| +--------------+ |     +------------------+     +------------------+
| |   ht[1]      | |---->|   dictht (新)    |---->| dictEntry 数组   |
| +--------------+ |     +------------------+     +------------------+
| rehashidx: n     |
+------------------+

三、Dict 的两大核心机制

3.1 机制 1:渐进式 Rehash(Incremental Rehashing)

问题 :传统哈希表在扩容/缩容时,需要一次性将所有键值对迁移到新表,这在大数据量下会导致服务长时间阻塞。

Redis 的解决方案 :分而治之!

  1. 准备阶段 :为 ht[1] 分配新空间(通常是 ht[0].used * 2)。
  2. 迁移阶段 :不一次性迁移 ,而是将迁移操作分散到后续的每一次 Dict 操作 (增删改查)中。
    • 每次操作时,顺带将 ht[0] 中 rehashidx 位置上的所有键值对迁移到 ht[1]。
    • 同时 rehashidx 自增。
  3. 完成阶段 :当 ht[0] 所有桶都迁移完毕,释放 ht[0],将 ht[1] 赋值给 ht[0],并重置 rehashidx = -1。

✅ 优势:

  • 无感扩容:避免了服务卡顿。
  • 平滑过渡 :查询时会同时在 ht[0] 和 ht[1] 中查找,保证数据一致性。

3.2 机制 2:智能的扩容/缩容策略

Dict 的扩容/缩容并非简单地基于 used/size(负载因子),而是结合了服务器当前状态:

触发条件
  • 扩容 :
    • 服务器没有 在执行 BGSAVE 或 BGREWRITEAOF(即无子进程):
      • 负载因子 ≥ 1 时扩容。
    • 服务器正在 执行 BGSAVE 或 BGREWRITEAOF:
      • 禁止自动扩容!(防止写时复制 COW 导致大量内存页复制)
      • 除非 负载因子 ≥ 5(危险阈值),此时强制扩容以防性能急剧下降。
  • 缩容 :
    • 负载因子 < 0.1 时,进行缩容。

相关配置 (硬编码在 dict.c 中)

cpp 复制代码
static int dict_can_resize = 1; // BGSAVE/BGREWRITEAOF 时置为 0
static unsigned int dict_force_resize_ratio = 5; // 强制扩容阈值

⚠️ 设计哲学 :在内存效率和性能稳定性之间取得精妙平衡。


四、Dict 在 Redis 中的应用场景

应用场景 Key Value 说明
数据库 用户定义的 Key redisObject 整个 Redis DB 就是一个 Dict
Hash 类型 Field Value 直接使用 Dict 作为底层
Set 类型 Member NULL 当 Set 不满足 intset 条件时
ZSet 类型 Member Score (double) 用于 O(1) 查询成员分数

五、动手实验:观察 Dict 的行为

5.1 查看 Hash 的底层编码

bash 复制代码
# 创建一个小 Hash
> HSET smallhash name "Alice" age "25"
(integer) 2

# 查看编码(小 Hash 可能用 ziplist)
> OBJECT ENCODING smallhash
"ziplist"

# 插入更多字段,使其转为 Dict
> HSET smallhash f1 v1 f2 v2 ... f512 v512
(integer) 512

# 再次查看(应变为 hashtable)
> OBJECT ENCODING smallhash
"hashtable"

5.2 观察 Rehash 过程(需开启 MONITOR)

虽然无法直接看到 rehashidx,但可以通过 INFO 命令间接感知:

bash 复制代码
# 在大量写入后,观察 used_memory 的变化趋势
# 如果内存是平滑增长而非突增,说明 Rehash 是渐进式的
> INFO memory

六、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
叉烧滑蛋1 天前
B站台湾大学郭彦甫|MATLAB 学习笔记|04 变量与档案存取
数据结构
瀚高PG实验室1 天前
HGDB V9.0.5 .pgpass 免密登录提示 IsEncryptNon-base64 encoding 警告处理
数据库·postgresql·瀚高数据库
j7~1 天前
【穷举、深搜与回溯算法】一棵决策树讲透全排列与子集
数据结构·c++·算法·leetcode·回溯·递归
龙腾AI白云1 天前
大模型检索增强生成(RAG)的技术原理与架构设计
数据库·python·机器学习·知识图谱
KaiwuDB1 天前
KaiwuDB 运维实战06:数据库备份与恢复:从理论到实战
运维·数据库·时序数据库·kaiwudb
数字新视界1 天前
空调控制器AIRC800的智能化管理与应用实践
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
学IT的周星星1 天前
LangGraph:用「流程图」的方式编排 Agent
数据库·人工智能·大模型·llm·流程图·agent
枫子有风1 天前
卡码网盘项目深挖
java·redis·jwt·es·pgvector
吃一碗粉只付一碗钱1 天前
10-令牌里的用户主键不要明文
服务器·数据库·sql·sqlserver·c#
蹄蹄1 天前
从“一次性检测“到“年度服务“:检测机构收入结构变了,合同和应收账款也要跟着变
大数据·数据库·人工智能