# 《Redis设计与实现》知识点全解 > AI生成|2026年10月29日|codebuddy|原书:黄健宏《Redis设计与实现》,机械工业出版社,2015-01-01 > **我的阅读**:进度 100%|累计 5小时13分钟|开始 2017-09-16|最近 2020-07-19|读完 2020-07-06 **一句话总结**:把 Redis 从"会敲命令"拉到"知道每条命令背后动的是哪块内存、哪份文件、哪个循环",用数据结构和执行流程解释它的性能边界与可靠性取舍。 **适合谁 / 前置知识**:适合已能日常使用 Redis、想读懂其设计或要排查性能与持久化问题的后端、运维工程师。需要 C 语言基础(指针、结构体、内存布局)、基本数据结构概念(哈希表、链表、跳表)以及 Linux 的 fork、文件 I/O 常识。不适合零基础入门——它讲实现不讲命令速查;也不适合只想找分布式实战案例的读者,多机部分偏原理而非部署手册。 ## 一、全书地图 全书按"零件 → 组装 → 单机 → 多机 → 独立模块"逐层搭建:先造六种底层结构,再用对象系统把它们包装成五种对外类型,然后放进单机数据库并解决过期与落盘,最后扩展到多机与旁支功能。 | 层次 | 章节 | 内容 | 逻辑位置 | |---|---|---|---| | 零件 | 第2–7章 | SDS、链表、字典、跳跃表、整数集合、压缩列表 | 六种底层结构,各自解决一类内存或性能问题 | | 组装 | 第8章 | 对象(redisObject) | 把底层结构包装成字符串、列表、哈希、集合、有序集合,并决定编码 | | 单机 | 第9–11章 | 数据库与过期键、RDB、AOF;事件(该章章号目录未列,待核对) | 数据放哪、怎么过期、怎么落盘、被谁驱动 | | 多机 | 章节号待核对 | 复制、Sentinel、集群 | 从一主一从,到自动故障转移与分片 | | 独立模块 | 章节号待核对 | 发布订阅、事务、Lua、排序、位数组、慢查询、监视器 | 不依赖前述机制、可单独理解的功能 | 主线是第2章→第8章,第9–11章是运行时底座,多机与独立模块可按需跳读。 ## 二、核心知识点 **1. SDS 简单动态字符串** 是什么:Redis 自建字符串,含 len(已用长度)、free(剩余空间)、buf[],靠长度而非 `\0` 判断结尾。为什么重要:O(1) 取长度、二进制安全、不会缓冲区溢出,并通过预分配减少 realloc。怎么用:追加时 free 足够就直接写;扩容小于 1MB 时翻倍,不小于 1MB 时多给 1MB;缩短时不立即归还。误区:把它当成"C 字符串加个长度";忽略惰性释放会让内存看起来不下降。 **2. 链表与字典** 链表是双向无环、带 head/tail/len、节点用 `void*` 存值并可注册 dup/free/match 的通用结构,列表键、发布订阅、客户端输出缓冲、慢查询都复用它。字典是双哈希表 `ht[0]/ht[1]` 加 `rehashidx`,扩容时不一次搬完,而是每次增删改查顺带搬一个桶(渐进式 rehash),避免单线程被大字典卡住。怎么用:rehash 期间查找先查 ht[0] 再查 ht[1],新增只写 ht[1];有子进程做 BGSAVE 或 AOF 重写时会提高扩容阈值,借写时复制省内存。误区:以为 rehash 有后台线程,其实由主线程分摊。 **3. 跳跃表** 是什么:有序链表加多层索引,节点层高 1–32、以约 1/4 概率上升,平均 O(log N);节点带 span 可算排名,带 backward 可逆序遍历。为什么重要:实现比平衡树简单,范围查询天然有序。怎么用:有序集合在元素多或成员长时用它,集群内部也有类似结构。误区:只记"跳表很快",忽略选它的真实理由是简单加范围操作友好。 **4. 整数集合与升级** 是什么:有序、无重复、连续存放的整数数组,encoding 为 int16/int32/int64。为什么重要:比哈希表省大量内存,还能自动适配取值范围。怎么用:插入更大整数时整体升级编码并保持有序,一次 O(N) 搬运;不支持降级。误区:以为删掉大数就会退回小编码。 **5. 压缩列表与连锁更新** 是什么:连续内存块,头部有 zlbytes、zltail、zllen,尾部 zlend,每个 entry 用 prevlen 记录前一项长度(小于 254 用 1 字节,否则 5 字节)。为什么重要:小数据量下极省内存,是列表、哈希、有序集合的紧凑编码。怎么用:某项变长使后项 prevlen 由 1 字节扩为 5 字节,可能逐项传染,即连锁更新,最坏 O(N²)。误区:以为压缩列表操作永远 O(1);忽略超过阈值会整体转成常规结构。 **6. 对象系统:类型与编码** 是什么:redisObject = type + encoding + ptr + refcount + lru。type 是对外五种类型,encoding 是底层实现。为什么重要:同一命令因编码不同走不同实现,即命令多态;类型检查也在这一层完成。怎么用:用 `OBJECT ENCODING` 观察真实编码,而不是凭命令猜。误区:把 type 与 encoding 混为一谈——"列表"和"压缩列表"不在同一维度。 **7. 编码转换阈值** 字符串:能表示为整数用 int,短字符串用 embstr(一次分配、只读),更长用 raw。列表:元素少且短用 ziplist,否则 linkedlist。哈希与有序集合:条目和值都短时用 ziplist(哈希阈值 512/64,有序集合 128/64),否则转 dict 或 skiplist。集合:全整数且数量不超过 512 用 intset,否则 hashtable。误区:以为转换可逆——多数只升不降,删元素也不会退回。 **8. 引用计数、共享与空转时长** refcount 归零即回收;0–9999 的整数对象在启动时共享;lru 记录最近访问时间,`maxmemory-policy` 的 LRU 类策略据此淘汰。为什么重要:前者省内存,后者是内存上限的最后手段。误区:以为共享对所有值生效——只对整数常量,且在启用 LRU 淘汰时共享会受限,因为 lru 字段无法按对象独立更新。 **9. 过期键删除策略** 是什么:expires 字典存过期时间,访问时惰性检查删除,同时周期性随机抽查清理。为什么重要:纯惰性会漏删,纯定期会耗时,两者结合才平衡。怎么用:RDB 载入时主服务器丢弃过期键、从服务器保留等待主节点同步 DEL;AOF 重写会过滤已过期键。误区:以为到期就立刻释放内存——不被访问就可能长期占着。 **10. RDB 持久化** 是什么:SAVE 阻塞主进程写盘;BGSAVE fork 子进程写,父进程继续服务,靠写时复制保证快照一致。文件含 REDIS 魔数、版本、数据库键值对、EOF 与校验和。为什么重要:全量快照,恢复快,适合冷备。怎么用:接受"两次保存之间的数据可能丢",并用 fork 峰值内存做容量评估。误区:以为 fork 后内存立刻翻倍——实际只复制被改写过的页。 **11. AOF 持久化与重写** 是什么:命令先写入 aof_buf,再按 appendfsync 的 always/everysec/no 落盘;载入时用伪客户端重放。重写不读旧文件,而是遍历当前数据库生成最小命令集,重写期间新命令追加到重写缓冲区以免丢失。为什么重要:丢失窗口可控,文件可读。怎么用:按业务容忍度选 everysec(最多丢约 1 秒)。误区:以为 everysec 不丢数据;以为重写只是"压缩文本"。 **12. 事件模型** 是什么:单线程 Reactor,I/O 多路复用(select/epoll/evport/kqueue)产生文件事件,时间事件(serverCron)处理定期任务,两者在同一循环按到期时间调度。为什么重要:它同时解释了 Redis 为什么快(无锁、无频繁上下文切换)和为什么会卡(一条慢命令阻塞所有客户端)。误区:以为命令是多线程并行执行的(本书所述版本为单线程)。 **13. 事务、Lua 与发布订阅** 是什么:MULTI 入队、EXEC 执行、WATCH 做乐观锁;Lua 脚本在服务端原子执行;发布订阅按频道或模式即时转发。为什么重要:它们把多条命令合成不可插入的执行单元,或提供轻量消息通道。怎么用:入队期语法错误放弃整个事务,运行期错误只跳过该条、其余继续,没有回滚;pub/sub 不落盘、不重投,订阅者离线即丢。误区:把事务当关系库事务(无隔离级别、无回滚),把 pub/sub 当可靠队列。慢查询日志按阈值记录耗时,MONITOR 实时回放全部命令但有开销。 **14. 复制** 是什么:从节点执行 PSYNC,带复制偏移量与复制积压缓冲区,短时断连可部分重同步,否则全量同步。为什么重要:复制是 Sentinel 与集群的地基,且是异步的。误区:以为主从强一致——主节点写完即返回,故障切换可能丢最后一段写。 **15. Sentinel 与集群** 是什么:Sentinel 负责监控、主观与客观下线判定、选举领头 Sentinel 执行故障转移并通知客户端新主;集群用 16384 个槽分片,节点间用 Gossip 传播状态。为什么重要:前者解决高可用,后者解决容量与吞吐,目标不同。怎么用:客户端收到 MOVED 改走目标节点,收到 ASK 只对本次迁移中的键做临时转向。误区:拿 Sentinel 当分片方案;以为集群能强一致。 ## 三、关键概念速查表 | 概念 | 一句话解释 | 用在哪儿 | |---|---|---| | SDS | 带 len/free 的二进制安全字符串 | 所有字符串值、键、缓冲区 | | 渐进式 rehash | 把一次大搬迁拆到每次操作里 | 字典扩容与收缩 | | 跳跃表 | 多层索引的有序链表,带 span 排名 | 有序集合、集群内部结构 | | intset 升级 | 插入更大整数时整体提升编码 | 小整数集合 | | 连锁更新 | prevlen 扩字节引发逐项连锁 | 压缩列表 | | redisObject | type + encoding + ptr + refcount + lru | 所有对外对象的统一外壳 | | embstr | 短字符串的一次性分配只读编码 | 短字符串键值 | | 惰性删除 | 访问时才检查并删除过期键 | 过期键清理 | | 定期删除 | 周期性随机抽查过期键 | 过期键清理 | | 写时复制 | fork 后仅复制被修改的内存页 | BGSAVE、AOF 重写 | | appendfsync | always/everysec/no 三种落盘策略 | AOF 可靠性取舍 | | AOF 重写缓冲 | 重写期间承接新命令,防止丢写 | AOF 重写 | | PSYNC | 带偏移量的同步,支持部分重同步 | 主从复制 | | 16384 槽 | 集群的分片单位 | 集群扩缩容与寻址 | | MOVED / ASK | 永久转向 / 迁移中临时转向 | 集群客户端重定向 | | WATCH | 乐观锁,监控键是否被改动 | 事务 | ## 四、可复用的方法/框架 1. **三层追问法**:面对一个 Redis 行为,先问"对外是什么类型",再问"当前是什么编码",最后问"什么条件触发切换"。这三问能解释绝大多数性能突变。 2. **用结构解释复杂度**:任何命令都落到 O(1)、O(log N) 或 O(N)。遇到慢查询,先定位它操作的结构与规模,而不是先怀疑网络。 3. **空间与时间的交换清单**:预分配与惰性释放、整数共享、紧凑编码,都是用实现复杂度换内存;反过来,升级与转换为可预测性牺牲内存。 4. **单线程阻塞点排查顺序**:大 key 删除 → 字典 rehash → 过期键集中到期 → SAVE/fork → 慢命令。按此顺序排查,比盲看监控快。 5. **可靠性取舍表**:RDB 是"时点快照、可能丢一段",AOF 是"追加日志、可配置丢约 1 秒",复制是"异步、切换可能丢写"。任何持久化方案先写清丢失窗口,再谈性能。 6. **读源码的路径**:命令入口 → 类型检查 → 编码分支 → 底层结构函数。带着这条链读,不会陷进细节。 ## 五、我的阅读轨迹 | 指标 | 数据 | |---|---| | 进度 | 100% | | 累计阅读 | 5小时13分钟 | | 开始阅读 | 2017-09-16 | | 最近阅读 | 2020-07-19 | | 读完时间 | 2020-07-06 | 五小时多通读一遍,属于"建立了地图"而非"形成了肌肉记忆";最近阅读晚于读完时间,说明读后仍有回看。 复习建议:优先复看第8章(对象与编码,最易忘且直接决定选型)、第9章(过期键在三类场景下的不同处理)、第10–11章(RDB 与 AOF 的丢失窗口对比)。多机部分只需守住三条主线:PSYNC 的部分重同步条件、Sentinel 的客观下线与选举、集群的槽迁移与 MOVED/ASK 区别。验收标准设为"合上书能画出五种类型的编码转换图",画不出就回到第8章。 ## 六、行动清单 - 画出 redisObject 结构与五种类型的编码转换阈值表,贴在笔记首页。 - 用一个 key 逐步增加元素,配合 `OBJECT ENCODING` 记录每次编码跳变的临界点。 - 手写一遍渐进式 rehash 流程,标明期间查找、新增、删除分别落在哪张表。 - 为现有业务写一页 RDB 与 AOF 的选型结论,明确可接受的丢失窗口。 - 复述 AOF 重写为什么需要子进程加重写缓冲区,写成 200 字。 - 用 SLOWLOG 找一个慢命令,归因到具体数据结构与复杂度。 - 画出 Sentinel 故障转移时序:主观下线 → 客观下线 → 选举 → 提升 → 通知。 - 用自己的话解释 16384 个槽与 MOVED/ASK 的差异,写 200 字。 ## 七、延伸阅读 1. **《Redis实战》**——先读或并行读。它补本书最缺的部分:命令用法与业务场景直觉,读完再回看本书的数据结构,能对上"这个设计为哪种访问模式服务"。 2. **《Redis开发与运维》**——并行读。覆盖部署、监控、排障与集群实践,把本书的原理落到线上问题;建议在读完第9–11章后开始。 3. **《数据密集型应用系统设计》**——最后读。把持久化、复制、分区放回分布式系统的通用框架,帮助你判断 Redis 的取舍在哪些场景不成立。