分布式系统百科 ·
etcd 深度解剖 已从 Watch/MVCC/Lease 讲过协调层全景;Raft
深度重写 与 Raft
实现拆解 讲过 go.etcd.io/raft/v3
工程补丁;分布式
KV 对比 把 etcd 放在 TiKV、FoundationDB
选型海报里。中间仍缺一层:一次 Put/Txn 卡在 Raft
propose 还是 MVCC apply、Raft lag 与 apply lag
如何分列、线性一致读与 follower 读各走哪条路径、Watch 从旧
revision 追赶为何触发 ErrCompacted、Lease
KeepAlive 不经 Raft 时 Leader 切换如何影响 TTL,以及 K8s
apiserver 耦合下 resourceVersion 与 Node Lease
落在哪一层。
本文只做三件事:钉缺口、定义后续 15
篇共用的五条坐标系,并给出阅读路线。难点不在「会不会
etcdctl put」,而在共识层与存储层交界上的可核对字段——Raft
index 是否追上、committed 与 applied 是否分叉、MVCC revision
是否被 compact、Watch 客户端 rev 是否落后、Lease TTL 是否在
Leader 切换后漂移。
本篇在系列中的位置
篇目 核心内容 第 1 篇 · 生产全景 缺口、五轴坐标系、16 篇路线 第 2 篇 · 单 Raft 组与服务器角色 Leader/Follower/Learner;request 路由 系列目录 关键问题、阅读路径、全部价值点
版本锚定:etcd v3.5.33(源码 tag
v3.5.33,2026-07-23 补丁线)。机制叙述对齐该 tag 下go.etcd.io/etcd/server/v3与内嵌go.etcd.io/etcd/raft/v3;官方文档以 etcd.io/docs/v3.5/ 路径为准。etcd.io/docs/latest是滚动文档,不以/latest冒充本版本。无真实 etcd 集群则不粘贴伪造etcdctl endpoint status/etcdctl check perf输出。 Kubernetes 控制面常见嵌入 3.5.x——正文机制钉 3.5.33;3.6/3.7 差异只在第 14 篇升级门列出。
一、相对站内已有内容,缺口在哪
下表列出各已有系列的视角,以及本系列补的格子。规则是:不重写已有内容,只填真正空着的格子。
| 已有内容 | 视角 | 本系列补什么 |
|---|---|---|
| distributed/13 | Raft 论文 → etcd/raft 工程补丁 |
不重写;只钉 etcdserver 如何嵌入
raft.Node |
| distributed/raft-etcd | PreVote、流水线、ReadIndex 代码走读 | 收进 02–03、08;成系列生产路径 |
| distributed/50 | Watch/MVCC/Lease/K8s 百科单篇 | 成 16 篇可排障精度;五轴与失败落格 |
| distributed/39 | etcd/TiKV/FDB 横向选型 | 第 16 篇收束 etcd 叶;不重写 TiKV/FDB 全书 |
| distributed/53 | Jepsen etcd 3.4.3 | 第 11、16 篇引用;Lease 锁边界 |
| tikv-htap/ | Multi-Raft + Region | 第 16 篇对照;不重写 Percolator |
| foundationdb/ | Unbundled + OCC | 第 16 篇对照;不重写 Sequencer/Resolver |
结论:读者知道「K8s 靠 etcd Watch」,但不知道 proposal 卡在 Raft 还是 MVCC apply、follower 读为何可能 stale、Watch 从 historical rev 追赶为何 OOM、KeepAlive 不经 Raft 时 Leader 切换如何影响 TTL、defrag 与 compaction 能否同窗口、member replace 与 learner 升级的检查单差在哪;把 etcd 当通用数据库会在 quota 与 bbolt 单写者处撞墙。
与通识教程的差别也在这里:多数「在 K8s 上运维
etcd」文章停在 etcdctl endpoint health
与备份脚本;本系列追问 apiserver 超时之后「no leader
/ quota / ErrCompacted / Lease TTL
漂移」仍落在哪一层,以及 Raft lag 与 apply
lag
为何不能混成一个指标。深度来自可证伪的阶段划分,而不是「etcd
很轻量」口号。
本系列刻意不写 Raft 论文全文、apiserver storage 实现全书、TiKV/FDB 内核重讲、Kine 兼容层全书、未实测「etcd vs TiKV QPS%」排行榜。若目标只是「K8s 靠 etcd Watch 的直觉」,distributed/50 已够;若目标是「Raft lag vs apply lag、Compaction SLO、apiserver 超时是否 etcd 轴」,则必须沿五轴下钻。
本系列反复回指的版本门(不当本篇深挖)
| 门 | v3.5.33 事实 | 主篇 |
|---|---|---|
| v3 MVCC + Watch + Lease | 有 | 04–11 |
| ReadIndex 线性一致读 | 有 | 08 |
| Learner / non-voting member | 有 | 02、14 |
| Lease checkpoint | 有 | 10 |
| auto-compaction / quota / alarm | 有 | 12 |
| bbolt 后端 | 有 | 05–07 |
| v3.6/v3.7 独有变更 | 相对 3.5.33 正文不展开 | 14 升级门 |
| live docs 后版本能力 | 无 | 禁止写入正文当 v3.5.33 事实 |
二、五条坐标系(后续章节回指)
后面每一篇都落到下面某一条轴上。排障口诀:先点名轴,再下钻组件——不要一上来 defrag 或换盘。Raft lag 与 apply lag 在轴 1–2 分列证据包。
Li et al.(;login: 2017)把 etcd 描述为 Raft 共识 + 内存索引 + 持久化后端 的三层栈;v3.5.33 在此基础上增加了 Watchable MVCC、Lease lessor 与 alarm/quota 门。五轴是把这条管线按值班可核对的失败落格切开。
flowchart LR
client["gRPC client"] --> api["etcdserver API"]
api --> raft["Raft module"]
raft --> wal["WAL"]
raft -->|"commit"| apply["Apply pipeline"]
apply --> mvcc["MVCC store"]
mvcc --> bbolt["bbolt backend"]
mvcc --> watch["watchableStore"]
api --> lease["Lease lessor"]
lease --> mvcc
| 轴 | 可核对锚点 | 失败表象 | 主篇 |
|---|---|---|---|
| Raft 共识与成员 | Leader 是否存在;Raft index/term;learner 是否可投票;quorum | no leader、选举风暴、复制 lag、member
不可达 |
02、03、14、15 |
| WAL / 持久化与恢复 | WAL 段;snapshot;disk fsync;applied vs committed | 重启慢、corrupt WAL、snapshot 失败、NOSPC | 03、14、15 |
| MVCC / 存储与容量 | Revision;treeIndex;bbolt;quota;compaction;defrag | database space exceeded、读旧 rev、写
stall、defrag 窗口 |
04–07、12、15 |
| Watch / 客户端同步 | start
rev;synced/unsynced;victims;ErrCompacted |
Watch 断流、事件风暴、追赶 OOM、客户端 rev 落后 | 09、13、15 |
| Lease / TTL 与语义 | grant/KeepAlive/checkpoint;Txn 边界 | 锁提前释放、Node NotReady 漂移、TTL 雪崩 | 10–11、13、15 |
Raft 共识与成员轴
定义:整个集群是否由单个 Raft 组管理;Leader 是否存活;写请求是否经 Leader propose;Learner 是否参与投票。
etcd 与 TiKV 的分叉点在这里:etcd 是 Single Raft Group——所有 key 共享同一份日志;TiKV 是 Multi-Raft,按 Region 切分。单组带来运维简单与全局 Revision 全序,也带来写吞吐与磁盘容量的硬顶。
「etcdserver: no leader」或「member add
后长期 catch-up」优先落本轴。展开见 第 2 篇、第 3
篇。
WAL / 持久化与恢复轴
定义:Raft 条目如何经 WAL 段持久化;snapshot 何时截断日志;重启后 recovery 顺序;committed index 与 applied index 是否分叉。
Ongaro & Ousterhout(USENIX ATC 2014)的 Raft
论文定义了日志复制与快照压缩;etcd 的
server/wal 包把 HardState、Entry、Snapshot
记录成 CRC 保护的段文件(默认段大小 64MB,见
SegmentSizeBytes)。
「节点重启后长时间 not ready」或「WAL
corrupt / snapshot mismatch」优先落本轴。展开见 第 3
篇。
MVCC / 存储与容量轴
定义:Revision (main, sub)
如何分配;treeIndex/keyIndex 如何映射到 bbolt;quota 与
alarm 如何拦截写入;compaction/defrag 如何回收空间。
与 ZooKeeper 的一次性 Watch 不同,etcd v3 的 MVCC 保留历史版本直到 compaction——这是 Watch 从任意 revision 重放的前提,也是磁盘膨胀的来源。Hunt et al.(ATC 2010)的 ZooKeeper 论文提供了协调服务对照基线。
「mvcc: database space exceeded」或「读到的
mod revision 明显落后」优先落本轴。展开见 第 4 篇、第
05–07、12 篇。
Watch / 客户端同步轴
定义:watchableStore 如何把 apply
后的变更推给 synced/unsynced watcher;historical revision
追赶路径;channel 背压与 victims;ErrCompacted
何时强制全量重同步。
K8s apiserver 与 controller 的长生命周期 Watch 都依赖这一轴。客户端 rev 落后 + compaction 窗口配置不当,会把「看似 etcd 健康」变成「controller 大面积 resync」。
「Watch 断流但 Put 仍成功」或「unsynced watcher 堆积导致 OOM」优先落本轴。展开见第 09、13 篇。
Lease / TTL 与语义轴
定义:Lease grant/revoke/KeepAlive 哪些路径不经 Raft;checkpoint 如何把 TTL 持久化;与 Txn 分布式锁、Jepsen 结论如何共存。
K8s Node Lease(/leases/
前缀)把节点心跳写进 etcd;Leader 切换时 lessor 的时钟语义与
apiserver 的 Node NotReady
判定耦合——这是协调层特有的失败面,不是通用 KV
文档会展开的内容。
「锁提前释放」或「Node 抖动但 etcd 健康」优先落本轴。展开见第 10–11、13 篇。
三、坐标系背后的三个不变量
- 写路径必须经过 Raft,读路径可以分叉:Put/Txn/Compaction 等 mutating 操作走 propose → commit → apply;Serializable 读可绕过 ReadIndex,线性一致读走 ReadIndex 或 Lease read。代价是 follower 读与 Leader 读证据包不同。
- committed 不等于 applied:Raft 多数派
ACK 只意味着日志可提交;MVCC/bbolt 应用与 Watch 通知发生在
apply 阶段。代价是
committedIndex - appliedIndex可独立膨胀——不能把 Raft metrics 当成存储健康。 - Revision 全序与单 Raft 组绑定:全局
(main, sub)序是 Watch 与 K8s resourceVersion 的语义基础;换 Multi-Raft(TiKV)意味着另一套版本模型。代价是 etcd 水平扩展有硬顶,不能靠加节点线性提升写吞吐。
排障时先问「哪条不变量被触碰」,再落到对应轴。例如:Raft index 正常但 Watch 延迟高 → 不变量 2;客户端从 rev=1 Watch 全库 → 不变量 3 + Watch 轴。
四、设计谱系:Chubby → Raft → etcd v3 MVCC
Lamport Paxos(1998)→ Ongaro Raft(2014):可理解的复制日志
→ CoreOS etcd(2013–):K8s 协调层选型
→ Li et al. ;login: 2017:Raft + 内存索引 + BoltDB 栈
→ etcd v3:Revision MVCC + 持久 Watch + Lease API
→ 仍争:单 Raft 组 vs Multi-Raft 扩展性;bbolt 单写者 vs LSM 写放大
| 维度 | Raft 论文 / distributed/13 | etcd v3.5.33 生产路径 | 开放分叉 |
|---|---|---|---|
| 拓扑 | 抽象复制状态机 | 单 Raft 组 + Learner | Multi-Raft(TiKV) |
| 持久化 | 日志 + 快照 | WAL 段 + bbolt snapshot | 云盘 fsync 延迟 |
| 版本模型 | 日志 index | Revision (main, sub) + keyIndex |
ZK 多版本对照 |
| 协调语义 | 未定义 Watch | 持久 Watch + Lease | Kine 类兼容层语义差 |
| 对照 | distributed/13 | 本系列 | distributed/39 |
不在第 1 篇宣布胜负;选型留给 第 16 篇 排除树。
五、16 篇路线与阅读建议
flowchart TD
overview["01 Overview"] --> raft["02 Single Raft"]
raft --> wal["03 WAL Snapshot"]
wal --> mvcc["04 MVCC Model"]
mvcc --> bbolt["05 bbolt Backend"]
bbolt --> apply["06 Apply Pipeline"]
apply --> write["07 Write Path"]
write --> read["08 Read Consistency"]
read --> watch["09 Watch"]
watch --> lease["10 Lease"]
lease --> txn["11 Txn Semantics"]
txn --> cap["12 Compaction Quota"]
cap --> k8s["13 K8s Coupling"]
k8s --> ops["14 Ops Upgrade"]
ops --> trouble["15 Troubleshoot"]
trouble --> select["16 Selection"]
overview --> select
| 路径 | 篇目 | 适合 |
|---|---|---|
| 必读核心 | 1 → 3 → 6 → 8 → 15 | 快速建立五轴 |
| K8s 控制面 | 1 → 9 → 10 → 13 → 15 | apiserver 超时归因 |
| 容量与运维 | 4 → 7 → 12 → 14 | quota/defrag/升级 |
| 对照选型 | 1 → 16 | 协调层路径选型 |
| 完整通读 | 1 → … → 16 | 系统掌握 |
读法:先读本篇;若症状是
no leader,优先 02→03→15;若是
ErrCompacted,优先 04→09→13→15;若是 Node Lease
漂移,优先 10→13→15。Raft 论文与 PreVote 细节回 distributed/13;Multi-Raft
回 tikv-htap/。
Kubernetes 控制面指针
本系列第 13 篇专写 apiserver ↔︎ etcd 耦合;本篇只登记链路上的位置:
kubectl / controller / scheduler
→ kube-apiserver(storage 接口 + watch cache)
→ etcd gRPC(Put/Txn/Watch/Lease)
→ 单 Raft 组 → WAL → MVCC → bbolt
apiserver 把 etcd Revision 暴露为
resourceVersion;controller 的 resync 与 Watch
断连重连都依赖 MVCC 轴与 Watch 轴。控制面 etcd
故障的第一症状常在 apiserver 超时或 503,而不是
Pod 网络——排障时须能把症状映射回五轴,而不是默认 CNI
问题。
六、本篇不写什么
- Raft 论文全文与 Jepsen 方法论复述(外链 distributed/13、53)。
- Kubernetes 安装/Helm 全书;apiserver storage 实现全书。
- TiKV/FDB 内核重讲;Kine 兼容层全书。
- 未实测 etcd vs TiKV QPS/latency 排行榜。
- 把 live docs 后版本能力写成 v3.5.33。
- 伪造
etcdctl endpoint status、check perf截图。
参考资料
规范 / 官方文档 / 源码(A)
- etcd v3.5.33(源码 tag
v3.5.33,2026-07-23 补丁线) - etcd 官方文档 v3.5
etcd-io/etcdtagv3.5.33:server/etcdserver/、server/wal/、server/mvcc/- 本系列 PLAN.md §一、§八
论文(A)
- Ongaro & Ousterhout, In Search of an Understandable Consensus Algorithm, USENIX ATC 2014
- Li et al., etcd: A Distributed Key-Value Store for Shared Configuration and Service Discovery, ;login: 2017
站内对照(A/B)
- distributed/50 · etcd 深度解剖
- distributed/39 · KV 存储对比
- distributed/13 · Raft 深度重写
- distributed/53 · Jepsen
实验台账
- 本篇无集群实测;无伪造
etcdctl输出。
上一篇:系列目录
下一篇:单 Raft 组与服务器角色
读完这篇,下一步读什么
优先读同系列或同问题的下一篇,把单篇消费变成主题集群。
etcd / 生产内核:Raft、WAL、MVCC 与 Kubernetes 控制面底座
补齐 distributed 百科单篇之上的 etcd 生产内核:单 Raft 组、WAL/快照、Revision MVCC、bbolt 单写者、Watch/Lease、K8s apiserver 耦合,并以排障与相对 TiKV/FoundationDB 的选型收束。
【etcd】排障五轴:Raft/WAL/MVCC/Watch/Lease 口令表
按 Raft、WAL、MVCC、Watch、Lease 五轴做症状否证;解释 etcdctl endpoint status/health 字段语义;并对照 K8s 控制面 apiserver 超时与 Node Lease 漂移。
【etcd】Kubernetes 控制面耦合:apiserver、resourceVersion 与 Node Lease
钉 kube-apiserver 与 etcd 的分层边界;resourceVersion 如何映射 Revision MVCC;Node Lease 如何落在 Lease 轴;以及 apiserver 超时应如何分列到 Raft/Watch/Quota 五轴。
【etcd】treeIndex 与 Apply 管道:propose → commit → apply
走读 etcd v3.5.33 从 Raft commit 到 MVCC apply 的串行管道:treeIndex 与 bbolt 分工、consistent index、watchableStore 通知触发点,以及 committed/applied 分列排障。