Raft:为可理解性而生的共识
本文基于模型知识整理(生成时未联网核对),关键结论建议对照经典文献复核。
一句话定义
Raft 把共识拆解为领导者选举、日志复制、安全性三个强约束子问题:唯一 leader 在任期内串行复制日志,多数派确认即提交,配合"日志新者当选"与"仅提交当前任期条目"两条规则保证不丢已确认数据。
为什么重要
Raft 是当下事实上的共识标准:etcd(Kubernetes 的大脑)、TiKV/TiDB、CockroachDB、Consul、RocketMQ/KRaft 全在其上。它证明了"可理解性"可以与正确性共存——比起 Paxos,它给工程界提供了一幅可以直接照着实现的蓝图。
前置知识
核心概念
- 三种角色:leader(唯一)、follower、candidate;任期(term)单调递增。
- 日志复制:客户端请求 → leader 追加日志 → 并行发 AppendEntries 给 followers → 多数派确认 → leader 应用并回复 → 心跳捎带提交位。
- 选举规则(选举限制):投票者只投给"日志至少和自己新"的 candidate(先比最后日志的 term,再比 index)⇒ 当选者必然拥有全部已提交日志。
- 提交规则:leader 只直接提交本任期内的条目;旧任期条目随本任期条目"顺带"提交(防止已复制的旧条目在新任期被覆盖)。
- 持久化三件套:currentTerm、votedFor、log[] 必须在应答 RPC 前落盘(fsync),否则崩溃恢复后违反安全性。
原理与机制
为什么两条提交/选举规则缺一不可:设想 leader 复制了旧任期条目但未提交就崩溃;新 leader 若"日志更旧"却当选,会把未提交的旧条目覆盖掉。规则一(日志新者当选)保证含已提交条目的节点不可能被更旧的 candidate 击败;规则二堵住另一个漏洞——leader 不能因为旧条目在多数派上就直接提交它(它可能只是被复制过但从未在有效任期获得多数确认),必须等自己任期的条目先行提交。
心跳与选举超时:leader 周期性心跳阻止重新选举;follower 在随机化超时(如 150–300ms)内没听到心跳则自增 term 发起竞选。超时只是活性机制——正确性不依赖其精度(kp-006 与 kp-017 的和解)。
成员变更:直接换全配置会出现新旧配置各自成多数派(双主)。Raft 给出两方案:单节点变更(一次只增删一个节点,可证明新旧多数派必相交)或联合共识(joint consensus 两阶段切换)。生产实现(etcd)多采用单节点变更。
日志压缩:日志无限增长,成熟实现用快照(snapshot)截断:leader 定期打快照,follower 过慢时直接安装快照而非逐条补日志(kp-027 与 MVCC 的存储层衔接)。
图示
Client ──set(x=3)──► Leader(term=5, log idx=9)
Leader ──AppendEntries(9)──► F1,F2 (并行)
F1,F2 fsync 后回复 ack
Leader 收到多数派 ack → commit idx=9 → 应用 → 回复 Client
下一轮心跳携带 commitIdx → followers 应用
直观类比
Raft 像"班长负责制":全班(多数派)举手选出班长(日志最新者优先当选,保证他不缺课);班长把每条通知写在编号的班级日志上(append-only),过半同学抄到手(多数派确认)才算生效(commit);班长失联,大家限时没收到心跳就重选,且只选笔记最全的人。
实例或案例
- etcd:Kubernetes 存储层;linearizable read 经 ReadIndex/lease read 优化读路径。
- TiKV:Multi-Raft(数据按 Region 分片、每个 Region 一个 Raft 组),支撑 TiDB 的强一致分布式 SQL。
- Kafka KRaft:用内置 Raft 取代 ZooKeeper 管理元数据,消除外部依赖与 controller 脑裂史。
常见误区
- 误区一:"Raft 读 leader 就是线性一致"。leader 可能在自己不知道的情况下已被罢免(分区),直接读会返回旧值;需要 ReadIndex(确认任期/询问多数派)或 lease read(在租约窗口内信任本地状态)。
- 误区二:"日志是最终真相所以可随意清理"。压缩必须以快照覆盖到 committed 索引,未提交段清理会破坏选举限制。
- 误区三:"fsync 可选/可以批量攒"。currentTerm/votedFor 不落盘就应答投票,重启后可能双投——破坏单任一票约束,是 Raft 实现的第一大 bug 源。
与其他知识点的关系
- kp-009/kp-018:选举与 Paxos 的对应概念。
- kp-021:Raft 之上做锁与租约的正确姿势。
- kp-025/032:Multi-Raft 与分区结合构成 TiDB/CockroachDB 的底座。
自测题
- 为什么 leader 只能直接提交本任期的日志?
答:旧任期条目可能只是被复制而从未获多数派有效确认;直接提交可能事后被新 leader 覆盖。必须由本任期条目带入提交。
- 选举限制如何防止数据丢失?
答:只投日志"至少和自己新"的 candidate,保证拥有全部已提交条目的节点不可能落选。
- 哪三个状态必须先落盘再应答?
答:currentTerm、votedFor、log 条目;否则崩溃重启后可能重复投票或丢失已确认日志,违反安全性。
延伸阅读
- Diego Ongaro & John Ousterhout, "In Search of an Understandable Consensus Algorithm"(USENIX ATC 2014)。
- Ongaro 博士论文(成员变更与客户端会话的完整论证)。
- etcd 文档:linearizable read / ReadIndex / lease。