快照和日志压缩有什么作用?
简化版
快照把某个已提交且已应用水位的完整状态固化下来,日志压缩则删除该水位之前已被快照覆盖的历史,从而控制日志空间并加速新副本恢复。快照必须记录边界 index/term、校验和与格式版本,原子生成和安装;它不是备份,未提交日志也不能被快照固化。
详细版
若状态机有 1 亿条日志,新节点逐条重放可能要数小时。创建 index=9000 的快照后,新节点先安装快照,再重放 9001 之后日志即可。
创建边界必须满足 snapshotIndex <= appliedIndex <= commitIndex。Copy-on-write、MVCC 快照或短暂停写用于得到一致视图,不能边遍历边读取互相矛盾的业务状态。
安装流程是分块传输到临时文件、校验长度/checksum、fsync、原子切换状态机和元数据,再处理可衔接的日志后缀。中断时旧状态仍可服务。
快照用于副本恢复和日志压缩;备份则面向误删、软件 bug 和灾难恢复,通常要独立保留多个历史恢复点。
完整版教学
一、为什么日志不能无限增长
日志适合增量复制,却不适合永久保存全部历史。空间会持续增长,启动重放越来越慢,严重落后副本追赶时间也不可接受。快照把历史命令的效果折叠为一个状态基线。
snapshot(index=9000, term=12) + log[9001..current]
二、快照边界包含什么
除了状态数据,还要记录 lastIncludedIndex、lastIncludedTerm、成员配置、schema/format version 和 checksum。index/term 让共识层判断后续日志能否衔接,格式版本让新旧程序知道能否读取。
| 字段 | 用途 | 缺失风险 |
|---|---|---|
| index/term | 连接共识历史 | 错接日志后缀 |
| checksum | 检测传输/落盘损坏 | 静默错误 |
| config | 恢复成员关系 | 使用错误 quorum |
| format version | 兼容升级 | 新程序无法解释状态 |
三、如何生成一致快照
快照只能覆盖已应用前缀。状态机在 index=9000 建立 MVCC read point,后台可继续应用更高日志,但快照读取始终看到同一版本。没有 MVCC 时可用 copy-on-write 或短暂冻结后复制。
边扫描边读“当前值”会得到撕裂视图,例如账户 A 已扣款而 B 尚未入账。快照 API 必须提供一致读取语义,而不是仅仅复制目录。
四、何时压缩日志
快照成功写入、checksum 验证并原子发布后,才能删除它覆盖的日志。还要考虑是否有安装中的旧副本、增量备份或审计需要引用这些段;生产常保留一段尾日志,而非立即删到边界。
触发条件可结合日志字节、条目数、恢复时间目标和磁盘水位。过于频繁会增加 CPU/I/O,太稀疏则恢复慢且日志占用大。
五、安装快照的原子性
Leader 分块发送快照,Follower 写临时区域并支持断点续传。全部块到齐后校验 checksum、fsync 数据和目录,再以原子元数据切换为活动状态。失败时删除临时产物,继续使用旧状态。
如果本地在快照边界之后存在 term 匹配的合法日志,可保留后缀;无法证明衔接时应丢弃冲突后缀,再从 Leader 获取。绝不能把已应用的新状态与不匹配旧日志拼接。
六、快照不是备份
如果软件 bug 把错误状态合法提交,最新快照也包含错误;若运维误删,集群会把删除复制到所有副本。备份应隔离权限、跨故障域、保留历史恢复点并定期恢复演练。
监控快照年龄、生成耗时、大小、失败率、安装进度、校验错误、日志保留字节和预计恢复时间。空间规划要覆盖新旧快照短时共存。
记忆钩子:快照是“已应用历史的压缩证明”,只有新证明原子落稳后,旧日志才有资格删除。
七、常见误区与追问
- 误区:commitIndex 之前都可直接快照。 状态机必须已经应用到边界,不能快照尚未执行的命令。
- 误区:复制数据目录就是一致快照。 并发更新可能产生撕裂视图,需要 MVCC/COW 或冻结。
- 误区:有快照就不需要备份。 快照随集群复制错误,不能替代独立历史恢复点。
- 追问:安装中断怎么办? 临时文件不激活,支持续传或丢弃,旧状态继续有效。
- 追问:日志何时能删? 快照校验并原子发布,且没有必要引用后再按保留策略删除。
- 追问:为什么记录 term? 用于验证快照边界与后续日志是否属于同一合法历史。
八、加强记忆
快照流程按“选已应用边界—取得一致视图—写临时文件—校验 fsync—原子发布—压缩旧日志”记忆。恢复时先装快照再追尾日志。把 index/term、原子性和备份边界讲清,才能避免把快照误解成普通文件拷贝。