跳转到内容

Redis

Redis 是一个把高频数据访问压缩到内存、把命令执行保持在事件循环单线程中的数据服务器。源码的关键不只是“字典加 epoll”,而是如何把对象编码、后台 fork、复制 offset、惰性过期和渐进式维护组织成一个可持续运行的状态机。

Redis 的源码阅读主线不是把数据结构逐个背下来,而是追踪“一条命令如何穿过事件循环、对象表示、持久化和复制”。本册重点回答:

  1. 命令为什么能在单线程中保持高吞吐? 事件循环如何串行执行命令,又把哪些工作交给后台线程或子进程。
  2. 同一种逻辑数据为什么有多种编码? SDS、listpack、quicklist、跳表和 dict 分别在什么规模与操作下切换。
  3. 渐进式 rehash 解决了什么停顿? 哈希表扩容如何分摊迁移成本,迁移期间读写访问哪张表。
  4. 数据如何在重启后恢复,又如何复制给副本? RDB、AOF、PSYNC 分别提供什么时间点和故障语义。
  5. 过期和淘汰有什么区别? 一个是键的逻辑生命周期,一个是内存压力下的容量策略。

读完整册后,应当能把“单线程”解释成命令执行边界,而不是误解成 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
  1. server.c:8065 的 main → initServer → aeMain。
  2. ae.c:497 的 aeMain 和 server.c:1956 的 beforeSleep。
  3. networking.c:3982 → processInputBuffer → server.c:4477。
  4. object.c:107、object.c:939、dict.c:406。
  5. rdb.c、aof.c、replication.c。
  6. expire.c、evict.c。
对象 对照点
Netty EventLoop 都把 IO 和任务纳入事件循环;Redis 的命令执行边界更集中
Disruptor 都用顺序位置和批处理摊薄协调成本;Redis 更强调单线程所有权
Valkey 对照 Redis fork 后的线程化 IO 和后台任务边界
hiredis 从客户端 reader 对照 Redis 的 RESP 输入输出路径