MVCC 与 backend
This content is not available in your language yet.
etcd 的 MVCC 把“同一个 key 的历史版本”和“整个集群的逻辑时间”统一起来。请求在状态机中按 revision 应用,索引负责从用户 key 找到对应的内部版本,再由 backend 批量提交。
先给答案:etcd 的 MVCC 把“同一个 key 的历史版本”和“整个集群的逻辑时间”统一起来
Section titled “先给答案:etcd 的 MVCC 把“同一个 key 的历史版本”和“整个集群的逻辑时间”统一起来”etcd 的 MVCC 把“同一个 key 的历史版本”和“整个集群的逻辑时间”统一起来。请求在状态机中按 revision 应用,索引负责从用户 key 找到对应的内部版本,再由 backend 批量提交。 正文沿“数据路径 -> revision 语义 -> key index 与 backend 分工”展开:先确认入口和状态归属,再跟踪控制流或数据流的推进,最后落到对外可观察的结果。
主要失效边界集中在“读 compact 前 revision、revision 当 wall clock、单操作单 commit”这些场景。它们破坏的是容量、顺序、并发或生命周期前提;排查时应先确认状态是否仍由正确对象持有,再核对推进条件和清理路径。
Put / Txn | vnew revision + keyIndex | +--> backend batch tx: key -> revisioned value +--> event batch -> Watch +--> lease attachment | +--> CommittreeIndex.Range 在 server/storage/mvcc/index.go:162 根据 key、目标 revision 和范围返回版本定位;事务读写实现位于 kvstore_txn.go:31-73、:166-197。backend 的批量事务在 server/storage/backend/batch_tx.go:242-361 提交。
revision 语义
Section titled “revision 语义”每次状态机写事务推进全局 revision;同一个事务内的多个操作共享事务 revision,但通过 sub revision 或事件顺序区分操作。读取可以指定历史 revision;超过 compact revision 的历史读取必须返回 compacted 错误,而不是静默返回当前值。
key index 与 backend 分工
Section titled “key index 与 backend 分工”user key --treeIndex--> Revision{main, sub} | +--> backend bucket stores value/metatree index 适合按用户 key 和 revision 查询历史,backend 负责持久化实际值、元数据和一致性索引。把全部查询索引直接放在 backend 中会让历史选择和 compact 逻辑更难隔离。
compact
Section titled “compact”store.Compact 调度并执行索引和历史版本清理:server/storage/mvcc/kvstore_compaction.go:28-272;tree index 的清理入口为 index.Compact:server/storage/mvcc/index.go:205。compact 不是删除当前值,而是删除低于保留边界的历史版本,并更新 compact revision。
backend commit 与读事务
Section titled “backend commit 与读事务”backend 提供读事务和批量写事务,ForceCommit 可把缓冲写入落盘:server/storage/backend/backend.go:49-77、:355-355。MVCC 在一次状态机应用中聚合多个操作,再提交 backend,避免每个 key 都产生独立磁盘事务。
为什么使用 MVCC 而不是覆盖写
Section titled “为什么使用 MVCC 而不是覆盖写”替代方案:key 只保存当前值,Watch 和历史读另建日志查询。 为什么不行:历史读取、事务快照和 compact 需要一致的版本坐标,旁路日志很容易与当前值脱节。 取舍:MVCC 消耗更多空间和索引维护成本,但让读一致性、Watch 起点和历史清理共享同一 revision 模型。
| 场景 | 现象 | 原因 | 处理 |
|---|---|---|---|
| 读 compact 前 revision | 返回历史数据失败 | 历史已被清理 | 从当前 revision 重新读取 |
| revision 当 wall clock | 跨集群时间理解错误 | revision 是逻辑序号 | 只用于同一状态机版本语义 |
| 单操作单 commit | 吞吐下降 | backend 事务过碎 | 在 apply 中批量聚合 |
| 只删除 backend 值 | index 残留 | 元数据和索引未同步 | 由 MVCC compact 统一清理 |
MVCC 的价值不是“保存旧值”这么简单,而是用一个可比较的逻辑版本把读快照、事务、事件和垃圾回收串起来。
面试锚点
- etcd 的 revision 和 Raft index 为什么不是同一个概念?
- tree index 与 backend 各自负责什么?
- compact 为什么不能删除当前版本?