RedisNotes

第 29 章:大师之路:源码、版本与成长地图

zjc 于 2026-01-29 发布

这是《Redis 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 会使用 Redis、会排查故障、会设计缓存架构,已经能胜任大多数业务场景。但要成为真正的 Redis 专家,还需要能阅读源码、理解版本演进、跟踪社区讨论,并把 Redis 的设计思想迁移到其他存储系统中。

本章给出一套可执行的源码阅读路线、调试方法、版本演进重点和 L1 到 L5 成长地图。

29.1 为什么值得读 Redis 源码

Redis 源码有四个特点:

  1. 代码量相对小,核心模块清晰;
  2. C 语言实现直接,能看清数据结构和内存布局;
  3. 单线程事件模型简洁,适合学习网络编程;
  4. 存储组件中的经典问题都能看到工程答案。

读完源码后,你会更容易回答这些问题:

更重要的是,你会开始理解“简单模型 + 极致工程优化”的设计哲学。

29.2 获取与准备源码

29.2.1 克隆源码

git clone https://github.com/redis/redis.git
cd redis
git checkout 7.4

建议选择一个稳定分支或 tag,不要长期在 master 上阅读。源码和线上版本保持一致,遇到行为差异时更容易对照。

29.2.2 编译

make -j8
make test

如果需要调试符号:

make distclean
make OPTIMIZATION=0 MALLOC=libc -j8

常用调试工具:

工具 用途
gdb / lldb 断点、单步、查看结构体
clangd / ccls 代码跳转
valgrind 内存问题
perf CPU 热点
strace / ltrace 系统调用
redis-cli monitor 观察命令

29.2.3 调试启动

gdb ./src/redis-server
(gdb) break serverCron
(gdb) run --port 6380 --save "" --appendonly no

另一个窗口:

redis-cli -p 6380 ping
redis-cli -p 6380 set hello world

调试生产问题时,不要直接对线上进程附加 gdb;长时间停顿会阻塞所有请求。应在测试环境复现,或使用指标、日志和采样工具。

29.3 源码目录地图

Redis 根目录中最值得关注的是 src/

目录或文件 内容
server.c 服务器启动、命令分发、serverCron
ae.c 事件循环抽象
networking.c 客户端、协议解析、输出缓冲区
db.c 数据库、查找、过期辅助逻辑
object.c RedisObject 创建、引用计数
t_string.c 各数据类型命令实现
expire.c 过期逻辑与删除策略
evict.c 内存淘汰
latency.c 延迟监控
rdb.c RDB 生成与加载
aof.c AOF 写入、重写、恢复
replication.c 主从复制
cluster.c 集群与 Gossip
module.c 模块系统
config.c 配置解析
blocked.c 阻塞命令
listpack.c 紧凑编码
zipmap.c / ziplist.c 旧紧凑结构
sds.c 动态字符串
dict.c 字典
adlist.c 双端链表
skiplist.c 跳表
t_stream.c Stream

测试目录:

目录 内容
tests/unit 单元测试
tests/integration 复制、AOF、集群集成测试
tests/cluster 集群测试
deps 第三方依赖

阅读顺序建议从命令执行链路开始,再进入数据结构,最后读持久化、复制和集群。

29.4 源码阅读路线

阶段一:命令是怎么执行的

目标文件:

server.c
networking.c
db.c
object.c
t_string.c

阅读链路:

main -> initServer -> aeMain
接受连接 -> 创建 client
读取 socket -> 解析 RESP
查找命令表 -> 校验参数和权限
调用命令实现函数
添加输出缓冲区 -> 写回客户端

重点问题:

  1. 命令表在哪里定义;
  2. 命令的 arity、flags 表示什么;
  3. lookupKey 读写都有哪些副作用;
  4. client 的输入输出缓冲区如何管理;
  5. 为什么命令执行天然串行。

练习:给一个普通命令加上自定义日志,观察执行链路。不要修改生产版本源码,只在本地分支实验。

阶段二:核心数据结构

目标文件:

sds.c
dict.c
t_hash.c
t_list.c
t_set.c
t_zset.c
t_string.c
listpack.c
skiplist.c

阅读重点:

文件 关注点
sds.c 长度、扩容、二进制安全、多种 sdshdr 类型
dict.c 渐进 rehash、哈希算法、迭代器
t_zset.c ziplist/listpack 与 skiplist 转换
listpack.c 紧凑存储、级联更新差异
skiplist.c 插入、删除、范围查询

重点问题:

  1. 对象编码阈值由哪些配置控制;
  2. 渐进 rehash 期间读写如何处理;
  3. ZSet 为什么同时保留 dict 和 skiplist;
  4. listpack 如何解决 ziplist 的级联更新问题;
  5. 内存预估为什么要看编码而不是只看元素数量。

阶段三:过期与淘汰

目标文件:

expire.c
evict.c
db.c
lazyfree.c
object.c

阅读重点:

  1. TTL 如何写入过期字典;
  2. 惰性删除和定期删除的触发条件;
  3. 定期删除的采样数量和时间上限;
  4. LRU/LFU 时钟字段如何更新;
  5. 同步删除和异步删除的选择;
  6. maxmemory 达到后的写入路径。

练习:

CONFIG SET maxmemory 100mb
CONFIG SET maxmemory-policy allkeys-lru
DEBUG SLEEP 0
INFO memory
INFO stats

观察 evicted_keysexpired_keys 和内存变化,再对应源码阅读。

阶段四:事件循环与网络

目标文件:

ae.c
ae_epoll.c
ae_kqueue.c
networking.c
io_threads.c

阅读重点:

  1. aeMain 如何分发时间事件和文件事件;
  2. epoll/kqueue 如何抽象;
  3. beforeSleep 做哪些事情;
  4. IO 线程何时启用;
  5. 输出缓冲区限制如何断开客户端。

重点认知:Redis 的单线程不是没有并发,而是把并发点放在网络 IO、后台任务和异步释放内存上,命令执行保持串行以简化一致性。

阶段五:持久化

目标文件:

rdb.c
aof.c
childinfo.c
bio.c

阅读重点:

  1. bgsave 如何 fork 子进程;
  2. 写时复制对内存的影响;
  3. RDB 格式和加载流程;
  4. AOF 写入缓冲和刷盘策略;
  5. AOF 重写期间增量如何追加;
  6. Redis 7 multipart AOF 的文件组织;
  7. 混合持久化如何组织 RDB 前缀和 AOF 增量。

练习:构造写负载,观察 INFO persistence

redis-cli info persistence
redis-cli latency latest
redis-cli config get save
redis-cli config get appendfsync

阶段六:复制与哨兵

目标文件:

replication.c
server.c
sentinel.c

阅读重点:

  1. PSYNC 参数与返回值;
  2. replid、replid2 和 offset;
  3. replication backlog 的写入与截断;
  4. 全量同步 RDB 与增量缓冲如何交接;
  5. 主从心跳和偏移量上报;
  6. 哨兵主观下线、客观下线和 leader 选举;
  7. 故障转移步骤和复制拓扑调整。

实验:

redis-server --port 6380
redis-cli -p 6381 replicaof 127.0.0.1 6380
redis-cli -p 6380 info replication
redis-cli -p 6381 info replication

模拟断开和恢复,观察是部分重同步还是全量同步。

阶段七:Cluster

目标文件:

cluster.c
cluster_legacy.c
cluster_slot_stats.c

不同版本文件拆分差异较大,阅读时以当前分支为准。

重点问题:

  1. 节点 ID、槽位和 epoch 的作用;
  2. Gossip 消息类型;
  3. PFAIL 和 FAIL 的判定;
  4. MOVED、ASK 如何产生;
  5. reshard 时 migrate 的 key 流程;
  6. replica 迁移和故障转移;
  7. 为什么多 key 命令要求同槽。

实验:

redis-cli --cluster create 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003
redis-cli --cluster check 127.0.0.1:7001
redis-cli -p 7001 cluster info
redis-cli -p 7001 cluster nodes

阶段八:扩展模块

目标文件:

module.c
t_stream.c
redismodule.h

如果使用 Redis Stack,还可以阅读对应模块仓库:

模块 能力
RedisJSON JSON 文档和路径操作
RediSearch 索引与查询
RedisTimeSeries 时间序列
RedisBloom 概率结构

模块学习重点:

  1. 模块如何注册命令;
  2. 模块如何访问 key 和类型;
  3. 模块阻塞命令如何实现;
  4. 模块内存如何统计;
  5. 模块升级与兼容性。

29.5 高效阅读源码的方法

29.5.1 带问题读

不要从第一行顺序读到最后一行。每轮只回答一个问题:

第一轮:SET 命令如何执行?
第二轮:Hash 什么时候从 listpack 转 hashtable?
第三轮:expire key 后内存什么时候释放?
第四轮:BGSAVE 期间新写入为什么不进 RDB?
第五轮:从节点断线后如何判断能否部分重同步?

29.5.2 命令对照

redis-cli 执行命令,同时用 gdb 或日志观察内部函数。命令行为是外部契约,源码是内部实现,双向对照最不容易迷路。

示例:

HSET user:1 name Tom age 20
OBJECT ENCODING user:1
DEBUG OBJECT user:1
MEMORY USAGE user:1

先猜结果,再看源码验证。

29.5.3 画时序图

每读一个模块,输出一张图。例如 AOF 重写:

sequenceDiagram
    participant C as Client
    participant M as 主进程
    participant B as 子进程
    participant FS as 文件系统
    M->>B: fork
    B->>FS: 写 base AOF
    C->>M: 新写命令
    M->>FS: 写原 AOF / incr
    M->>FS: 写重写缓冲
    B-->>M: 重写完成
    M->>FS: 追加增量并原子改名

能画出来,才算真正理解主干。

29.5.4 做实验记录

建议维护一个实验日志:

日期 版本 实验 结果 源码解释
2026-08-25 7.4 小 Hash 编码 listpack 阈值未超过
2026-08-25 7.4 100 万元素 ZSet skiplist 超过阈值转换

长期积累后,这些记录会变成你的个人知识库。

29.6 版本演进重点

Redis 3.x

Redis 4.x

Redis 5.x

Redis 6.x

Redis 7.x

Redis 8.x 与许可证变化

Redis 8 将部分 Redis Stack 能力并入主版本,Search、JSON 等能力更易使用;同时 Redis 许可证策略在社区引发分支和厂商方案变化。生产选型时要确认:

  1. 具体小版本许可证;
  2. 云厂商支持方式;
  3. 是否需要商业模块;
  4. 是否考虑 Valkey 等兼容实现;
  5. 长期升级路径。

不要只记“Redis 开源”或“Redis 不开源”这种粗粒度结论,版本和发行版才是关键。

29.7 社区与资源

官方资源:

建议持续关注:

  1. release notes,尤其升级前的破坏性变化;
  2. 官方配置说明;
  3. INFO 指标含义;
  4. 性能延迟案例;
  5. 安全公告;
  6. Valkey 等兼容项目的发展。

阅读资料建议:

类型 建议
命令手册 每天精读几个命令,注意时间复杂度和边界
官方文档 优先级高于二手博客
源码注释 很多设计原因写在注释里
release notes 理解行为变化
故障复盘 学习真实生产边界

29.8 L1 到 L5 成长地图

L1 入门使用者

能力:

进阶建议:

L2 业务开发者

能力:

进阶建议:

L3 生产工程师

能力:

进阶建议:

L4 架构师

能力:

进阶建议:

L5 专家

能力:

进阶建议:

29.9 30 天进阶计划

第 1 周:命令与数据结构
  Day 01-02  五大类型命令复习
  Day 03-04  Bitmap/HLL/GEO/Stream
  Day 05-06  编码与内存实验
  Day 07     输出 key 设计规范

第 2 周:原理与源码
  Day 08-09  server.c/networking.c 命令链路
  Day 10-11  sds/dict/skiplist/listpack
  Day 12     expire.c/evict.c
  Day 13-14  输出对象编码与内存实验报告

第 3 周:持久化与高可用
  Day 15-16  RDB/AOF 实验
  Day 17-18  主从复制实验
  Day 19-20  Sentinel 实验
  Day 21     Cluster 建立与检查

第 4 周:生产与架构
  Day 22-23  性能排查与监控
  Day 24-25  缓存架构设计
  Day 26-27  秒杀或 Feed 项目复盘
  Day 28-29  故障演练
  Day 30     输出个人 Redis 手册

执行建议:每天至少一个实验、一条笔记。学习 Redis 最怕只看文章不动手。

29.10 大师级工作习惯

  1. 先量化,再优化:没有 P99 延迟和 QPS,不做结论;
  2. 先边界,再方案:数据可不可丢、一致性要求多高;
  3. 先兜底,再加速:锁和队列必须有幂等;
  4. 先监控,再上线:不能观测的缓存等于隐形风险;
  5. 先演练,再相信:备份、高可用和降级都要真实执行;
  6. 先读文档,再读源码:外部行为先明确;
  7. 先复现,再修改:不要根据猜测改配置;
  8. 先小流量,再全量:任何配置变更都可能改变延迟曲线。

29.11 本章小结

29.12 思考题

  1. 如果只能读 10 个 Redis 源码文件,你会选择哪 10 个?为什么?
  2. 如何设计一个实验证明 listpack 到标准结构的转换阈值?
  3. AOF 重写期间发生宕机,如何分析哪些数据会保留?
  4. Redis 6 IO 多线程为什么没有让命令执行并行化?
  5. 你如何评估 Redis、Valkey 和云托管方案未来三年的演进风险?