3. etcd 核心原理回顾:为压测打下理论基础
在构建多集群 etcd 压测沙盘之前,必须深入理解 etcd 的四大核心支柱:Raft 共识算法、MVCC 存储引擎、Watch 机制以及事务 API。这些机制直接决定了 etcd 在高压场景下的行为特征、性能瓶颈和故障模式。本节将逐一剖析,为后续压测指标设计和故障注入提供理论依据。
3.1 Raft 共识算法:分布式一致性的基石
etcd 使用 Raft 算法在集群节点间复制日志,确保数据强一致性。Raft 将节点角色分为三种:Leader、Follower 和 Candidate。所有写请求必须由 Leader 处理,Leader 将日志条目复制到多数节点(Quorum)后,才视为提交成功。
3.1.1 选举与心跳
Leader 通过定期发送 Heartbeat(心跳)维持权威。Follower 如果在选举超时时间内未收到心跳,则转为 Candidate 并发起选举。选举超时通常为 150~300ms 随机值,避免多个节点同时发起选举导致选票分裂。
| 参数 | 默认值 | 压测影响 |
|---|---|---|
| Heartbeat Interval | 100ms | 心跳间隔越短,网络开销越大;间隔过长会延迟故障发现 |
| Election Timeout | 1000ms | 超时时间影响 Leader 切换速度,压测中可模拟网络延迟触发频繁选举 |
| Snapshot Count | 10000 | 触发快照的日志条目数,影响磁盘 I/O 和内存占用 |
3.2 MVCC 存储引擎:版本管理与并发控制
etcd 采用 多版本并发控制(MVCC) 存储引擎,底层基于 BoltDB(etcd v3 使用 bbolt)。每次写操作不会原地覆盖旧值,而是生成一个新版本号(revision),旧版本数据保留在磁盘上,直到被压缩(Compaction)回收。
3.2.1 版本号与事务隔离
etcd 使用全局单调递增的 revision 作为版本号。每个 key 的每次修改都会产生一个新 revision。MVCC 使得读操作无需加锁,读请求可以基于某个固定 revision 获取一致性快照,实现 可重复读(Repeatable Read) 隔离级别。
- 全局 revision:集群范围内单调递增,每次事务提交时递增。
- key 的版本:每个 key 维护
create_revision、mod_revision、version(修改次数)。 - 读事务:基于 revision 的快照读,不阻塞写操作。
etcd_server_slow_read_indexes_total 指标,如果读索引变慢,说明 MVCC 版本链过长或磁盘 I/O 成为瓶颈。压测时可通过调整 --auto-compaction-retention 参数控制版本保留时间。
3.3 Watch 机制:事件驱动的核心
Watch 机制允许客户端监听 key 或 key 前缀的变化。etcd 通过 gRPC 双向流 将事件推送给客户端。Watch 事件包含 PUT、DELETE 类型,以及变更前后的 key-value 和版本号。
| Watch 类型 | 描述 | 压测关注点 |
|---|---|---|
| Key Watch | 监听单个 key 的变化 | 事件推送延迟、丢事件概率 |
| Prefix Watch | 监听某个前缀下的所有 key | 大量 key 变更时的事件吞吐量 |
| ProgressNotify | 定期发送进度通知,确保 Watch 不超时 | 长时间空闲连接的心跳开销 |
3.3.1 Watch 实现原理
etcd 的 Watch 基于 MVCC 的事件历史记录实现。当客户端创建 Watch 时,etcd 会从指定的 revision 开始扫描事件历史,然后持续监听新事件。Watch 事件存储在内存中的 EventHistory 环形缓冲区,默认容量为 1000 条。
3.4 事务 API:原子性与条件更新
etcd v3 提供 事务(Txn) API,支持在一个原子操作中执行多个比较(If)、成功操作(Then)和失败操作(Else)。事务基于乐观锁实现,通过比较 revision 或 value 来保证原子性。
// 事务示例:原子 CAS(Compare-And-Swap)
txn := client.Txn(ctx)
txn.If(client.Compare(client.Value("/app/config"), "=", "old_value"))
txn.Then(client.Put("/app/config", "new_value"))
txn.Else(client.Get("/app/config"))
resp, err := txn.Commit()
3.4.1 事务性能特征
事务的性能受以下因素影响:
- 比较条件数量:每个比较条件需要读取一次 key,增加延迟。
- 操作数量:Then/Else 中的操作越多,事务执行时间越长。
- 冲突概率:高并发下事务冲突导致重试,降低吞吐量。
| 事务类型 | 典型延迟(3 节点集群) | 适用场景 |
|---|---|---|
| 单 key 写入 | 0.5~2ms | 简单配置更新 |
| 多 key 事务(5 个操作) | 2~5ms | 分布式锁、服务注册 |
| 条件事务(CAS) | 1~3ms | Leader 选举、资源分配 |
3.5 四大机制协同工作:压测视角
在压测场景中,Raft、MVCC、Watch 和事务 API 并非独立运行,而是紧密耦合:
- 写路径:客户端发起事务 → Leader 接收 → Raft 日志复制 → 多数节点确认 → 提交到 MVCC 存储 → 生成新 revision → 触发 Watch 事件推送。
- 读路径:客户端发起读请求 → 基于 revision 的快照读 → 从 MVCC 存储返回数据(无需 Raft 共识)。
- 故障场景:Leader 宕机 → Raft 选举新 Leader → 期间写请求失败 → 客户端重试 → 新 Leader 恢复服务。