WAL、快照与恢复
etcd 的持久化不是“把 KV 写进一个文件”,而是用 WAL 保存 Raft 元数据和 entries,用 snapshot 降低恢复成本,再用 backend 保存已应用的 MVCC 状态。
先给答案:etcd 的持久化不是“把 KV 写进一个文件”,而是用 WAL 保存 Raft 元数据和 entries…
Section titled “先给答案:etcd 的持久化不是“把 KV 写进一个文件”,而是用 WAL 保存 Raft 元数据和 entries…”etcd 的持久化不是“把 KV 写进一个文件”,而是用 WAL 保存 Raft 元数据和 entries,用 snapshot 降低恢复成本,再用 backend 保存已应用的 MVCC 状态。 正文沿“持久化关系 -> 为什么 snapshot 要先于普通日志释放 -> 快照不是 backend 备份的同义词”展开:先确认入口和状态归属,再跟踪控制流或数据流的推进,最后落到对外可观察的结果。
主要失效边界集中在“禁用 fsync、手工删除 WAL、只备份 backend.db”这些场景。它们破坏的是容量、顺序、并发或生命周期前提;排查时应先确认状态是否仍由正确对象持有,再核对推进条件和清理路径。
Raft Ready | +--> snapshot -> snapshot file -> ApplySnapshot +--> HardState + entries -> WAL segments -> fsync +--> backend -> MVCC state / consistent index
restart:snapshot + WAL + backend -> reconstruct raft storage -> replay committed entriesWAL 类型和创建/打开入口在 server/storage/wal/wal.go:73-101、:346-359。启动时 bootstrap.go 判断 WAL 是否存在,从 snapshot/backend 恢复,再创建 Raft storage:server/etcdserver/bootstrap.go:77-128。
为什么 snapshot 要先于普通日志释放
Section titled “为什么 snapshot 要先于普通日志释放”raftNode.start 在收到 snapshot 后先 SaveSnap,再保存 HardState 和 entries;随后 Sync,应用 Raft snapshot,最后 Release WAL 旧段:server/etcdserver/raft.go:250-320。如果先释放旧 WAL 而快照尚未可靠落盘,重启时可能既找不到旧日志,也没有可恢复的新快照。
快照不是 backend 备份的同义词
Section titled “快照不是 backend 备份的同义词”Raft snapshot 描述的是某个一致性 index 的状态机快照及成员配置;backend 是本地 MVCC/Bbolt 存储。etcd 在恢复和应用快照时需要协调两者的 index、版本和 schema,不能只复制某个 .db 文件就认为集群状态完整。
日志截断与恢复
Section titled “日志截断与恢复”当 Raft snapshot 持久化后,旧 WAL 段可以释放;新节点启动时从 snapshot index 之后的 WAL entries 继续构造 Raft log。backend 中的 consistent index 用于判断状态机已经应用到哪里,避免重复或跳过 entry。
为什么恢复路径要区分“有 WAL”和“无 WAL”
Section titled “为什么恢复路径要区分“有 WAL”和“无 WAL””替代方案:每次启动都把所有目录当作全新节点或都强行重放。
为什么不行:新集群需要初始化成员和空状态,已有成员则必须校验并恢复既有 term/index/cluster 信息;错误混用会造成成员身份或日志分叉。
证据:bootstrap 根据 wal.Exist(cfg.WALDir()) 选择恢复 backend、WAL 和 snapshot 的路径:server/etcdserver/bootstrap.go:77-121。
| 场景 | 风险 | 处理 |
|---|---|---|
| 禁用 fsync | 进程崩溃后 WAL 尾部丢失 | 仅在明确接受数据风险时使用 |
| 手工删除 WAL | 节点无法沿原日志恢复 | 使用 snapshot/restore 工具链 |
| 只备份 backend.db | 缺少 Raft term/index/成员信息 | 同时考虑 WAL 和 snapshot |
| 快照释放顺序错误 | 恢复出现 out-of-range | 遵循 SaveSnap -> Sync -> Apply -> Release |
日志、快照和状态机存储各自回答不同问题:日志回答“发生了什么”,快照回答“某个位置的完整状态”,状态库回答“当前可查询数据是什么”。
面试锚点
- WAL 和 snapshot 如何配合减少恢复时间?
- 为什么 snapshot 落盘后还要 Sync?
- backend、WAL、Raft snapshot 三者如何避免 index 不一致?