一致性笔记:快照与日志截断:减少恢复时间
发布于:
这篇讲清楚一个“基于日志复制的一致性系统”迟早要面对的现实:
- 日志会无限增长
- 新节点追赶会越来越慢
- 重启恢复会越来越慢
快照(snapshot)+ 日志截断(log truncation)就是用来把“恢复成本”从“与历史长度相关”变成“与快照大小相关”。
可以将它理解成:
- 日志负责增量复制
- 快照负责把增量压缩成一个可快速安装的检查点
1. 先定义目标:成功意味着什么
做快照/截断不是为了“省磁盘”,而是为了:
- 恢复时间可控:重启不需要重放超长日志
- 追赶时间可控:新 follower 不需要从远古 entry 一条条补
- 运行稳定:避免日志元数据/存储膨胀导致性能退化
flowchart TD
G[Goal] --> R[Fast restart]
G --> C[Fast catch-up]
G --> S[Stable long-running]
2. 直觉模型:日志是增量,快照是压缩后的基线
把状态机状态表示为:
state = apply(snapshot) + apply(log entries after snapshot)
因此截断的前提是:
- 被截断掉的日志内容已经被快照“覆盖”(已包含其效果)
flowchart LR
SS[Snapshot at index S] --> L[Log entries S+1..]
L --> SM[State machine]
SS -->|covers| T[Log <= S can be truncated]
3. 快照生命周期:生成、持久化、传播、安装
3.1 什么时候生成快照(触发条件)
常见触发:
- 日志长度/大小超过阈值(最常见)
- 磁盘空间压力(不得不做)
- 运行时间周期性(保守策略)
建议:以“截断带来的收益”来选阈值:
- 快照太频繁:CPU/IO 开销高
- 快照太稀疏:恢复/追赶慢
3.2 快照包含什么
典型快照内容:
- 状态机数据(可能是 KV、元数据、索引等)
lastIncludedIndex/lastIncludedTerm- (可选)配置/成员信息、校验和
4. InstallSnapshot:leader 如何把快照发给落后的 follower
当 follower 落后太多,leader 发现:
nextIndex已经落到 leader 的截断点之前
此时继续发 AppendEntries 已经没意义,必须改发快照。
sequenceDiagram
participant L as Leader
participant F as Follower
Note over L: follower too far behind
L->>F: InstallSnapshot with index term and data
F->>F: Persist snapshot
F->>F: Discard log up to lastIncludedIndex
F->>F: Apply snapshot to state machine
F-->>L: Ack
L->>F: Resume AppendEntries from lastIncludedIndex plus 1
4.1 关键安全点:先落盘,再 apply
快照安装的顺序很关键:
- 先把 snapshot 持久化
- 再更新本地元数据(lastIncludedIndex/Term)
- 再 apply 到状态机
否则 crash 会把节点留在“半安装”的危险状态。
5. 日志截断:能删什么
截断(truncation)的安全条件可以压成核心要点:
- 只截断已经被快照覆盖的前缀日志
典型动作:
- 保留:
lastIncludedIndex之后的日志 - 删除:
<= lastIncludedIndex的日志段
flowchart TD
A[Truncate] --> B[Snapshot covers prefix]
B --> C[Delete log segments <= S]
C --> D[Keep S+1..]
6. 恢复路径:重启时怎么恢复得快
重启恢复通常变成:
- 读取 snapshot(得到一个基线)
- 重放 snapshot 之后的日志增量
flowchart TD
Boot[Restart] --> LoadSS[Load snapshot]
LoadSS --> Replay[Replay log after snapshot]
Replay --> Serve[Ready]
7. 常见问题:为什么快照做不好会“更慢更不稳”
7.1 快照太大
- 生成快照耗时长
- 传输耗时长
- install 期间 follower 落后更严重
对策:
- 分片/增量快照
- 压缩(但注意 CPU)
- 传输分块 + 断点续传(实现复杂度更高)
7.2 快照与业务 IO 争用
快照本质是大 IO:
- 与前台读写抢磁盘带宽
- 导致尾延迟抖动
对策:
- 限速(rate limit)
- 后台优先级
- 资源隔离(独立盘/独立线程池)
7.3 快照与成员变更/配置的边界
如果系统支持成员变更:
- 快照里是否包含配置?
- install 后如何保证配置一致?
需要保证:配置变更与快照的元数据能一起被安全恢复。
8. 需要监控什么(可观测信号)
建议直接围绕“恢复成本与落后程度”监控:
- log size / log segment count
- snapshot size / generation time
- install snapshot count / duration
- replication lag(follower 落后 index)
- catch-up time(新节点追赶耗时)
9. 排障顺序
- 先确认瓶颈:追赶慢还是重启慢
- 看 lag:是不是某些 follower 长期落后
- 看 snapshot:是不是太大/太慢/太频繁
- 看 IO:是不是快照把磁盘打爆导致尾延迟
- 最后改阈值/架构:分片/限速/隔离
10. 小结
快照与日志截断的本质是:用一个可安装的“基线”替代无限增长的历史,让恢复与追赶成本可控。做不好会引入巨大的 IO 抖动与恢复风险;做得好则是强一致系统长期稳定运行的关键。