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 时,不直接覆盖旧数据,而是生成一个新的数据版本,旧版本数据保留在存储引擎中。
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 在其中的位置:
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 是否与备份时一致,确保数据完整性。