3. etcd 存储模型:MVCC多版本并发控制、键值存储结构、Watch机制与事件流

etcd 的存储模型是其高可用、强一致与实时监听能力的基石。理解 MVCC、键值存储结构与 Watch 机制,是进行 Federation 调度参数优化的前提。本章将深入剖析这三个核心组件,并揭示它们如何协同工作。

3.1 MVCC 多版本并发控制

etcd 采用 MVCC(Multi-Version Concurrency Control)机制,确保读写操作互不阻塞,同时维护数据的历史版本。每次对键值对的写操作(Put/Delete)都会生成一个新的版本号,旧版本数据不会被立即覆盖,而是保留一段时间。

  • 版本号(Revision): 全局单调递增的整数,每次事务提交时递增。它是 etcd 逻辑时钟的核心。
  • 子版本(Sub-Revision): 在同一事务内,对多个键的修改会共享同一个 Revision,但通过 Sub-Revision 区分操作顺序。
  • 压缩(Compaction): 为避免历史版本无限膨胀,etcd 会定期执行压缩,删除指定 Revision 之前的所有旧版本数据。
压测启示: 频繁的写操作会快速产生大量历史版本,若压缩策略(--auto-compaction-retention)配置不当,会导致存储空间暴涨和性能下降。在 Federation 压测中,需监控 backend 库大小与压缩延迟。

3.2 键值存储结构:BoltDB 与内存树

etcd 的持久化存储基于 BoltDB(一个嵌入式 Go 数据库),而内存中维护了一棵 B-tree 用于加速范围查询。整体架构分为两层:

层级 组件 作用 关键特性
内存层 B-tree (Go 实现) 提供键的快速查找与范围遍历 按 key 排序,支持前缀扫描
持久化层 BoltDB (B+树) 将数据与索引写入磁盘文件 支持事务 ACID,数据页管理
逻辑层 MVCC 索引 管理 key 到多个 Revision 的映射 支持历史版本查询与 Watch

每个键在 BoltDB 中存储为一个主键为 key_revision 的条目,值包含键名、创建版本、修改版本、租约 ID 和用户数据。这种设计使得按 Revision 遍历成为可能。

// 键值存储的简化逻辑结构
type KeyValue struct {
    Key            []byte
    CreateRevision int64
    ModRevision    int64
    Version        int64
    Value          []byte
    Lease          int64
}
etcd 键值存储双层架构 内存层:B-tree 索引 按 key 字典序排序 支持前缀扫描与范围查询 指向 BoltDB 中的最新版本 读操作优先命中此层 持久化层:BoltDB (B+树) 数据页存储在磁盘文件 支持 ACID 事务 存储所有历史版本数据 通过 mmap 加速访问 查找 MVCC 逻辑层:版本管理 维护 key → [Revision1, Revision2, ...] 映射 支持按 Revision 遍历与 Watch 事件生成 写操作:B-tree 更新指针 + BoltDB 追加新版本;读操作:B-tree 定位 + BoltDB 读取

3.3 Watch 机制与事件流

Watch 是 etcd 实现实时配置同步与服务发现的核心。客户端通过 gRPC 流式接口注册对某个键或前缀的监听,当数据发生变化时,etcd 会推送事件。

3.3.1 Watch 工作流程

  1. 注册 Watch: 客户端发起 Watch 请求,指定 key 或前缀,以及起始 Revision。
  2. 事件缓冲: etcd 将 Watch 注册到对应的 WatchableStore,并记录当前 Revision。
  3. 事件触发: 当有写操作提交时,MVCC 模块生成事件(Put/Delete),并广播到所有匹配的 Watch。
  4. 事件推送: etcd 通过 gRPC 流将事件批量发送给客户端,客户端按序处理。

3.3.2 事件流与版本关系

每个事件都携带发生时的 Revision,客户端可以根据 Revision 判断事件顺序。Watch 支持从指定历史 Revision 开始回放,实现断连恢复。

事件类型 触发操作 事件内容 Revision 变化
Put Put / Txn key, value, 新 ModRevision 全局 Revision +1
Delete Delete / Txn key, 删除前的 value 全局 Revision +1
Create Put (key 不存在) key, value, CreateRevision 全局 Revision +1
Watch 事件流与 MVCC 版本关系 Revision 1 2 3 4 5 Put /a Del /a Put /b Put /a Put /c Watch /a (从 Rev=1) 客户端事件流 Watch 从指定 Revision 开始,按序推送所有匹配事件。 客户端通过 Revision 判断事件顺序,实现断连后增量同步。

3.4 压测场景下的关键参数

在 Federation 压测沙盘中,以下参数直接影响存储模型的行为与性能:

  • --auto-compaction-retention: 历史版本保留时长(如 1h)。设置过短会导致 Watch 历史数据丢失;设置过长会占用大量磁盘 IO。
  • --quota-backend-bytes: 后端存储大小限制(默认 2GB)。超过限制后写操作会失败,需配合压缩使用。
  • --max-request-bytes: 单次请求大小限制(默认 1.5MB)。影响批量 Watch 事件的大小。
  • Watch 连接数: 每个客户端可建立多个 Watch,但过多连接会消耗服务端内存与 goroutine。
压测陷阱: 当 Watch 客户端消费速度慢于事件产生速度时,etcd 会积压事件在内存中,导致 OOM。务必在压测中监控 etcd_server_events_queued_total 指标,并设置合理的 Watch 超时与流控。