@@ -394,18 +394,20 @@ Paxos 算法的第二个阶段称“批准阶段”(Accept)。提议者向
394
394
395
395
现在,我们来分析 S1 、S5 同时发起提案,会出现什么情况。
396
396
397
- ** 情况一:提案已批准** 。如图 6-7 所示 ,S1 收到客户端的请求,于是 S1 作为提议者,向 S1...S3 广播 Prepare(3.1) 消息,决策者 S1...S3 没有接受过任何提案,所以接受该提案。接着,S1 广播 Accept(3.1, X) 消息,提案 X 成功被批准。
397
+ ** 情况一:提案已批准** 。如图所示 ,S1 收到客户端的请求,于是 S1 作为提议者,向 S1...S3 广播 Prepare(3.1) 消息,决策者 S1...S3 没有接受过任何提案,所以接受该提案。接着,S1 广播 Accept(3.1, X) 消息,提案 X 成功被批准。
398
398
399
399
在提案 X 被批准后,S5 收到客户端的提案 Y,S5 作为提议者向 S3...S5 广播 Prepare(4.5) 消息。对 S3 来说,4.5 比 3.1 大,且已经接受了 X,它回复提案 (3.1, X)。S5 收到 S3...S5 的回复后,使用 X 替换自己的 Y,接着进入批准阶段,广播 Accept(4.5, X) 消息。S3...S5 批准提案,所有决策者就 X 达成一致。
400
- ![ [ Pasted image 20250606160134.png]] ** 情况二:事实上,对于情况一,也就是“取值为 X”并不是一定需要多数派批准,S5 发起提案时,准备阶段的应答中是否包含了批准过 X 的决策者也影响决策** 。如图 6-8 所示,S3 接受了提案 (3.1, X),但 S1、S2 还没有收到 Accept(3.1, X) 消息。此时 S3、S4、S5 收到 Prepare(4.5) 消息,S3 回复已经接受的提案 (3.1, X),S5 将提案值 Y 替换成 X,广播 Accept(4.5, X) 消息给 S3、S4、S5,对 S3 来说,编号 4.5 大于 3.1,所以批准提案 X,最终共识的结果仍然是 X。
400
+ ![ [ Pasted image 20250606160134.png]]
401
+
402
+ ** 情况二:事实上,对于情况一,也就是“取值为 X”并不是一定需要多数派批准,S5 发起提案时,准备阶段的应答中是否包含了批准过 X 的决策者也影响决策** 。如图所示,S3 接受了提案 (3.1, X),但 S1、S2 还没有收到 Accept(3.1, X) 消息。此时 S3、S4、S5 收到 Prepare(4.5) 消息,S3 回复已经接受的提案 (3.1, X),S5 将提案值 Y 替换成 X,广播 Accept(4.5, X) 消息给 S3、S4、S5,对 S3 来说,编号 4.5 大于 3.1,所以批准提案 X,最终共识的结果仍然是 X。
401
403
![ [ Pasted image 20250606160150.png]]
402
404
403
405
** 情况三:另外一种可能的情况是 S5 发起提案时,准备阶段的应答中未包含批准过 X 的决策节点** 。S1 接受了提案 (3.1, X),S3 先收到 Prepare(4.5) 消息,后收到 Accept(3.1, X) 消息,由于 3.1 小于 4.5,会直接拒绝这个提案。提案 X 没有收到多数的回复,X 提案就被阻止了。提案 Y 顺利通过,整个系统最终对“取值为 Y”达成一致。
404
406
![ [ Pasted image 20250606160159.png]]
405
407
406
408
** 情况四:从情况三可以推导出另一种极端的情况** ,多个提议者同时发起提案,在准备阶段互相抢占,反复刷新决策者上的提案编号,导致任何一方都无法达到多数派决议,这个过程理论上可以无限持续下去,形成“活锁”(livelock)。
407
409
408
- 解决这个问题并不复杂,将重试时间随机化,就能减少这种巧合发生
410
+ 解决这个问题并不复杂,** 将重试时间随机化** ,就能减少这种巧合发生
409
411
410
412
![ [ Pasted image 20250606160219.png]]
411
413
以上,就是整个 Paxos 算法的工作原理。
@@ -428,13 +430,11 @@ Raft 算法出现之前,绝大多数共识系统都是基于 Paxos 算法或
428
430
429
431
那段时期,虽然所有的共识系统都是从 Paxos 算法开始的,但工程师们实现过程中有很多难以逾越的难题,往往不得已开发出与 Paxos 完全不一样的算法,这导致 Lamport 的证明并没有太大价值。所以,很长的一段时间内,实际上并没有一个被大众广泛认同的 Paxos 算法。
430
432
431
- > Paxos 算法的理论描述与实际工程实现之间存在巨大鸿沟,最终实现的系统往往建立在一个尚未完全证明的算法基础之上。
432
-
433
- —— Chubby 作者评论 Paxos
433
+ > Paxos 算法的理论描述与实际工程实现之间存在巨大鸿沟,最终实现的系统往往建立在一个尚未完全证明的算法基础之上。-- Chubby 作者评论 Paxos
434
434
435
435
考虑到共识问题在分布式系统的重要性,同时为了提供一种更易于理解的教学方法,斯坦福大学的学者们决定重新设计一个替代 Paxos 的共识算法。
436
436
437
- 2013 年,斯坦福大学的学者 Diego Ongaro 和 John Ousterhout 发表了论文 《In Search of an Understandable Consensus Algorithm》[[ 1 ]] ( https://www.thebyte.com.cn/consensus/raft.html#footnote1 ) ,提出了 Raft 算法。Raft 论文开篇描述了 Raft 的证明和 Paxos 等价,详细阐述了算法如何实现。也就是说,Raft 天生就是 Paxos 算法的工程化。
437
+ 2013 年,斯坦福大学的学者 Diego Ongaro 和 John Ousterhout 发表了论文 《In Search of an Understandable Consensus Algorithm》,提出了 Raft 算法。Raft 论文开篇描述了 Raft 的证明和 Paxos 等价,详细阐述了算法如何实现。也就是说,Raft 天生就是 Paxos 算法的工程化。
438
438
439
439
> 《In Search of an Understandable Consensus Algorithm》节选
440
440
> Raft is a consensus algorithm for managing a replicated log. It produces a result ** equivalent to (multi-)Paxos, and it is as efficient as Paxos,** but its structure is different from Paxos;
@@ -466,7 +466,7 @@ raft的选举过程
466
466
467
467
RequestVote RPC 消息示例如下:
468
468
469
- ```
469
+ ``` bash
470
470
{
471
471
" term" : 5, // 候选者的当前任期号,用于通知接收方当前选举属于哪个任期。
472
472
" candidateId" : 3, // 候选者的节点 ID,标识请求投票的节点。
@@ -482,7 +482,7 @@ RequestVote RPC 消息示例如下:
482
482
483
483
RequestVote 响应的示例如下:
484
484
485
- ```
485
+ ``` bash
486
486
{
487
487
" term" : 5, //接收方的当前任期号,用于告知候选者最新的任期号。如果候选者发现该值比自己大,会转为跟随者。
488
488
" voteGranted" : true//是否投票给候选者,true 表示同意,false 表示拒绝。
@@ -497,14 +497,14 @@ RequestVote 响应的示例如下:
497
497
498
498
一旦选出一个公认的领导者,那领导者顺理成章地承担起“** 处理系统发生的所有变更,并将变更复制到所有跟随者节点** ”的职责。
499
499
500
- 在 Raft 算法中,日志承载着系统所有变更。图 6-13 展示了 Raft 集群的日志模型,每个“日志条目”(log entry)包含索引、任期、指令等关键信息:
500
+ 在 Raft 算法中,日志承载着系统所有变更。如图展示了 Raft 集群的日志模型,每个“日志条目”(log entry)包含索引、任期、指令等关键信息:
501
501
502
502
- ** 指令** : 表示客户端请求的具体操作内容,也就是待“状态机”(State Machine)执行的操作。
503
503
- ** 索引值** :日志条目在仓库中的索引值,是单调递增的数字。
504
504
- ** 任期编号** :日志条目是在哪个任期中创建的,用于解决“脑裂”或日志不一致问题。
505
505
![ [ raft-log-C8Qk2Ad7.svg]] Raft 算法中,领导者通过广播消息(AppendEntries RPC)将日志条目复制到所有跟随者。AppendEntries RPC 的示例如下:
506
506
507
- ```
507
+ ``` bash
508
508
{
509
509
" term" : 5, // 领导者的任期号
510
510
" leaderId" : " leader-123" ,
@@ -517,7 +517,7 @@ RequestVote 响应的示例如下:
517
517
}
518
518
` ` `
519
519
520
- 根据图 6-14 所示, 当 Raft 集群收到客户端请求(例如 set x=4)时,日志复制的过程如下:
520
+ 当 Raft 集群收到客户端请求(例如 set x=4)时,日志复制的过程如下:
521
521
522
522
- 若当前节点非领导者,将请求转发至领导者;
523
523
- 领导者接收请求后:
@@ -538,7 +538,7 @@ Raft 日志复制过程需要等待多数节点确认。节点越多,等待的
538
538
539
539
日志不连续的问题是这样解决的:follower-2 收到日志复制请求后,它会通过 prevLogIndex 和 prevLogTerm 检查本地日志的连续性。如果日志缺失或存在冲突,follower-2 返回失败响应,指明与领导者日志不一致的部分。
540
540
541
- ```
541
+ ` ` ` bash
542
542
{
543
543
" success" : false,
544
544
" term" : 4,
@@ -573,7 +573,7 @@ Raft 的论文中,对此提出过一种基于两阶段的“联合共识”(
573
573
574
574
单成员变更方案很容易穷举所有情况,如图 6-16 所示,穷举奇/偶数集群下节点添加/删除情况。如果每次只操作一个节点,Cold 的 Quorum 和 Cnew 的 Quorum 一定存在交集。交集节点只会进行一次投票,要么投票给 Cold,要么投票给 Cnew。因此,不可能出现两个符合条件的 Quorum,也就不会出现两个领导者。
575
575
576
- 以图 6-16 第二种情况为例 ,Cold 为 [ Server1、Server2、Server3] ,该配置的 Quorum 为 2,Cnew 为 [ Server1、Server2、Server3、Server4] ,该配置的 Quorum 为 3。假设 Server1、Server2 比较迟钝,还在用 Cold ,其他节点的状态机已经应用 Cnew:
576
+ 以上图第二种情况为例 ,Cold 为 [Server1、Server2、Server3],该配置的 Quorum 为 2,Cnew 为 [Server1、Server2、Server3、Server4],该配置的 Quorum 为 3。假设 Server1、Server2 比较迟钝,还在用 Cold ,其他节点的状态机已经应用 Cnew:
577
577
578
578
- 假设 Server1 触发选举,赢得 Server1,Server2 的投票,满足 Cold Quorum 要求,当选领导者;
579
579
- 假设 Server3 也触发选举,赢得 Server3,Server4 的投票,但** 不满足 Cnew 的 Quorum 要求,选举失效** 。
0 commit comments