3. etcd 存储模型:MVCC、Revision、Key-Value 存储、Compact 与 Defrag
etcd 作为云原生控制面的核心数据层,其存储模型直接决定了多租户场景下的隔离能力与性能边界。本章深入剖析 etcd 的底层存储机制,包括多版本并发控制(MVCC)、全局修订号(Revision)、扁平化的 Key-Value 存储结构,以及关键的运维操作 Compact 与 Defrag。理解这些概念是设计仲裁水位隔离策略的前提。
3.1 多版本并发控制(MVCC)
etcd 采用 MVCC 模型来支持高并发读写与历史查询。每次对 Key-Value 的写入(Put)或删除(Delete)操作,etcd 不会原地覆盖旧数据,而是生成一个新的版本记录。这种设计避免了读写冲突,并允许客户端通过指定版本号读取历史数据。
3.2 全局修订号(Revision)
Revision 是 etcd 集群中一个全局单调递增的 64 位整数,每次事务提交(无论涉及多少个 Key)都会递增一次。它是 MVCC 版本链的“时间戳”,也是 etcd 实现 Watch 机制、事务隔离和仲裁水位计算的基础。
| Revision 属性 | 说明 | 多租户场景意义 |
|---|---|---|
| 全局唯一 | 整个集群共享一个 Revision 计数器,跨所有 Key 和所有租户。 | 可作为全局时间轴,用于仲裁水位线的统一标记。 |
| 单调递增 | 每次写入操作(包括事务)都会使 Revision +1。 | 水位隔离策略可基于 Revision 差值计算租户的“数据活跃度”。 |
| 与 Key 绑定 | 每个 Key 的每个版本都记录其创建时的 Revision(create_revision)。 | 可用于判断租户数据的“年龄”和“冷热程度”。 |
| Watch 触发 | 客户端 Watch 基于 Revision 订阅事件,从指定 Revision 开始推送。 | 水位过高时,Watch 事件堆积可能影响租户隔离性。 |
current_revision - compacted_revision 来估算集群中未被压缩的历史版本总量。这个差值越大,BoltDB 的存储压力越大,越容易触发仲裁水位告警。
3.3 Key-Value 存储结构
etcd 的存储模型在逻辑上是一个扁平的 Key-Value 空间,所有数据都存储在 BoltDB 的一个 Bucket 中。Key 是字节数组,Value 是包含版本元数据的结构体。
3.3.1 逻辑存储模型
- Key 空间: 扁平的,无目录层级。前缀查询(如
/registry/pods/)是模拟目录结构的主要方式。 - Value 结构: 包含
data(用户数据)、create_revision、mod_revision、version(当前版本号,从 1 开始递增)。 - 删除标记: 删除操作不会立即清除数据,而是写入一个带有
tombstone标记的版本。
3.3.2 物理存储模型(BoltDB)
etcd 使用 BoltDB(现为 bbolt)作为底层存储引擎。BoltDB 是一个嵌入式、事务性的 KV 存储,数据直接写入内存映射文件。
3.4 Compact(压缩)
Compact 是 etcd 回收历史版本、释放存储空间的核心操作。它通过指定一个 Revision 阈值,删除所有小于该阈值的旧版本数据。
3.4.1 Compact 操作流程
- 触发: 通过 etcdctl 或 API 调用
PUT /v3/kv/compact,指定revision参数。 - 标记: etcd 在内部记录一个
compacted_revision,表示该 Revision 之前的所有版本已被逻辑删除。 - 后台清理: etcd 的后台 goroutine 会逐步扫描 BoltDB 的 B+ 树,删除被标记的旧版本数据页。
- 空间释放: 删除的页被标记为 Freelist,等待后续写入复用。但 BoltDB 文件大小不会立即缩小。
3.4.2 自动 Compact 策略
| 策略类型 | 配置参数 | 说明 |
|---|---|---|
| 周期性 | --auto-compaction-retention |
按时间周期(如 1h)自动执行 Compact,保留最近 N 小时的数据。 |
| 基于 Revision | --auto-compaction-retention (单位: revision) |
保留最近 N 个 Revision 的数据,适用于对版本数量敏感的场景。 |
| 手动 | etcdctl compaction | 运维人员根据监控指标手动触发,适合精细化控制。 |
3.5 Defrag(碎片整理)
Compact 只标记删除旧版本,但 BoltDB 文件中的物理空间并未归还给操作系统。随着 Compact 的反复执行,文件内部会产生大量碎片(空闲页分散在各处),导致文件大小持续增长。Defrag 操作通过重写 BoltDB 文件来整理碎片、回收空间。
3.5.1 Defrag 触发条件
- 文件大小持续增长: 即使执行了 Compact,文件大小仍接近配额上限。
- 后端存储使用率过高: 监控指标
etcd_server_is_leader和etcd_server_quota_backend_bytes显示使用率 > 80%。 - 仲裁水位告警: 多租户场景下,某个租户的“数据活跃度”导致整体存储水位异常。
3.5.2 Defrag 操作流程
- 停止写入: Defrag 期间需要暂停对该 etcd 成员的写入操作(通常通过 Leader 转移实现)。
- 创建新文件: etcd 创建一个新的 BoltDB 临时文件。
- 数据重写: 将当前所有有效数据(未被 Compact 的版本)按顺序写入新文件,消除碎片。
- 文件替换: 原子性地用新文件替换旧文件,释放磁盘空间。
- 恢复服务: 恢复该成员的读写能力。
3.6 存储模型与仲裁水位的关系
etcd 的存储模型直接决定了仲裁水位的计算方式和隔离策略的可行性:
- MVCC 版本堆积: 每个租户的频繁写入都会产生大量历史版本,这些版本共享同一个 BoltDB 文件,导致全局水位上升。
- Revision 作为水位标尺: 仲裁水位隔离策略通常使用
current_revision - compacted_revision作为“未压缩版本深度”指标,用于评估每个租户对存储的消耗。 - Compact 与 Defrag 的协同: Compact 控制逻辑水位(版本数量),Defrag 控制物理水位(磁盘空间)。两者必须配合执行,才能有效控制仲裁水位。
| 存储维度 | 指标 | 对仲裁水位的影响 |
|---|---|---|
| 逻辑水位 | 未压缩版本数 (Revision 差值) | 高:Watch 事件堆积、查询性能下降、触发配额告警。 |
| 物理水位 | BoltDB 文件大小 / 配额 | 高:磁盘 I/O 压力增大、Defrag 耗时增加、集群进入只读模式。 |
| 租户隔离水位 | 单个租户的 Key 数 + 版本增长率 | 高:该租户成为“噪声邻居”,影响其他租户的写入延迟和 Watch 稳定性。 |