Redis
Redis 是一个把高频数据访问压缩到内存、把命令执行保持在事件循环单线程中的数据服务器。源码的关键不只是“字典加 epoll”,而是如何把对象编码、后台 fork、复制 offset、惰性过期和渐进式维护组织成一个可持续运行的状态机。
本册问题地图
Section titled “本册问题地图”Redis 的源码阅读主线不是把数据结构逐个背下来,而是追踪“一条命令如何穿过事件循环、对象表示、持久化和复制”。本册重点回答:
- 命令为什么能在单线程中保持高吞吐? 事件循环如何串行执行命令,又把哪些工作交给后台线程或子进程。
- 同一种逻辑数据为什么有多种编码? SDS、listpack、quicklist、跳表和 dict 分别在什么规模与操作下切换。
- 渐进式 rehash 解决了什么停顿? 哈希表扩容如何分摊迁移成本,迁移期间读写访问哪张表。
- 数据如何在重启后恢复,又如何复制给副本? RDB、AOF、PSYNC 分别提供什么时间点和故障语义。
- 过期和淘汰有什么区别? 一个是键的逻辑生命周期,一个是内存压力下的容量策略。
读完整册后,应当能把“单线程”解释成命令执行边界,而不是误解成 Redis 内部所有工作都只有一个线程。
| 项 | 值 |
|---|---|
| 仓库 | redis/redis |
| 本地路径 | E:\source\redis\redis |
| 分支 | unstable |
| Commit | cbdad795d8d75746e501aae06f14a3398bd190a2(2026-08-13) |
| 最近 tag | 8.10-m04-int |
本册所有源码坐标均基于上述 commit。当前快照使用
kvstore/kvobj命名、multi-part AOF manifest 和带rax索引的 replication backlog;旧版文章中的字段名可能已经过时。
| 模块 | 职责 | 关键源码 |
|---|---|---|
src/ae.* |
文件事件、时间事件、主循环 | ae.c、ae_epoll.c |
src/networking.c |
连接读写、协议解析、输入缓冲 | readQueryFromClient、processInputBuffer |
src/server.c |
启动、命令分派、cron、beforeSleep | main、initServer、processCommand |
src/object.*、src/kvstore.* |
对象头、编码和 DB 存储 | createObject、tryObjectEncodingEx |
src/dict.* |
哈希表、双表 rehash | dictRehash |
src/rdb.c、src/aof.c |
快照、AOF 重写与加载 | rdbSaveRio、rewriteAppendOnlyFile |
src/replication.c |
backlog、PSYNC、主从状态 | replicationFeedSlaves、syncCommand |
src/expire.c、src/evict.c |
过期和淘汰 | activeExpireCycle、performEvictions |
读码入口顺序
Section titled “读码入口顺序”server.c:8065的main→initServer→aeMain。ae.c:497的aeMain和server.c:1956的beforeSleep。networking.c:3982→processInputBuffer→server.c:4477。object.c:107、object.c:939、dict.c:406。rdb.c、aof.c、replication.c。expire.c、evict.c。
| 对象 | 对照点 |
|---|---|
Netty EventLoop |
都把 IO 和任务纳入事件循环;Redis 的命令执行边界更集中 |
| Disruptor | 都用顺序位置和批处理摊薄协调成本;Redis 更强调单线程所有权 |
| Valkey | 对照 Redis fork 后的线程化 IO 和后台任务边界 |
| hiredis | 从客户端 reader 对照 Redis 的 RESP 输入输出路径 |