4. etcd 数据模型:Key-Value 存储、版本号、Revision、MVCC 机制

etcd 是一个强一致性的分布式键值存储系统,其底层数据模型是理解备份恢复、灾备策略以及数据一致性的核心基础。本章将深入剖析 etcd 的逻辑存储结构、版本控制机制以及多版本并发控制(MVCC)的实现原理。

4.1 逻辑存储模型:扁平的 Key-Value 空间

etcd 的数据模型在逻辑上是一个扁平的键值空间,所有数据都以 Key-Value 对的形式存储。Key 被组织成一个类似于文件系统的层级命名空间(例如 /registry/pods/default/my-pod),但底层存储并不依赖目录树,而是基于前缀的扁平化 B+ 树索引。

概念 说明 示例
Key 唯一的字节数组标识符,支持前缀查询和范围查询。 /registry/deployments/default/nginx
Value 与 Key 关联的数据内容,通常为序列化后的 protobuf 或 JSON 对象。 {"kind":"Deployment","metadata":{...}}
Lease 关联到 Key 的租约(TTL),用于自动过期清理。 Lease ID: 0x12345678
Create Revision 该 Key 被创建时的全局修订版本号。 100
Mod Revision 该 Key 最近一次被修改时的全局修订版本号。 150
Version 该 Key 从创建至今被修改的次数(单调递增)。 3
关键理解: 在 etcd 中,Key 是唯一的,但同一个 Key 在不同时间点可以对应多个历史 Value。Version 是 Key 级别的计数器,而 Revision 是集群级别的全局时钟。

4.2 全局修订版本号(Revision)

Revision 是 etcd 集群中一个单调递增的 64 位整数,是整个集群的“逻辑时钟”。每一次对数据的写入操作(Put、Delete、Txn)都会使 Revision 增加 1。Revision 是 MVCC 和 Watch 机制的核心。

  • 全局唯一性: 在整个集群生命周期内,Revision 严格递增,不会重复。
  • 事务原子性: 在一个事务(Txn)中,无论包含多少个 Put/Delete 操作,整个事务只对应一个 Revision。
  • 数据版本快照: 通过指定 Revision,可以读取集群在该时间点的历史数据快照。
  • Watch 机制基础: Watch 客户端基于 Revision 订阅后续的数据变更事件。
备份恢复关联: 在执行 etcd 快照备份时,备份文件会记录当时的 revision。恢复时,集群会从该 Revision 开始重建数据。如果备份的 Revision 落后于当前集群,恢复后可能会丢失部分数据变更。

4.3 多版本并发控制(MVCC)

etcd 采用 MVCC(Multi-Version Concurrency Control) 机制来支持并发读写和历史数据查询。MVCC 的核心思想是:每次修改 Key 时,不直接覆盖旧数据,而是生成一个新的数据版本,旧版本数据保留在存储引擎中。

etcd MVCC 数据版本链示意图 Key: /app/config Version 1 Revision: 10 Version 2 Revision: 25 Version 3 (当前) Revision: 42 删除标记 (Tombstone) MVCC 核心要点 • 每个 Key 维护一个版本链,Version 从 1 开始递增。 • 每次 Put 操作创建新版本,旧版本仍可被历史查询访问。 • Delete 操作不立即删除数据,而是写入一个“删除标记”(Tombstone)。 • 后台压缩(Compaction)会清理指定 Revision 之前的旧版本,释放存储空间。

4.4 MVCC 与备份恢复的实践关联

理解 MVCC 机制对于制定正确的备份恢复策略至关重要。以下表格总结了 MVCC 特性对灾备操作的影响:

MVCC 特性 对备份的影响 对恢复的影响
历史版本保留 快照备份包含所有未压缩的历史版本,备份文件可能较大。 恢复后,集群包含备份时刻的所有历史版本,可支持基于 Revision 的历史查询。
Revision 单调递增 备份文件记录的 Revision 是恢复后集群的起点。 恢复后,新写入操作的 Revision 会从备份的 Revision 继续递增,保证逻辑时钟连续性。
压缩(Compaction) 定期执行压缩可以减小备份体积,但会丢失历史版本。 如果备份在压缩之后执行,恢复后将无法查询到被压缩掉的旧版本数据。
删除标记(Tombstone) 备份包含删除标记,恢复后 Key 仍处于删除状态。 恢复后,被删除的 Key 不会自动复活,除非从更早的备份恢复。
灾备实践警告: 不要仅依赖 etcd 的 MVCC 历史版本作为长期备份方案。MVCC 的版本保留受压缩策略控制,默认的自动压缩会定期清理旧版本。必须使用 etcdctl snapshot save 或定期备份工具(如 Velero)来创建独立的物理快照。

4.5 数据存储架构总览

下图展示了 etcd 从客户端请求到底层存储的完整数据流,以及 MVCC 和 Revision 在其中的位置:

etcd 数据写入与 MVCC 存储架构 客户端 (etcdctl/API) gRPC API / KV Server MVCC 模块 分配 Revision / 管理版本链 Backend (BoltDB) B+Tree 索引 / 持久化存储 写入流程:客户端请求 → gRPC 认证 → MVCC 分配 Revision → Backend 写入 BoltDB

4.6 关键代码示例:Revision 与 Version 的查询

以下命令展示了如何通过 etcdctl 查看 Key 的 MVCC 元数据:

# 写入一个 Key
etcdctl put /app/config '{"port": 8080}'

# 读取 Key 并查看详细信息(包括 Version 和 Revision)
etcdctl get /app/config -w json

# 输出示例(简化):
# {"header":{"cluster_id":14841639068965178418,"member_id":10276657743932975437,
#  "revision":100,"raft_term":2},"kvs":[{"key":"L2FwcC9jb25maWc=",
#  "create_revision":100,"mod_revision":100,"version":1,
#  "value":"eyJwb3J0IjogODA4MH0="}]}

# 再次修改 Key
etcdctl put /app/config '{"port": 9090}'

# 再次查询,观察 version 和 mod_revision 的变化
etcdctl get /app/config -w json
# 输出中 version 变为 2,mod_revision 变为 101

# 查询历史版本(假设 revision 100 仍未被压缩)
etcdctl get /app/config --rev=100 -w json
# 返回 revision 100 时的旧值 {"port": 8080}
恢复验证要点: 在灾备恢复测试中,恢复后应使用 etcdctl get --rev=<backup_revision> 验证历史数据是否完整。同时检查 cluster_id 和 member_id 是否与备份时一致,确保数据完整性。