为什么需要共识算法?
在分布式系统中,多节点之间的"达成一致"是最核心也最困难的问题。当三台服务器需要在某个值上达成共识——无论是数据库写入命令、配置变更指令,还是分布式锁的获取——都必须保证:只有当一个值被选定后,所有节点最终都会接受它,且永远不会被篡改。
这就是共识(Consensus)问题。而 Raft 算法的诞生,就是为了给这个经典难题提供一个可理解、可实现、可验证的工业级解决方案。
相比 Paxos 的"学术优雅但工程艰涩",Raft 采用了"强领导者(Strong Leader)"模型,将共识过程分解为三个相对独立的子问题:
- 领导者选举(Leader Election):选出唯一的决策者
- 日志复制(Log Replication):将决策同步到多数节点
- 安全性(Safety):确保已提交日志永不丢失
三个子问题层层递进,构成了 Raft 的完整故事。
基础架构:术语与状态模型
节点角色
Raft 集群中每个节点始终处于以下三种角色之一:
- Leader:处理所有客户端请求、驱动日志复制,每个任期至多一个
- Follower:被动接收 Leader 的心照不宣与日志条目,超时未收到心跳则转为 Candidate
- Candidate:发起选举投票、争取成为 Leader,获得多数票则成为 Leader
正常工作时,一个 Leader + 多个 Follower。Leader 定期发送心跳维持权威。当 Follower 在选举超时窗口内未收到心跳,它就认为 Leader 宕机,开始新一轮选举。
时间模型:Term(任期号)
Raft 将全局时间划分为连续的任期(Term),每个任期由一个严格递增的整数标识。任期是逻辑时钟:
- 每个节点维护 current_term,遇到更高任期号时立即更新
- 每个任期至多有一个 Leader
- 某些任期可能没有 Leader(选举分裂导致)
- 节点间通信时携带任期号:任何节点如果发现自己的任期过期,立即退为 Follower
第一阶段:领导者选举
选举触发机制
每个 Follower 维护一个随机化的选举超时(通常 150ms ~ 300ms)。当超时到期且未收到 Leader 心跳时:
- 递增 current_term
- 转为 Candidate
- 投给自己一票
- 向所有节点发送 RequestVote RPC
- 重置选举超时(随机值,降低再次分裂概率)
随机化超时的核心目的:防止多个节点同时发起选举导致选票分裂。如果所有 Follower 同时超时,必然分裂;随机化使得各节点超时时间错开,第一个超时的节点大概率在其他人超时前赢得选举。
投票规则(关键安全性约束)
接收者只有在以下两个条件同时满足时才会投票:
- Candidate 的日志至少和接收者一样新:比较 (last_log_term, last_log_index) 字典序
- 接收者在本任期内尚未投票给其他人(voted_for 为 None 或就是该 Candidate)
"日志至少一样新"的规则防止了已提交日志的丢失:一个日志较旧的节点永远无法赢得选举。
第二阶段:日志复制
基本流程
Leader 处理客户端请求的完整路径:
- 追加:Leader 将命令作为新日志条目追加到本地 log
- 广播:Leader 并行向所有 Follower 发送 AppendEntries RPC
- 确认:Follower 复制成功后返回 success: true
- 提交:Leader 收到多数确认后,递增 commit_index,应用到状态机
- 通知:下一次心跳携带新的 commit_index,Follower 依次应用
日志冲突的快速回退优化
当 Follower 的日志与 Leader 不一致时,Raft 使用 Term-based 快速回退策略:Follower 在自己日志中找到冲突 Term 的第一条索引告知 Leader,直接回退到该 Term 之前的日志,大幅减少试错轮次。
第三阶段:安全性与正确性证明
选举安全性(Election Safety)
每个任期内至多有一个 Leader。每个节点每个任期只能投一票,只有获得多数票才能成为 Leader。多数集交集保证:任何两个多数集至少共享一个节点。
Leader 完备性(Leader Completeness)
一旦一条日志条目被提交,所有更高任期的 Leader 的日志中一定包含该条目。这依赖于投票规则保证:复制多数集与投票多数集必有重叠,因此 Leader 必定持有已提交条目。
No Committing Before the Current Term
这是 Raft 与 Paxos 最大的工程差异之一。即使有"被多数复制"的直接证据,如果是旧任期的日志也不能直接提交。这是防止"幽灵复现"的关键保障。
生产级工程优化
日志快照(Snapshotting)
长时间运行的 Raft 集群日志无限增长,Leader 定期创建状态机快照,通告 Follower 安装快照(InstallSnapshot RPC),安装完成后删除对应日志条目。
集群成员变更
使用联合共识(Joint Consensus)或单节点变更策略,保证任何时刻不会存在两个不相交的多数决议。etcd、TiKV 等系统采用主流的单节点变更方案。
线性一致性读
Read Index(等待 commit_index 追上)、Lease Read(心跳租约期内直读)、Follower Read(向 Leader 确认 commit_index 后读)三种方案保障。
性能关键优化
- Pipeline 复制:不等返回就发送下一个
- Batch 合并:多条目合并为一个 RPC
- Learner 节点:新节点先追赶日志再转 Voter
- Pre-Vote:正式递增 Term 前先探测多数可达
- 检查点提示:快速回退 hint 减少冲突重试
Raft vs Paxos vs ZAB
- Raft:可理解性优先,强 Leader,etcd/TiKV/CockroachDB
- Paxos:数学优雅但工程模糊,Chubby/Spanner
- ZAB:ZooKeeper 私有协议,KV 存储优化
总结
Raft 的核心设计理念是可理解性优先,但安全性不打折扣:
- 持久化三项:current_term、voted_for、log[] 每次响应前落盘
- 提交条件:只提交当前任期的、被多数复制的条目
- 随机超时:选举超时随机化避免选票分裂
- 日志一致性:prev_log_index + prev_log_term 保证对齐
- 安全第一:成员变更一次一个节点或使用联合共识
- 快照不可或缺:长期运行必须配合快照压缩
从 etcd 到 TiKV,从 Consul 到 CockroachDB,Raft 已成为现代分布式系统的"事实共识标准"。

发表评论 取消回复