这是《Redis 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 会使用 Redis、会排查故障、会设计缓存架构,已经能胜任大多数业务场景。但要成为真正的 Redis 专家,还需要能阅读源码、理解版本演进、跟踪社区讨论,并把 Redis 的设计思想迁移到其他存储系统中。
本章给出一套可执行的源码阅读路线、调试方法、版本演进重点和 L1 到 L5 成长地图。
29.1 为什么值得读 Redis 源码
Redis 源码有四个特点:
- 代码量相对小,核心模块清晰;
- C 语言实现直接,能看清数据结构和内存布局;
- 单线程事件模型简洁,适合学习网络编程;
- 存储组件中的经典问题都能看到工程答案。
读完源码后,你会更容易回答这些问题:
- SDS 为什么要这样设计;
- 对象编码什么时候转换;
- 过期采样如何权衡 CPU 与内存;
- RDB fork 和写时复制如何影响延迟;
- AOF 重写如何保证增量不丢;
- 复制 backlog 为什么能部分重同步;
- Cluster 如何通过 Gossip 维护拓扑;
- 命令表如何统一实现命令分发。
更重要的是,你会开始理解“简单模型 + 极致工程优化”的设计哲学。
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
查找命令表 -> 校验参数和权限
调用命令实现函数
添加输出缓冲区 -> 写回客户端
重点问题:
- 命令表在哪里定义;
- 命令的 arity、flags 表示什么;
- lookupKey 读写都有哪些副作用;
- client 的输入输出缓冲区如何管理;
- 为什么命令执行天然串行。
练习:给一个普通命令加上自定义日志,观察执行链路。不要修改生产版本源码,只在本地分支实验。
阶段二:核心数据结构
目标文件:
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 |
插入、删除、范围查询 |
重点问题:
- 对象编码阈值由哪些配置控制;
- 渐进 rehash 期间读写如何处理;
- ZSet 为什么同时保留 dict 和 skiplist;
- listpack 如何解决 ziplist 的级联更新问题;
- 内存预估为什么要看编码而不是只看元素数量。
阶段三:过期与淘汰
目标文件:
expire.c
evict.c
db.c
lazyfree.c
object.c
阅读重点:
- TTL 如何写入过期字典;
- 惰性删除和定期删除的触发条件;
- 定期删除的采样数量和时间上限;
- LRU/LFU 时钟字段如何更新;
- 同步删除和异步删除的选择;
- maxmemory 达到后的写入路径。
练习:
CONFIG SET maxmemory 100mb
CONFIG SET maxmemory-policy allkeys-lru
DEBUG SLEEP 0
INFO memory
INFO stats
观察 evicted_keys、expired_keys 和内存变化,再对应源码阅读。
阶段四:事件循环与网络
目标文件:
ae.c
ae_epoll.c
ae_kqueue.c
networking.c
io_threads.c
阅读重点:
aeMain如何分发时间事件和文件事件;- epoll/kqueue 如何抽象;
- beforeSleep 做哪些事情;
- IO 线程何时启用;
- 输出缓冲区限制如何断开客户端。
重点认知:Redis 的单线程不是没有并发,而是把并发点放在网络 IO、后台任务和异步释放内存上,命令执行保持串行以简化一致性。
阶段五:持久化
目标文件:
rdb.c
aof.c
childinfo.c
bio.c
阅读重点:
bgsave如何 fork 子进程;- 写时复制对内存的影响;
- RDB 格式和加载流程;
- AOF 写入缓冲和刷盘策略;
- AOF 重写期间增量如何追加;
- Redis 7 multipart AOF 的文件组织;
- 混合持久化如何组织 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
阅读重点:
PSYNC参数与返回值;- replid、replid2 和 offset;
- replication backlog 的写入与截断;
- 全量同步 RDB 与增量缓冲如何交接;
- 主从心跳和偏移量上报;
- 哨兵主观下线、客观下线和 leader 选举;
- 故障转移步骤和复制拓扑调整。
实验:
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
不同版本文件拆分差异较大,阅读时以当前分支为准。
重点问题:
- 节点 ID、槽位和 epoch 的作用;
- Gossip 消息类型;
- PFAIL 和 FAIL 的判定;
- MOVED、ASK 如何产生;
- reshard 时 migrate 的 key 流程;
- replica 迁移和故障转移;
- 为什么多 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 | 概率结构 |
模块学习重点:
- 模块如何注册命令;
- 模块如何访问 key 和类型;
- 模块阻塞命令如何实现;
- 模块内存如何统计;
- 模块升级与兼容性。
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 Cluster 正式可用;
- Sentinel 逐渐成熟;
- 常用数据结构稳定。
Redis 4.x
- 混合持久化;
- Module 系统;
- PSYNC2 改进故障后部分重同步;
- 异步删除相关能力增强。
Redis 5.x
- Stream 数据类型;
- 改进 Cluster 管理;
- Redis Module 生态扩展。
Redis 6.x
- ACL 权限体系;
- 客户端缓存 Tracking;
- IO 多线程;
- RESP3;
- SSL/TLS 支持;
- 过期淘汰算法和配置进一步优化。
Redis 7.x
- Functions;
- Sharded Pub/Sub;
- multipart AOF;
- listpack 使用范围扩大;
- Cluster 管理和数据结构持续优化;
- ACL、命令参数和安全能力增强。
Redis 8.x 与许可证变化
Redis 8 将部分 Redis Stack 能力并入主版本,Search、JSON 等能力更易使用;同时 Redis 许可证策略在社区引发分支和厂商方案变化。生产选型时要确认:
- 具体小版本许可证;
- 云厂商支持方式;
- 是否需要商业模块;
- 是否考虑 Valkey 等兼容实现;
- 长期升级路径。
不要只记“Redis 开源”或“Redis 不开源”这种粗粒度结论,版本和发行版才是关键。
29.7 社区与资源
官方资源:
- Redis 官方文档:
https://redis.io/docs/latest/ - Redis GitHub:
https://github.com/redis/redis - Redis 发布说明:
https://github.com/redis/redis/releases - Redis 问题和讨论:
https://github.com/redis/redis/issues
建议持续关注:
- release notes,尤其升级前的破坏性变化;
- 官方配置说明;
INFO指标含义;- 性能延迟案例;
- 安全公告;
- Valkey 等兼容项目的发展。
阅读资料建议:
| 类型 | 建议 |
|---|---|
| 命令手册 | 每天精读几个命令,注意时间复杂度和边界 |
| 官方文档 | 优先级高于二手博客 |
| 源码注释 | 很多设计原因写在注释里 |
| release notes | 理解行为变化 |
| 故障复盘 | 学习真实生产边界 |
29.8 L1 到 L5 成长地图
L1 入门使用者
能力:
- 安装和连接 Redis;
- 掌握五大基础类型;
- 能使用 RedisTemplate 或客户端 SDK;
- 理解 TTL 和简单缓存。
进阶建议:
- 每周整理 20 个命令;
- 写一个增删改查 Demo;
- 观察每个结构的
OBJECT ENCODING; - 用
MEMORY USAGE对比设计。
L2 业务开发者
能力:
- 能设计 key 和 TTL;
- 能实现缓存、计数器、排行榜、签到;
- 能使用 pipeline、事务和 Lua;
- 理解序列化和连接池。
进阶建议:
- 给业务输出 key 清单;
- 做缓存命中率埋点;
- 学习大 key 治理;
- 为每个写操作设计幂等。
L3 生产工程师
能力:
- 能部署哨兵和 Cluster;
- 理解 RDB、AOF、复制和故障切换;
- 能处理慢查询、大 key、热点 key;
- 能做备份恢复和容量规划。
进阶建议:
- 定期故障演练;
- 建立监控告警;
- 输出 Runbook;
- 压测并记录性能上界。
L4 架构师
能力:
- 能设计多级缓存和一致性方案;
- 能拆分业务边界和数据生命周期;
- 能组合 Redis、MySQL、Kafka、ES 和数仓;
- 能评审容量、成本、风险和降级预案。
进阶建议:
- 写架构决策记录;
- 建设统一 Cache SDK;
- 制定平台规范和配额;
- 每季度复盘故障与成本。
L5 专家
能力:
- 能阅读和修改源码;
- 能定位底层性能问题;
- 能参与社区讨论或提交 issue;
- 能设计存储引擎级别优化;
- 能指导团队建立技术体系。
进阶建议:
- 完成源码阅读路线;
- 复现官方 issue;
- 研究 Valkey、KeyDB 等实现差异;
- 输出源码解析和性能报告;
- 关注操作系统、网络和存储底层。
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 大师级工作习惯
- 先量化,再优化:没有 P99 延迟和 QPS,不做结论;
- 先边界,再方案:数据可不可丢、一致性要求多高;
- 先兜底,再加速:锁和队列必须有幂等;
- 先监控,再上线:不能观测的缓存等于隐形风险;
- 先演练,再相信:备份、高可用和降级都要真实执行;
- 先读文档,再读源码:外部行为先明确;
- 先复现,再修改:不要根据猜测改配置;
- 先小流量,再全量:任何配置变更都可能改变延迟曲线。
29.11 本章小结
- 源码阅读应从命令执行链路开始,再进入数据结构、过期淘汰、事件循环、持久化、复制和集群;
- Redis 源码的关键价值是看清工程权衡,而不是背函数名;
- 版本演进要关注 ACL、IO 多线程、AOF、Cluster、模块和许可证变化;
- 成长路线从会命令、会业务、会生产,逐步到会架构和会源码;
- 大师能力的核心是量化、验证、兜底和持续复盘。
29.12 思考题
- 如果只能读 10 个 Redis 源码文件,你会选择哪 10 个?为什么?
- 如何设计一个实验证明 listpack 到标准结构的转换阈值?
- AOF 重写期间发生宕机,如何分析哪些数据会保留?
- Redis 6 IO 多线程为什么没有让命令执行并行化?
- 你如何评估 Redis、Valkey 和云托管方案未来三年的演进风险?