彻底搞懂 Raft 算法:为“被理解”而生的分布式共识
彻底搞懂 Raft 算法:为“被理解”而生的分布式共识
在分布式系统的世界里,共识(Consensus)是一个核心难题:如何让一堆机器对某个值(比如数据记录)达成一致?
很久以来,Paxos 算法是这个领域的“神”,但它太难懂了,难懂到连工程师都难以正确实现它。于是,Raft 诞生了。Raft 的设计初衷非常直白——“为了被理解”。它将复杂的共识问题拆解得清晰明了。
要理解 Raft,其实只需要搞懂三个核心问题:
- 老大是怎么选出来的?(Leader Election)
- 数据是怎么同步的?(Log Replication)
- 出了问题怎么办?(Safety & Network Partition)
本文将通过图解的方式,带你拆解 Raft 的核心原理。
一、 核心概念:三个角色与任期
在 Raft 集群中,任何时候,一个节点只能处于以下三种状态之一。我们可以把它想象成一个由群众、竞选者和独裁者组成的微型社会。
1. 三种角色 (Roles)
- Leader(领导者/独裁者):
- 特权:全权负责处理客户端的写请求,把日志复制给 Follower。
- 义务:它必须不断给 Follower 发心跳(Heartbeat),证明自己还活着,否则会被“罢免”。
- Follower(追随者/群众):
- 本分:平时不说话,只响应 Leader 的指令(写数据)和 Candidate 的拉票请求。
- 造反:如果在一段时间内没收到 Leader 的心跳,它就会认为 Leader 挂了,从而变身 Candidate。
- Candidate(候选人/竞选者):
- 目的:当 Follower 没听到 Leader 的声音,就会变成 Candidate,发起投票,试图成为新 Leader。
2. 任期 (Term)
Raft 将时间划分为一个个的 Term(任期),用递增的数字表示(Term 1, Term 2...)。
- 每一届任期开始都是一次选举。
- 规则:如果一个节点发现别人的 Term 比自己大,它会立马变回 Follower(认怂)。这就像前朝皇帝遇到了新朝皇帝。
角色状态转换图
二、 阶段一:Leader 选举 (Leader Election)
Raft 使用心跳机制来触发选举。只要 Leader 还在发心跳,Follower 就乖乖听话;心跳一停,天下大乱。
1. 触发选举
Follower 节点有一个选举超时定时器(Election Timeout)(通常在 150ms-300ms 之间随机)。
- 正常情况:收到 Leader 心跳 -> 重置定时器 -> 继续当 Follower。
- 异常情况:定时器倒计时结束(超时) -> 变身 Candidate。
2. 竞选流程
当一个节点变成 Candidate 后,它会执行以下动作:
- 任期 +1(Term ++)。
- 给自己投一票。
- 群发 RequestVote RPC:向所有其他人喊话“请投我一票!”。
3. 谁能当选?
其他节点收到拉票请求,会根据以下原则投票:
- 先来后到:一个任期内,票只有一张,先给谁就是谁的。
- 比我新:你的日志不能比我的旧。
结果判定:
- 赢了:获得了集群大多数(N/2 + 1)的票数 -> 成为 Leader -> 立即发送心跳宣告主权。
- 输了:收到了别人的心跳(说明别人赢了) -> 变回 Follower。
- 僵局(Split Vote):票数对半开,谁都没过半。
4. 巧妙的“随机超时”
为了解决僵局,Raft 让每个节点的超时时间是一个随机值。这保证了大概率有一个节点会“先醒来”并发起投票,从而打破平衡。
选举流程图
三、 阶段二:日志复制 (Log Replication)
Leader 选出来了,现在开始干活。Raft 保证数据的强一致性。
流程步骤
- Client 请求:客户端发送命令
set x=5给 Leader。 - 预写入 (Append):Leader 把命令写入本地日志(状态:Uncommitted),并并行发送
AppendEntries RPC给 Follower。 - Follower 写入:Follower 收到日志,检查一致性。如果没问题,写入本地日志,返回 Success。
- 提交 (Commit):
- Leader 收到大多数 Follower 的 Success。
- Leader 将日志标记为 Committed,应用到状态机(执行
x=5)。 - Leader 给客户端返回“成功”。
- 后续同步:Leader 在下一次心跳中告诉 Follower:“刚才那条已提交,你们也可以应用了。”
日志复制流程图
四、 阶段三:安全性与脑裂 (Brain Split)
分布式系统最怕网络分区(Network Partition)。假设 5 个节点(A, B, C, D, E)裂变成了两半:[A, B] 和 [C, D, E]。
4.1.场景演练
- 旧 Leader (A) 的困境:
- A 在
[A, B]分区。Client 给它发数据,它试图同步给 B。 - 但它永远凑不齐 3 票(总共5个节点,需要3票)。
- 结果:A 收下的数据永远是 Uncommitted,无法响应客户端成功。
- A 在
- 新 Leader (C) 的崛起:
- 在
[C, D, E]分区,C 发现联系不上 A,发起选举。 - C 拿到 C, D, E 的 3 票,成为新 Leader(Term 增加)。
- Client 连接到 C,写入数据。C 能同步给 D 和 E,成功提交。
- 在
- 网络恢复(真相大白):
- 分区消失。A (Term 1) 遇到了 C (Term 2)。
- 规则:Term 小的自动退位。
- 结果:A 发现自己任期旧,立马变为 Follower。它会清空自己那些未提交的“脏数据”,同步 C 的最新数据。
4.2.如果分裂了两边都是都是三个刚好一半怎么办?
这种情况(比如总共 6 个节点,裂变成了 3 对 3),正是分布式系统中为什么要强烈建议部署奇数个节点(3, 5, 7...)的原因。
简单直接的答案是:整个系统会“停摆”(不可用)。两边都无法工作,谁也当不了老大。
Raft 为了保证数据绝不出错(Safety),宁可让系统停止服务(牺牲 Availability),也不会允许出现两个无法达成大多数共识的分区。
下面详细拆解这个“僵局”:
4.2.1、 数学规则:大多数 (Majority) 是多少?
Raft 的核心铁律是:必须要拿到“大多数”票数才能干活。
公式是:大多数 = floor(总节点数 / 2) + 1
- 如果是 5 个节点:大多数 =
5/2 + 1= 3 票。 - 如果是 6 个节点:大多数 =
6/2 + 1= 4 票。
4.2.2、 场景演练:3 对 3 的绝望死锁
假设集群有 6 个节点(A, B, C, D, E, F),Leader 是 A。
网络发生了分割,切成了两半:[A, B, C] 和 [D, E, F]。
1. 左边分区 [A, B, C](包含老 Leader A)
- 现状:Leader A 依然活着,试图处理请求。
- 写数据:A 收到客户端写请求,发送给 B 和 C。
- 结果:A 最多能收到 A, B, C 三个节点的确认。
- 判定:3 票 < 4 票(大多数)。
- 结局:Leader A 无法提交任何日志。客户端会一直等待或超时。左边瘫痪。
2. 右边分区 [D, E, F](没有 Leader)
- 现状:D, E, F 收不到 A 的心跳,超时发起选举。
- 选举:假设 D 发起竞选,它向 E 和 F 拉票。
- 结果:D 最多能拿到 D, E, F 三张票。
- 判定:3 票 < 4 票(大多数)。
- 结局:D 永远选不上 Leader。哪怕无限重试,只要网络不好,它永远凑不齐 4 票。右边也瘫痪。
4.2.3、 为什么这反而是“好事”?
虽然系统不可用了(Downtime),但 Raft 成功避免了脑裂(Brain Split)导致的数据不一致。
- 如果是 Paxos 或 Raft,这种情况下没有产生双主(左边是不能提交的废主,右边选不出新主)。
- 数据没有冲突,一旦网络恢复,大家重新凑在一起,就能立刻恢复正常。
4.2.4、 最佳实践:为什么总是个数(Odd Number)?
这就解释了为什么在生产环境中,Etcd、Zookeeper、Consul 等组件的节点数永远建议是 3 个、5 个或 7 个,而极少见到 4 个或 6 个。
偶数部署的坏处(比如 4 个节点):
- 容错率没变:
- 3 个节点能容忍 1 个挂掉(剩 2 个,满足大多数 2)。
- 4 个节点也只能容忍 1 个挂掉(剩 3 个,满足大多数 3)。如果有 2 个挂了,剩 2 个 < 3,系统就挂了。
- 加了一台机器,容错能力竟然没提升!
- 脑裂风险增加:
- 偶数节点容易出现 50:50 的网络分区,导致像上面那样的全系统瘫痪。
小结
如果分裂成了两边刚好各一半:
- 写操作全部失败。
- 选不出新 Leader。
- 系统进入不可用状态,直到网络恢复。
这也是为什么架构师在设计 Raft 集群时,一定会告诉你:“请凑个单数!”
五、 总结 Raft 的精髓
Raft 之所以能取代 Paxos 成为工业界的主流(Etcd, Consul, TiKV 都在用),就在于它清晰的规则:
- 强主模型 (Strong Leader):所有写操作必须经过 Leader,简化了逻辑。
- 大多数原则 (Quorum):只要大多数节点活着,系统就能跑,数据就不会丢。
- 随机超时 (Randomized Timeout):用最简单的技巧解决了复杂的选举僵局。
- 任期逻辑 (Terms):用“朝代”的概念解决了时序和冲突问题。
一句话总结 Raft:
选出一个管事的,所有事情听他的,大多数人记下来了就算成功。
浙公网安备 33010602011771号