etcd-00-总览
0. 摘要
项目目标与问题域
etcd 是一个分布式、可靠的键值存储系统,用于存储分布式系统中最关键的数据。其核心目标包括:
- 强一致性保证:基于 Raft 共识协议实现分布式一致性
- 高可用性:支持集群部署,自动故障转移
- 数据可靠性:所有数据持久化存储,支持快照和增量备份
- 简单易用的 API:提供 gRPC 和 HTTP/JSON API
- Watch 机制:支持实时监听键值变化
核心能力边界
支持的功能:
- 键值存储的 CRUD 操作
- 事务(Transaction)操作
- Watch 监听机制
- 租约(Lease)管理
- 用户认证与权限控制
- 集群成员管理
- 数据版本管理(MVCC)
非目标:
- 大规模数据存储(适合存储配置数据,不适合大数据场景)
- 高吞吐量写入(受限于 Raft 共识协议)
- 复杂查询(不支持 SQL 或复杂的查询语言)
运行环境与部署形态
- 语言:Go 1.21+
- 依赖:bbolt(持久化存储)、Raft 共识库
- 部署形态:
- 集群模式:3/5/7 节点集群(推荐奇数节点)
- 单节点模式:开发测试环境
- 通信协议:
- 客户端通信:gRPC(默认端口 2379)
- 节点间通信:HTTP(默认端口 2380)
1. 整体架构图
flowchart TB
subgraph Client["客户端层"]
C1[etcdctl CLI]
C2[Go Client SDK]
C3[HTTP/gRPC Client]
end
subgraph APILayer["API 层"]
GRPC[gRPC Server<br/>端口 2379]
HTTP[HTTP Gateway]
end
subgraph ServerCore["Server 核心层"]
EtcdServer[EtcdServer<br/>请求路由与协调]
Apply[Apply 层<br/>请求应用]
end
subgraph Consensus["共识层"]
RaftNode[Raft Node<br/>共识协议]
WAL[WAL 日志]
Snapshot[Snapshot]
end
subgraph Storage["存储层"]
MVCC[MVCC Store<br/>多版本并发控制]
TreeIndex[TreeIndex<br/>内存索引]
Backend[BoltDB Backend<br/>持久化存储]
end
subgraph Support["支撑模块"]
Lease[Lease 租约管理]
Auth[Auth 认证授权]
Watch[Watch 监听]
Alarm[Alarm 告警]
end
subgraph Cluster["集群管理"]
Membership[Membership<br/>成员管理]
RaftHTTP[Raft HTTP Transport<br/>节点间通信 2380]
end
C1 --> GRPC
C2 --> GRPC
C3 --> HTTP
HTTP --> GRPC
GRPC --> EtcdServer
EtcdServer --> RaftNode
RaftNode --> WAL
RaftNode --> Snapshot
RaftNode -->|提案| Apply
Apply --> MVCC
MVCC --> TreeIndex
MVCC --> Backend
MVCC --> Lease
MVCC --> Watch
EtcdServer --> Auth
EtcdServer --> Alarm
EtcdServer --> Membership
RaftNode <--> RaftHTTP
RaftHTTP <-.->|Raft 消息| RaftHTTP
style Client fill:#e1f5ff
style APILayer fill:#fff4e6
style ServerCore fill:#f3e5f5
style Consensus fill:#e8f5e9
style Storage fill:#fce4ec
style Support fill:#fff9c4
style Cluster fill:#e0f2f1
架构说明
分层职责
-
客户端层
- 提供多种客户端接入方式(CLI、SDK、HTTP)
- 负责请求序列化和响应反序列化
- 实现连接池、重试、负载均衡等客户端逻辑
-
API 层
- gRPC Server 处理客户端请求,提供 6 大服务(KV、Watch、Lease、Cluster、Auth、Maintenance)
- HTTP Gateway 提供 RESTful API(通过 grpc-gateway 自动生成)
- 请求认证和权限校验
-
Server 核心层
- EtcdServer 作为请求路由中心,协调各模块工作
- 区分读请求(直接查询 MVCC)和写请求(通过 Raft 共识)
- Apply 层负责将已提交的 Raft 日志应用到状态机
-
共识层
- Raft Node 实现 Raft 共识算法,保证集群一致性
- WAL(Write-Ahead Log)持久化 Raft 日志
- Snapshot 用于日志压缩和快速恢复
-
存储层
- MVCC Store 实现多版本并发控制
- TreeIndex 维护键到版本的内存索引(B-Tree)
- BoltDB Backend 提供持久化存储(B+树)
-
支撑模块
- Lease 管理键的租约,支持自动过期
- Auth 提供用户认证和 RBAC 权限控制
- Watch 实现键值变化的实时监听
- Alarm 监控集群健康状态(空间不足、数据损坏)
-
集群管理
- Membership 管理集群成员信息
- Raft HTTP Transport 处理节点间 Raft 消息传输
数据流与控制流
写请求流程:
- 客户端 → gRPC Server
- gRPC Server → EtcdServer (认证、权限检查)
- EtcdServer → Raft Node (提交提案)
- Raft Node → WAL (持久化日志)
- Raft Node → 其他节点(复制日志)
- 领导者收到多数派确认 → Apply 层
- Apply 层 → MVCC Store(应用到状态机)
- MVCC Store → Backend(持久化)
- 返回结果给客户端
读请求流程:
- 线性化读(默认):需要等待 Raft 确认当前节点仍是领导者
- 串行化读:直接从本地 MVCC 读取(可能读到旧数据)
跨进程/线程通信
- gRPC 通信:客户端与服务器之间
- HTTP 通信:节点间 Raft 消息传输
- Channel 通信:Go routine 之间传递消息(apply channel、watch channel)
- 共享内存:TreeIndex、MVCC Store 在同一进程内共享
高可用与扩展性
- 高可用:通过 Raft 实现,容忍 (N-1)/2 个节点故障
- 状态管理:每个节点维护完整状态副本
- 扩展性限制:
- 写入受限于 Raft 共识开销
- 推荐集群规模 3-7 节点
- 单个键值大小推荐 < 1.5MB
2. 全局时序图
2.1 写请求主流程
sequenceDiagram
autonumber
participant Client as 客户端
participant GRPC as gRPC Server
participant ES as EtcdServer
participant Auth as AuthStore
participant Raft as Raft Node
participant WAL as WAL
participant Peer as Peer Nodes
participant Apply as Apply 层
participant MVCC as MVCC Store
participant BE as Backend
Client->>GRPC: Put(key, value)
GRPC->>ES: 路由请求
ES->>Auth: 权限检查
Auth-->>ES: 权限通过
ES->>Raft: 提交 Raft 提案
Raft->>WAL: 持久化日志
WAL-->>Raft: 持久化完成
Raft->>Peer: AppendEntries RPC
Peer-->>Raft: 多数派确认
Raft->>Apply: 提交已提交条目
Apply->>MVCC: 写入键值
MVCC->>BE: 持久化到 BoltDB
BE-->>MVCC: 持久化完成
MVCC-->>Apply: 写入完成
Apply-->>ES: 返回结果
ES-->>GRPC: 返回响应
GRPC-->>Client: PutResponse
关键点说明:
- 入口与认证:客户端通过 gRPC 发起请求,经过 AuthStore 权限检查
- 幂等性:Raft 日志使用单调递增的 index,保证幂等性
- 回退策略:
- Raft 提案失败:返回错误,客户端重试
- WAL 写入失败:节点 panic,需要恢复
- 多数派未确认:超时返回错误
- 重试点:客户端在收到错误后可重试,EtcdServer 会去重
- 超时设置:
- 客户端超时:默认 5 秒
- Raft 选举超时:1 秒
- 心跳超时:100ms
- 资源上界:
- 单次请求大小:1.5MB
- 并发请求数:受限于 gRPC 连接数
2.2 读请求主流程(线性化读)
sequenceDiagram
autonumber
participant Client as 客户端
participant GRPC as gRPC Server
participant ES as EtcdServer
participant Raft as Raft Node
participant MVCC as MVCC Store
Client->>GRPC: Range(key)
GRPC->>ES: 路由请求
ES->>Raft: ReadIndex 请求
Note over Raft: 确认领导者身份
Raft-->>ES: ReadIndex 确认
ES->>MVCC: 读取键值
MVCC-->>ES: 返回结果
ES-->>GRPC: 返回响应
GRPC-->>Client: RangeResponse
说明:
- 线性化读需要通过 ReadIndex 确认领导者身份
- 串行化读(Serializable=true)直接从本地读取,无需 ReadIndex
2.3 Watch 监听流程
sequenceDiagram
autonumber
participant Client as 客户端
participant GRPC as gRPC Server
participant WS as WatchStream
participant WatchableStore as WatchableStore
participant MVCC as MVCC Store
Client->>GRPC: Watch(key, rev)
GRPC->>WS: 创建 Watcher
WS->>WatchableStore: 注册 Watcher
Note over WatchableStore: 检查 rev 是否已过期
alt rev <= currentRev
WatchableStore->>WS: 同步历史事件
WS->>Client: 历史 WatchResponse
end
Note over MVCC: 有新的 Put/Delete 操作
MVCC->>WatchableStore: 通知事件
WatchableStore->>WS: 推送事件
WS->>Client: 实时 WatchResponse
3. 模块边界与交互图
3.1 模块清单
| 序号 | 模块名称 | 目录路径 | 主要职责 | 对外 API |
|---|---|---|---|---|
| 01 | API 层 | api/etcdserverpb |
Protocol Buffers 定义 | gRPC 服务定义 |
| 02 | EtcdServer | server/etcdserver |
核心协调逻辑 | Server 接口 |
| 03 | MVCC 存储 | server/storage/mvcc |
多版本存储引擎 | KV、TxnRead、TxnWrite |
| 04 | Lease 租约 | server/lease |
租约管理 | Lessor 接口 |
| 05 | Auth 认证 | server/auth |
用户认证授权 | AuthStore 接口 |
| 06 | Raft 共识 | 外部依赖 go.etcd.io/raft |
Raft 协议实现 | Node 接口 |
| 07 | Backend 存储 | server/storage/backend |
BoltDB 封装 | Backend 接口 |
| 08 | Watch 监听 | server/storage/mvcc |
键值变化监听 | WatchableKV 接口 |
| 09 | Membership | server/etcdserver/api/membership |
集群成员管理 | RaftCluster 接口 |
| 10 | Client SDK | client/v3 |
Go 客户端库 | Client API |
3.2 模块交互矩阵
| 调用方 ↓ \ 被调方 → | EtcdServer | MVCC | Lease | Auth | Raft | Backend | Watch |
|---|---|---|---|---|---|---|---|
| gRPC Server | 同步调用 | - | - | - | - | - | - |
| EtcdServer | - | 同步调用 | 同步调用 | 同步调用 | 同步调用 | - | - |
| Apply 层 | - | 同步调用 | 同步调用 | 同步调用 | - | - | - |
| MVCC | - | - | 同步调用 | - | - | 同步调用 | 异步通知 |
| Lease | - | 同步调用 | - | - | - | 同步调用 | - |
| Auth | - | - | - | - | - | 同步调用 | - |
| Watch | - | 同步调用 | - | - | - | - | - |
交互说明:
- 同步调用:直接函数调用,阻塞等待返回
- 异步通知:通过 Channel 或回调机制
- 错误语义:统一使用 error 返回,错误码在 gRPC 层映射为标准错误
- 一致性要求:所有写操作通过 Raft 保证强一致性
4. 关键设计与权衡
4.1 一致性模型
强一致性(Linearizability):
- 实现方式:基于 Raft 共识协议
- 保证:所有读写操作看起来像是原子地、按某个顺序发生
- 代价:写入延迟高(需要多数派确认),读取需要 ReadIndex 确认
最终一致性(Serializable):
- 实现方式:直接从本地 MVCC 读取
- 保证:可能读到旧数据,但保证单调性(不会回退)
- 优势:读取延迟低,吞吐量高
4.2 事务与并发控制
MVCC(Multi-Version Concurrency Control):
- 每次修改产生新版本(Revision)
- 使用
{Main, Sub}表示版本:Main:全局单调递增的事务 IDSub:同一事务内的子版本
- 支持快照隔离(Snapshot Isolation)
事务(Transaction):
- 基于 Compare-And-Set (CAS) 语义
- 支持条件判断(Compare)+ 成功/失败分支(Success/Failure)
- 事务内禁止多次修改同一个键
锁与并发:
- 写锁:通过 Raft 序列化写操作,天然互斥
- 读写锁:MVCC Store 使用
sync.RWMutex - 无锁读:串行化读无需加锁
4.3 性能关键路径
写入路径(P99 延迟):
- gRPC 处理:< 1ms
- Raft 提案 + WAL 写入:10-50ms(取决于磁盘)
- 多数派网络往返:5-20ms
- Apply 应用:< 1ms
- BoltDB 提交:5-10ms
总延迟:约 20-80ms
读取路径(P99 延迟):
- 线性化读:10-30ms(需要 ReadIndex)
- 串行化读:< 1ms
内存峰值:
- TreeIndex:约占键数量 * 100 字节
- WatchableStore:每个 Watcher 约 1KB
- BoltDB mmap:取决于数据库大小
I/O 热点:
- WAL 顺序写(每次写请求)
- BoltDB B+树更新(批量提交)
- Snapshot 文件(定期)
4.4 可观测性
关键指标:
etcd_server_proposals_committed_total:已提交提案数etcd_server_proposals_failed_total:失败提案数etcd_disk_wal_fsync_duration_seconds:WAL fsync 延迟etcd_disk_backend_commit_duration_seconds:Backend 提交延迟etcd_mvcc_db_total_size_in_bytes:数据库大小etcd_network_peer_round_trip_time_seconds:节点间 RTT
分布式追踪:
- 支持 OpenTelemetry
- 可追踪完整请求链路
4.5 配置项
关键配置:
--data-dir:数据目录--listen-client-urls:客户端监听地址(默认 http://localhost:2379)--listen-peer-urls:节点间通信地址(默认 http://localhost:2380)--initial-cluster:初始集群配置--initial-cluster-state:集群状态(new/existing)--snapshot-count:快照触发阈值(默认 10000 条日志)--quota-backend-bytes:后端数据库配额(默认 2GB)--max-request-bytes:单次请求最大大小(默认 1.5MB)--heartbeat-interval:心跳间隔(默认 100ms)--election-timeout:选举超时(默认 1000ms)
5. 典型使用示例与最佳实践
5.1 示例 1:基本 Put/Get 操作
package main
import (
"context"
"log"
"time"
clientv3 "go.etcd.io/etcd/client/v3"
)
func main() {
cli, err := clientv3.New(clientv3.Config{
Endpoints: []string{"localhost:2379"},
DialTimeout: 5 * time.Second,
})
if err != nil {
log.Fatal(err)
}
defer cli.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Put 操作
_, err = cli.Put(ctx, "/config/app/db_host", "192.168.1.100")
if err != nil {
log.Fatal(err)
}
// Get 操作
resp, err := cli.Get(ctx, "/config/app/db_host")
if err != nil {
log.Fatal(err)
}
for _, ev := range resp.Kvs {
log.Printf("Key: %s, Value: %s, Revision: %d", ev.Key, ev.Value, ev.ModRevision)
}
}
适用场景:配置存储、服务发现
5.2 示例 2:分布式锁
package main
import (
"context"
"log"
"time"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/concurrency"
)
func main() {
cli, _ := clientv3.New(clientv3.Config{
Endpoints: []string{"localhost:2379"},
DialTimeout: 5 * time.Second,
})
defer cli.Close()
// 创建 Session(自动续租)
session, _ := concurrency.NewSession(cli, concurrency.WithTTL(10))
defer session.Close()
// 创建互斥锁
mutex := concurrency.NewMutex(session, "/my-lock/")
// 加锁
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := mutex.Lock(ctx); err != nil {
log.Fatal(err)
}
log.Println("获取锁成功")
// 执行业务逻辑
time.Sleep(2 * time.Second)
// 释放锁
if err := mutex.Unlock(context.Background()); err != nil {
log.Fatal(err)
}
log.Println("释放锁成功")
}
适用场景:分布式互斥、Leader 选举
5.3 示例 3:Watch 监听
package main
import (
"context"
"log"
clientv3 "go.etcd.io/etcd/client/v3"
)
func main() {
cli, _ := clientv3.New(clientv3.Config{
Endpoints: []string{"localhost:2379"},
})
defer cli.Close()
// Watch 指定前缀
watchChan := cli.Watch(context.Background(), "/config/app/", clientv3.WithPrefix())
for watchResp := range watchChan {
for _, event := range watchResp.Events {
log.Printf("Event Type: %s, Key: %s, Value: %s, Revision: %d",
event.Type, event.Kv.Key, event.Kv.Value, event.Kv.ModRevision)
}
}
}
适用场景:配置热更新、事件监听
5.4 最佳实践
部署架构:
- 生产环境使用 3 或 5 节点集群
- 跨机房部署需考虑网络延迟
- 使用 SSD 存储 WAL 和数据目录
性能优化:
- 批量操作使用 Txn
- 读多写少场景使用串行化读
- 控制单个键值大小 < 1MB
- 定期压缩历史版本(Compact)
故障排查:
- 检查磁盘 I/O 延迟(WAL fsync)
- 检查网络延迟(节点间 RTT)
- 监控数据库大小是否接近配额
- 查看 Raft 日志确认同步状态
容量规划:
- 每秒写入 < 10000 次
- 键值总数 < 100 万
- 数据库大小 < 8GB
- Watch 数量 < 10000
6. 系统级时序图
6.1 场景 1:冷启动
sequenceDiagram
autonumber
participant Main as Main
participant Embed as Embed
participant ES as EtcdServer
participant Raft as Raft Node
participant MVCC as MVCC Store
participant BE as Backend
Main->>Embed: StartEtcd(cfg)
Embed->>BE: Open BoltDB
BE-->>Embed: Backend 就绪
Embed->>MVCC: Restore from Backend
MVCC->>BE: 读取所有键值
BE-->>MVCC: 返回键值数据
MVCC->>MVCC: 重建 TreeIndex
MVCC-->>Embed: MVCC 就绪
Embed->>Raft: Restart Raft Node
Raft->>Raft: 读取 WAL 日志
Raft->>Raft: 读取 Snapshot
Raft-->>Embed: Raft 就绪
Embed->>ES: NewServer(cfg)
ES->>ES: 初始化各模块
ES-->>Embed: Server 就绪
Embed->>ES: Start()
ES->>ES: 启动后台 goroutines
ES->>Raft: 启动 Raft 循环
Note over ES: 等待选举出 Leader
Raft-->>ES: 成为 Leader/Follower
ES-->>Embed: 服务就绪
说明:
- 冷启动需要从 BoltDB 恢复完整状态
- Raft 从 WAL 和 Snapshot 恢复日志
- 启动后需要等待选举完成才能提供服务
6.2 场景 2:Leader 切换
sequenceDiagram
autonumber
participant C as 客户端
participant OldLeader as 旧 Leader
participant NewLeader as 新 Leader
participant F1 as Follower 1
participant F2 as Follower 2
Note over OldLeader: Leader 失联
F1->>F2: RequestVote
F2->>F1: VoteGranted
F1->>NewLeader: RequestVote
NewLeader->>F1: VoteGranted
Note over F1: 获得多数票,成为 Leader
F1->>F1: 转换为 Leader
F1->>NewLeader: AppendEntries (心跳)
F1->>F2: AppendEntries (心跳)
C->>OldLeader: Put(key, value)
OldLeader-->>C: ErrNotLeader (重定向到新 Leader)
C->>F1: Put(key, value)
F1->>NewLeader: AppendEntries
F1->>F2: AppendEntries
NewLeader-->>F1: Success
F2-->>F1: Success
F1->>F1: 应用日志
F1-->>C: PutResponse
说明:
- Leader 失联后,Follower 发起选举
- 新 Leader 选出后,旧请求会被拒绝
- 客户端需要重定向到新 Leader
6.3 场景 3:数据库满触发告警
sequenceDiagram
autonumber
participant Apply as Apply 层
participant MVCC as MVCC Store
participant BE as Backend
participant Alarm as AlarmStore
participant ES as EtcdServer
Apply->>MVCC: Put(key, value)
MVCC->>BE: 写入 BoltDB
BE->>BE: 检查数据库大小
alt 数据库满
BE-->>MVCC: ErrNoSpace
MVCC-->>Apply: ErrNoSpace
Apply->>Alarm: Activate(NOSPACE)
Alarm->>BE: 持久化告警
BE-->>Alarm: 持久化完成
Alarm-->>Apply: 告警已激活
Apply-->>ES: 通知告警
Note over ES: 拒绝所有写请求
end
Note over ES: 管理员介入,清理数据或扩容
ES->>Alarm: Deactivate(NOSPACE)
Alarm->>BE: 清除告警
BE-->>Alarm: 清除完成
Note over ES: 恢复写入服务
说明:
- 数据库达到配额后触发 NOSPACE 告警
- 所有写请求被拒绝,保护集群稳定性
- 需要管理员清理数据或调整配额
7. 文档索引
模块详细文档
参考文档
etcd 核心链路与生产实践
线性一致读链路(Range/ReadIndex)
-
一致性策略:
linearizable(默认):发起 ReadIndex 心跳仲裁,确保本地读取不落后于集群多数派提交点,牺牲少量延迟换取线性一致性。serializable:本地读,无需跨节点心跳,延迟低但可能读到旧值(主从切换场景)。
-
聚合优化:
linearizableReadLoop协程聚合多个线性读请求,批量触发一次 ReadIndex,减少心跳风暴。 -
ReadIndex 机制:
- Leader 检查
raft.committed > raft.applied,等待 apply 追平后返回readIndex。 - Follower 向 Leader 发送
MsgReadIndex,Leader 确认自身为 Leader 后返回。 - 心跳确认:Leader 发送心跳广播,收到多数派响应后确认读索引有效。
- Leader 检查
-
MVCC 读取:根据
readIndex确定读取视图版本,从treeIndex查找键的版本历史,再从 BoltDB 读取实际数据。
写入全链路(Put)
-
入队前置检查:
- 背压保护:
commitIndex - appliedIndex超限即拒绝,避免磁盘慢导致内存被未应用的 Entry 撑爆。 - 大小限制:超过
MaxRequestBytes直接拒绝,防止大 value 拖垮复制、WAL、快照。
- 背压保护:
-
Raft 提案与追加:
- Leader 收到
MsgProp后追加到本地 Raft Log,发送MsgApp到 Follower。 - Follower 追加日志后返回
MsgAppResp,Leader 收到多数派响应后推进committedIndex。
- Leader 收到
-
WAL 持久化:日志先写入 WAL 再追加到内存 Raft Log,通过
fdatasync确保持久化。 -
Apply 应用:
- 定期检查
committedIndex > appliedIndex,批量应用已提交的 Entry。 - 调用
MVCC.Put写入数据,生成新版本号,更新treeIndex索引。 - 触发 Watch 事件派发,通知相关 Watcher。
- 定期检查
-
Backend 提交:MVCC 操作在 BoltDB 事务中执行,定期批量提交减少 fsync 开销。
Watch 机制
-
双循环模型:
recvLoop:处理客户端上行请求(创建/取消/进度请求)。sendLoop:下发变更事件和控制回应,控制面(ctrlStream)与数据面(watchStream.Chan)分离。
-
创建与取消:
- 创建时指定
StartRevision(默认为当前wsrev + 1),若低于压缩点返回ErrCompacted。 - 取消时清理 watcher 的进度、碎片、prevKV 跟踪。
- 创建时指定
-
事件派发:
- MVCC Apply 后将事件写入
watchableStore的通知通道。 syncWatchersLoop定期将事件批量推送到匹配的 Watcher。- 历史事件补偿:对
StartRevision低于当前版本的 Watcher,从 BoltDB 扫描历史版本补发。
- MVCC Apply 后将事件写入
-
进度通知:定期发送
ProgressNotify防止客户端超时,携带当前集群 revision。 -
碎片控制:单个 Watcher 积压事件过多时拆分成多个
WatchResponse,设置Fragment: true。
gRPC API 调用链路
-
KV 服务:
Range:经过 Auth 校验、配额检查、读写控制、MVCC 读取、结果限制。Put:经过 Auth 校验、Raft 提案、WAL 持久化、MVCC 写入、Watch 触发、Backend 提交。Txn:支持多个操作的原子执行,If-Then-Else 语义,所有操作在同一 MVCC 事务中。
-
Lease 服务:
LeaseGrant:创建租约并设置 TTL,返回租约 ID。LeaseKeepAlive:客户端发送心跳续约,服务端返回剩余 TTL。LeaseRevoke:删除租约及其关联的所有键。
-
Watch 服务:双向流式 RPC,一个连接可承载多个 Watch,支持历史事件补偿和进度通知。
-
Cluster 服务:成员管理(添加/删除/更新/列出/提升),通过 Raft 配置变更实现。
-
Auth 服务:用户/角色管理、权限授予/撤销、JWT Token 认证。
-
Maintenance 服务:告警管理、状态查询、碎片整理、快照、Leader 迁移。
生产部署最佳实践
-
集群规模:推荐奇数节点(3/5/7),Raft 需要多数派确认,偶数节点无法提供更好的容错性。
-
硬件配置:
- 小型集群(< 1000 客户端):2-4 核 CPU,8GB 内存,SSD 50GB+。
- 中型集群(1000-5000 客户端):4-8 核 CPU,16GB 内存,SSD 100GB+。
- 磁盘要求:SSD 必备,WAL 和数据目录分离,磁盘 IOPS 直接影响写入延迟。
-
配额与限流:
- 设置合理的
quota-backend-bytes(默认 2GB),达到配额后触发 NOSPACE 告警拒绝写入。 - 客户端请求大小限制
max-request-bytes(默认 1.5MB)。 - 单个事务操作数限制
max-txn-ops(默认 128)。
- 设置合理的
-
快照与压缩:
- 定期自动快照(
snapshot-count: 10000),减少 WAL 恢复时间。 - 定期自动压缩(
auto-compaction-retention),清理历史版本,防止数据库无限膨胀。 - 压缩后触发碎片整理(
Defragment),回收物理空间。
- 定期自动快照(
-
网络调优:
- 节点间 RTT < 10ms 为宜,跨区域部署需评估网络延迟对写入的影响。
- 心跳间隔(
heartbeat-interval: 100ms)和选举超时(election-timeout: 1000ms)根据网络条件调整。 - 启用 gRPC 连接复用和 HTTP/2 流控。
-
监控告警:
- 关键指标:Leader 变更频率、Raft Proposal 失败率、Apply 延迟、磁盘 fsync 延迟、Backend 提交延迟、Watch 事件积压。
- 告警阈值:磁盘 fsync > 25ms、Backend 提交 > 100ms、Apply 滞后 > 1000、NOSPACE 告警。
- Metrics 端点:
/metrics导出 Prometheus 格式指标。
性能优化要点
-
读优化:
- 非强一致场景使用
serializable读,避免 ReadIndex 开销。 - 客户端缓存热点数据,减少请求频率。
- 使用 Range 的
limit和keys_only减少数据传输。
- 非强一致场景使用
-
写优化:
- 批量写入:使用 Txn 合并多个 Put 操作。
- 调整 Backend 批量间隔(
backend-batch-interval)和批量限制(backend-batch-limit),平衡延迟和吞吐。 - 避免大 value 写入,单个键值建议 < 1MB。
-
Watch 优化:
- 精准过滤:使用前缀匹配减少不必要的事件推送。
- 避免 Watch 大量键或频繁创建/取消 Watch。
- 客户端及时消费事件,避免服务端积压触发碎片分片。
-
存储优化:
- 定期压缩历史版本(保留策略:时间窗口或版本数)。
- 定期碎片整理回收空间(需要在业务低峰期执行)。
- 独立 WAL 目录到高性能磁盘。
故障排查关键点
-
脑裂检测:检查 Leader 选举日志,确认网络分区恢复后旧 Leader 自动降级。
-
慢查询定位:通过 Metrics 分析 MVCC Range 延迟、BoltDB Tx 延迟、Raft Apply 延迟,定位瓶颈层。
-
数据不一致:使用
HashKV比对各节点数据哈希,Snapshot+ 恢复修复不一致节点。 -
NOSPACE 恢复:清理历史数据(Compact + Defragment)或扩容配额(调整
quota-backend-bytes需重启)。 -
Leader 频繁切换:检查网络延迟、磁盘 IO 延迟、节点负载,调整心跳和选举超时参数。