核心概念
etcd 架构总览:Raft 共识算法、MVCC 多版本控制、Revision 体系、gRPC API 布局、数据模型与 Key 空间,以及 Lease / Watch 机制概述。
什么是 etcd
etcd 是一个强一致性、分布式的键值存储系统,专为分布式系统协作设计,常用于:
- 服务发现与注册:服务实例在 etcd 中注册,消费者通过 watch 发现变更
- 分布式配置中心:配置集中管理,客户端 watch 变更实时生效
- 分布式锁与选主:基于 Lease 和 watch 实现选主与锁
- 协调与元数据存储:Kubernetes 用 etcd 持久化所有集群状态
核心特性
| 特性 | 说明 |
|---|---|
| 强一致性 | 基于 Raft 协议,保证线性一致性读写 |
| 高可用 | 奇数节点(≥3)容忍少数节点故障 |
| Watch 机制 | 客户端可监听 Key 变更,实时推送 |
| Lease/TTL | 键可绑定租约,到期自动删除 |
| MVCC | 多版本并发控制,历史版本可查询 |
| 事务 | 支持 if-then-else 原子比较与操作 |
| gRPC API | 高性能、多语言支持的通信协议 |
Raft 共识算法
🔬 深入原理
etcd 使用 Raft 协议保证集群内各节点数据一致。Raft 将共识问题分解为三个子问题:
核心机制
| 概念 | 说明 |
|---|---|
| Leader 选举 | 集群始终有一个 Leader 处理所有写请求。Leader 定期发心跳(--heartbeat-interval,默认 100ms),Follower 超时未收到心跳则发起选举。 |
| 日志复制 | 客户端的写请求由 Leader 写入本地日志,再复制给所有 Follower。大多数节点确认后即提交(quorum = N/2 + 1)。 |
| 安全性 | 只有拥有最新已提交日志的节点才能成为 Leader;已提交的条目一定不会被覆盖。 |
读请求处理
| 读取方式 | 说明 | 一致性 |
|---|---|---|
| Serializable | 读取本地状态,不保证全局最新 | 可能读到旧数据 |
| Linearizable(默认) | 经 Leader 确认后返回,保证读到最新已提交数据 | 强一致 |
⚡ 性能提示:单次 Linearizable 读需要与 Leader 通信确认,延迟约 1-2ms(局域网)。高吞吐场景可考虑
WithSerializable()降级读取(适合监控指标等容忍短暂不一致的场景)。
Raft 时间参数
心跳间隔 (heartbeat-interval) = 100ms
选举超时 (election-timeout) = 1000ms (推荐 ≥ 5 × 心跳间隔)
故障检测时间 ≈ election-timeout(约 1s 内完成 Leader 切换)MVCC 与 Revision
🔬 深入原理
etcd 使用多版本并发控制(MVCC)存储每个 Key 的所有历史版本。
Revision 体系
etcd 有两个逻辑时钟:
| 概念 | 类型 | 说明 |
|---|---|---|
| Main Revision | int64,全局单调递增 | 整个数据库的全局计数器,任何 Key 的变更都会使其 +1 |
| Sub Revision | int64 | 同一事务(Txn)中多个操作共享同一个 Main Revision,Sub Revision 在事务内递增 |
全局时间线(Main Revision):
1: put /foo "a"
2: put /bar "x"
3: put /foo "b" ← 新 revision
4: del /bar
5: txn { put /a "1", put /b "2" } ← 两个操作用同一个 revKey 版本索引
每个 Key 的历史变更记录为 (main_rev, sub_rev) → value 的映射:
/foo 的版本历史:
(1,0) → "a"
(3,0) → "b"
── 当前最新 ──
/bar 的版本历史:
(2,0) → "x"
(4,0) → (tombstone)💡 最佳实践:所有修改操作返回当前 revision,Watch 和 Get 可指定
WithRev(n)读取历史版本快照。这是实现"监听不丢事件"的基础——从上次已知 revision+1 开始 watch。
压缩(Compaction)
历史版本不会自动清理。空间会持续增长,需要在合适时机压缩:
# 保留最近 1000 个 revision
etcdctl compact 1000
# 自动压缩(推荐配置)
etcd --auto-compaction-mode=periodic --auto-compaction-retention=1h🚨 陷阱:压缩后,revision ≤ 指定值的所有历史版本被清理。Watch 若从已被压缩的 revision 恢复,会收到
ErrCompacted错误,必须重新全量获取当前状态后再 watch。
数据模型
Key 空间
etcd 的 Key 空间是一棵扁平、有序的字节序列 B-Tree:
- Key 是任意字节序列(通常是字符串),按字典序排列
- 没有目录概念,但利用字典序可模拟层级结构:
/app/config/db,/app/config/cache,/app/service/gateway - 支持前缀查询、范围查询(
WithPrefix,WithFromKey,WithRange(end))
Key 空间示意(字典序):
/app/config/db
/app/config/cache
/app/service/gateway
/app/service/worker
/registry/host1
/registry/host2Value
- 任意字节序列,无 schema,无大小限制(默认单请求最大 1.5MB)
- etcd 不解析 Value 内容,序列化/反序列化由应用层负责
Key 命名推荐
| 模式 | 示例 | 适用场景 |
|---|---|---|
| 层级路径 | /service/user/api/host1:8080 |
服务发现 |
| 配置路径 | /config/db/max_conns |
配置中心 |
| UUID 做 Key | /locks/uuid-1234 |
分布式锁(避免碰撞) |
gRPC API 总览
etcd v3 使用 gRPC 协议,核心服务如下:
| 服务 | 主要 RPC | 说明 |
|---|---|---|
| KV | Put, Range, DeleteRange, Txn, Compact |
键值 CRUD 与事务 |
| Watch | Watch(双向流) |
监听 Key 变更 |
| Lease | LeaseGrant, LeaseRevoke, LeaseKeepAlive(双向流), LeaseTimeToLive |
租约管理 |
| Cluster | MemberAdd, MemberRemove, MemberUpdate, MemberList, MemberPromote |
集群成员管理 |
| Auth | AuthEnable, AuthDisable, UserAdd, UserDelete, RoleAdd, RoleGrantPermission |
认证与 RBAC |
| Maintenance | Alarm, Status, Defragment, Hash, Snapshot, MoveLeader |
运维与管理 |
Go 客户端对应关系
cli, _ := clientv3.New(clientv3.Config{Endpoints: []string{"localhost:2379"}})
kv := clientv3.NewKV(cli) // KV 服务
watcher := clientv3.NewWatcher(cli) // Watch 服务
lease := clientv3.NewLease(cli) // Lease 服务
cluster := clientv3.NewCluster(cli) // Cluster 服务
mainten := clientv3.NewMaintenance(cli) // Maintenance 服务Lease 租约机制概述
etcd 中 Key 可绑定租约(Lease)。租约到期后,绑定的所有 Key 自动删除。
┌─────────────────────────────────┐
│ Lease (ID, TTL) │
│ ├── /service/host1 "10.0.0.1"│
│ ├── /service/host2 "10.0.0.2"│
│ └── /locks/mylock "owner1" │
└─────────────────────────────────┘
若未续约(KeepAlive),TTL 到期
后所有 Key 自动删除典型用途:
- 服务健康检查:服务注册时绑定 Lease,定期续约;服务宕机则 Lease 到期,Key 自动清理
- 分布式锁超时保护:锁绑定 Lease,防止持锁者崩溃后锁永不被释放
详见
05-租约与保活.md
Watch 监听机制概述
Watch 是 etcd 的核心特性之一:客户端可创建一个 Watcher 监听某 Key 或 Key 范围的变化,服务端实时推送事件。
Client Server
│ ─ Watch(/app/**) ─ │
│ ← Create event │ (key=/app/config, value=xxx)
│ ← Update event │ (key=/app/config, value=yyy)
│ ← Delete event │ (key=/app/config)关键特性:
- 基于 revision 的顺序通知,不丢事件
- 支持前缀/范围监听
- 支持从指定 revision 恢复监听
- 每个 Watcher 本质上是一个 gRPC 双向流
详见
06-监听机制.md
整体架构
┌─────────────────────────────────────────────────┐
│ clientv3 │
│ (KV / Watch / Lease / Cluster / Maintenance) │
├─────────────────────────────────────────────────┤
│ gRPC (HTTP/2) │
├──────────────┬──────────────────┬────────────────┤
│ etcd-01 │ etcd-02 │ etcd-03 │
│ (Leader) │ (Follower) │ (Follower) │
│ │ │ │
│ ┌─────────┐ │ ┌─────────┐ │ ┌─────────┐ │
│ │ Raft │◄├──┤ Raft │◄────├──┤ Raft │ │
│ │ State │ │ │ State │ │ │ State │ │
│ └────┬────┘ │ └────┬────┘ │ └────┬────┘ │
│ │ │ │ │ │ │
│ ┌────▼────┐ │ ┌────▼────┐ │ ┌────▼────┐ │
│ │ MVCC │ │ │ MVCC │ │ │ MVCC │ │
│ │ (BoltDB)│ │ │ (BoltDB)│ │ │ (BoltDB)│ │
│ └─────────┘ │ └─────────┘ │ └─────────┘ │
│ │ │ │
│ WAL + Snap │ WAL + Snap │ WAL + Snap │
└──────────────┴──────────────────┴────────────────┘- Raft 层:WAL(预写日志)+ Snapshot(快照)保证持久化和恢复
- MVCC 层:BoltDB 存储所有 Key 的历史版本
- gRPC 层:对外提供 API 服务
常见陷阱
| 陷阱 | 说明 |
|---|---|
| 🚨 Compact 后 watch 断连 | 从已压缩的 revision watch 会收到 ErrCompacted,必须先全量同步 |
| 🚨 默认大小限制 | 单请求默认最大 1.5MB,大 value 需要调整 --max-request-bytes |
| 🚨 Lease 到期 ≠ Key 立刻删除 | 存在微小延迟,不应依赖精确的到期时刻来协调流程 |
| 🚨 Serializable 读是旧数据 | 非强一致读可能返回过期数据,关键判断必须用 Linearizable 读 |