2. etcd 核心架构回顾:Raft 协议、MVCC、Watch 机制、存储引擎

在深入压测与调度瓶颈诊断之前,必须重新校准对 etcd 内部四大核心模块的理解。本章从 Raft 共识、MVCC 并发控制、Watch 事件推送、bbolt 存储引擎四个维度,梳理关键路径与性能敏感点。

2.1 Raft 协议:共识层与写放大

etcd 使用 Raft 协议保证多节点数据一致性。每个写请求必须经过 Leader 节点,并完成以下步骤:

  1. Propose:Client 向 Leader 提交提案。
  2. Append:Leader 将提案写入自己的 WAL 并广播给 Follower。
  3. Commit:Leader 收到多数派(quorum)确认后,提交日志并应用到状态机。
  4. Reply:Leader 返回成功给 Client。
性能关键点:Raft 的写延迟 = 网络 RTT(多数派) + 磁盘 fsync 延迟。在跨 AZ 部署或磁盘 IO 抖动时,P99 延迟会显著上升。

下表总结了 Raft 各阶段对性能的影响:

阶段 主要开销 压测关注指标
Propose 序列化 + 网络发送 Leader 接收 QPS
Append WAL fsync + 网络广播 磁盘 IOPS / 网络带宽
Commit 等待多数派 ACK P99 延迟 / 心跳超时
Apply 状态机写入 bbolt bbolt 事务吞吐
Raft 写请求生命周期 Client Leader Follower 1 Follower 2 ① Propose ② Append ③ ACK ④ Reply State Machine bbolt Apply WAL fsync

2.2 MVCC:并发控制与历史版本

etcd 通过 MVCC(Multi-Version Concurrency Control)实现无锁读。每个 key 的每次修改都会生成一个新版本,读请求可以指定版本号,避免读写冲突。

  • revision:全局单调递增的主版本号,每次事务提交 +1。
  • keyValue:存储 key、value、create_revision、mod_revision、version。
  • 历史版本:旧版本不会立即删除,而是等待 compaction 回收。
压测陷阱:大量历史版本堆积会导致 bbolt 空间膨胀、读放大。如果 compaction 配置不当(如 auto-compaction-retention 过小),会频繁触发压缩,消耗 CPU 和 IO。
MVCC 版本链示例 Key: /foo v1: rev=10 v2: rev=25 v3: rev=42 current compacted

2.3 Watch 机制:事件流与推送模型

Watch 是 etcd 的核心订阅能力。Client 通过 gRPC 流监听 key 或前缀的变化,etcd 在事务提交后异步推送事件。

  • WatchableStore:维护 watcher 列表,按 key 和 revision 索引。
  • 事件合并:同一 revision 内的多次修改合并为一个事件。
  • 历史事件回放:如果 Client 指定的 revision 已被 compaction,则返回 ErrCompacted。
压测关注点:Watch 数量过多(>10k)会导致内存膨胀和事件分发延迟。每个 watcher 在事件触发时都会产生一次 gRPC 消息,高吞吐场景下可能成为瓶颈。

2.4 存储引擎:bbolt 与 B+ 树

etcd 使用 bbolt(原 boltdb)作为底层存储引擎,这是一个基于 B+ 树的嵌入式 KV 数据库。

特性 说明 压测影响
事务模型 读写事务互斥,同一时刻只允许一个写事务 写并发受限,高 QPS 下易出现锁竞争
页管理 B+ 树节点以页(4KB/8KB)为单位分配 大量随机写入导致页分裂和碎片
mmap 通过内存映射文件访问数据 内存占用随数据量线性增长
freelist 管理空闲页,支持事务回滚 频繁 compaction 导致 freelist 膨胀
bbolt B+ 树结构 Root Page Internal Internal Internal Leaf Leaf Leaf Leaf Leaf

2.5 核心路径总结

一个 etcd 写请求的完整路径如下:

Client → gRPC → Raft (Propose) → WAL (fsync) → Raft (Commit) → 
State Machine → MVCC (版本分配) → bbolt (B+树写入) → Watch (事件推送) → gRPC → Client