文章

etcd 学习总结:分布式键值存储的核心原理

简介

etcd 是一个分布式、可靠的键值存储系统,专为分布式系统中的关键数据设计。它提供简单性、安全性、高性能和可靠性,广泛应用于 Kubernetes 等生产环境,是现代微服务架构中不可或缺的组件。

核心特性

  1. 强一致性:基于 Raft 算法,确保集群中各节点的数据强一致性
  2. 高可用性:支持集群部署,当节点故障时自动切换
  3. 高性能:支持线性一致性读取和批量提交优化写入性能
  4. 简单易用:提供 gRPC API 和 HTTP API,易于集成
  5. 数据持久化:通过 WAL 日志和 boltdb,保证 crash 后数据不丢失

整体架构

etcd 的架构分为五层:

1. 客户端层

包含 client v2 和 v3 两个大版本 API 客户端库

2. API 网络层

  • Client 访问 Server:v2 API 采用 HTTP/1.x 协议,v3 API 采用 gRPC 协议
  • Server 间通信:节点间通过 Raft 算法实现数据复制和 Leader 选举时使用的 HTTP 协议

3. Raft 共识层

实现了 Leader 选举、日志复制、ReadIndex 等核心算法,是 etcd 的基石,保障多节点间的数据一致性

4. 功能逻辑层

包含 KVServer、MVCC、Auth 鉴权、Lease 租约、Compactor 压缩等核心模块

5. 存储层

  • WAL 模块:预写日志,保证 crash 后数据不丢失
  • Snapshot 快照:快速恢复状态机
  • boltdb:持久化存储集群元数据和用户数据

Raft 一致性算法

为什么需要 Raft?

在分布式系统中,多个节点需要对状态达成一致。Raft 算法通过选举一个 Leader 来管理复制日志,解决了 Paxos 算法的复杂性问题,提供更易理解和实现的共识机制。

Raft 的三个核心子问题

1. Leader 选举

三种节点角色

  • Leader:处理所有客户端请求,向 Follower 复制日志
  • Follower:被动接收来自 Leader 的日志
  • Candidate:竞选 Leader 的节点

选举过程

  1. 正常情况下,只有一个 Leader,其他为 Follower
  2. Leader 定期向 Follower 发送心跳消息维持身份
  3. 如果 Follower 在超时时间内未收到心跳,转变为 Candidate
  4. Candidate 发起选举,请求其他节点投票
  5. 获得多数投票的 Candidate 成为新的 Leader

任期(Term)管理

  • 使用 Term 作为逻辑时钟,每次选举时 Term 递增
  • 一个 Term 内最多只有一个 Leader
  • 确保选举的安全性

2. 日志复制

  • 所有客户端请求都由 Leader 处理,追加到日志中
  • Leader 并行向所有 Follower 发送 AppendEntries RPC
  • 当一半以上节点确认后,日志条目被标记为已提交(committed)
  • Leader 通知 Follower 提交日志,应用到状态机

日志匹配特性

  • Leader 强制 Follower 的日志与自己一致
  • 通过检查前一个日志条目的索引和 Term,确保日志完整性

3. 安全性

选举规则

  • Candidate 当其日志至少与任何其他节点一样新时,才能赢得选举。
  • 比较日志的最后一个条目的 Term 和 Index

日志复制规则

  • Leader 完全特性:如果一个日志条目在某个 Term 内被提交,之后的 Leader 必然包含这条日志
  • 只附加原则:Raft 日志只能追加,不能修改或删除

读写流程详解

写流程

1
2
3
4
5
6
7
8
9
10
11
12
13
Client 发起更新请求
    ↓
Leader 接收请求,持久化到 WAL 日志
    ↓
Leader 广播给各节点(AppendEntries RPC)
    ↓
一半以上节点持久化成功 → 日志标记为已提交
    ↓
etcdserver 异步从 Raft 模块获取已提交日志
    ↓
应用到状态机(boltdb 等)
    ↓
返回结果给 Client

性能优化

  • 批量提交:将多个请求合并成一个日志条目
  • WAL 异步持久化:提高写入吞吐量

读流程

串行读(数据敏感度低)

直接读状态机数据,无需与集群交互,特点是低延迟、高吞吐量。但可能读到旧数据,因为 Follower 应用日志到状态机是异步过程。

线性读(数据敏感度高)

使用 ReadIndex 机制:

  1. Leader 记录当前 commitIndex
  2. 向 Follower 发送心跳确认自己的 Leader 身份
  3. 确认后,按 commitIndex 读取状态机数据
  4. 保证读到最新数据

核心数据结构

MVCC(多版本并发控制)

etcd 使用 MVCC 来实现高效的并发读写:

  • treeIndex:内存中的 B+ 树索引,维护键值对的版本信息
  • boltdb:持久化存储实际的键值数据,支持事务

工作原理

  • 每次修改都产生新版本,旧版本保留以供读取
  • 读操作通过指定版本号读取,避免锁竞争
  • 定期压缩(Compaction)回收旧版本,节省空间

Lease(租约机制)

租约用于检测客户端是否存活,支持:

  • 创建租约:设置 TTL(生存时间)
  • 续约:通过定期发送心跳延长租约
  • 绑定键值对:租约过期时,关联的键值对自动删除

应用场景:分布式锁、服务注册和健康检查

Watch(事件监听)

etcd 提供 Watch 机制实现高效的事件推送:

  • Synced Watcher:从当前版本开始推送事件
  • Unsynced Watcher:历史事件重放后再推送新事件
  • 滑动窗口:避免推送过多历史事件

应用场景

1. 服务发现

  • 服务在 etcd 中注册,提供强一致性的服务存储目录
  • 支持健康检查和自动故障转移
  • 适用于微服务架构和 PaaS 平台

2. 配置管理

  • 集中管理应用配置,支持版本控制
  • 通过 Watch 机制实时推送配置变更
  • 支持滚动更新和灰度发布

3. 分布式锁

  • 利用 etcd 的强一致性特性实现分布式锁
  • 结合租约机制,支持自动超时释放

4. 服务选举

  • 使用租约和原子操作实现领导者选举
  • 自动故障转移

5. 分布式队列

  • 创建先进先出的任务队列
  • 确保任务按顺序执行

6. 负载均衡

  • 监控集群节点状态
  • 动态维护负载均衡节点表

Kubernetes 中的应用

etcd 在 Kubernetes 中扮演核心角色:

  • 集群状态存储:存储所有 K8s 对象(Pod、Service、Deployment 等)
  • 配置管理:维护 ConfigMap 和 Secret
  • 服务发现:Service 与 Pod 的映射关系
  • 状态监控:通过 Watch 机制实时同步集群状态变化

性能优化建议

  1. 集群规模:推荐 3-5 个节点,避免过多节点导致选举延迟
  2. 网络优化:确保节点间网络延迟低
  3. 存储优化
    • 定期执行 Compaction 回收空间
    • 监控数据库大小
  4. 读写优化
    • 大量读场景使用串行读
    • 关键数据使用线性读
  5. 监控告警:监控 Leader 选举频率、写入延迟等关键指标

常见问题

Q1: etcd 如何保证数据不丢失?

通过两种机制:

  • WAL 日志:所有操作在应用到状态机前先写入 WAL
  • Raft 多副本:数据复制到多个节点,任何单个节点故障都不会丢失数据

Q2: etcd 读旧数据怎么办?

使用线性读(ReadIndex)机制替代串行读,确保读到最新数据。

Q3: etcd 如何选择集群节点数?

  • 奇数节点更优(3、5、7 等)
  • 容错能力与故障转移速度需要平衡
  • 通常推荐 3 节点用于高可用,5 节点用于多数据中心

总结

etcd 通过 Raft 算法实现了一个可靠的分布式键值存储系统,其核心创新包括:

  1. Raft 算法:相比 Paxos 更易理解和实现
  2. MVCC 机制:高效的并发读写
  3. Lease 租约:优雅的健康检测
  4. Watch 事件流:实时数据推送

这些特性使 etcd 成为现代分布式系统的基础组件,特别是在 Kubernetes 生态中不可或缺。

学习资源

  • 官方文档:https://etcd.io/docs/
  • Raft 论文:https://raft.github.io/
  • etcd 源码:https://github.com/etcd-io/etcd
本文由作者按照 CC BY 4.0 进行授权