Skip to content
核心概念

核心概念

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" }  ← 两个操作用同一个 rev

Key 版本索引

每个 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/host2

Value

  • 任意字节序列,无 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 读