本文主要是介绍Raft论文阅读笔记+翻译:In Search of Understandable Consensus Algorithm,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
In Search of Understandable Consensus Algorithm
Raft是一种管理复制日志的共识算法。它产生与(多)Paxos等效的结果,并且与Paxos一样高效,但其结构与Paxos不同。这使得Raft比Paxos更易理解,也为构建实际系统提供了更好的基础。为了增强可理解性,**Raft将共识的关键元素(如领导选举,日志复制和安全性)分离,并强制执行更强的一致性以减少必须考虑的状态数。**用户研究的结果表明,相比于Paxos,Raft更容易学习。Raft还包括一种改变集群成员关系的新机制,它使用重叠多数来保证安全性。
一、简介
共识算法允许一组机器作为一个协调一致的群体工作,可以在其中一些成员发生故障时仍然正常运行。因此,它们在构建可靠的大规模软件系统中起着关键作用。在过去的十年中,Paxos [15, 16]主导了共识算法的讨论:大多数共识的实现都基于Paxos或受其影响,并且Paxos已成为教授学生共识的主要工具。
不幸的是,尽管进行了许多努力以使Paxos更易理解,但它仍然相当难以理解。此外,它的架构需要复杂的改变来支持实际系统。因此,系统构建者和学生对Paxos感到困难。
Paxos在实际系统中较难实现
在自己努力应付 Paxos 算法后,我们决定寻找一种新的共识算法,为系统构建和教育提供更好的基础。我们的方法与众不同,主要目标是可理解性:我们能否定义一个适用于实际系统的共识算法,并以一种比 Paxos 更容易学习的方式描述它?此外,我们希望该算法能够促进系统构建者必不可少的直觉发展。算法不仅在工作上至关重要,而且其工作原理也应该显而易见。
这项工作的结果是一种称为Raft的共识算法。在设计Raft时,我们应用了特定的技术来提高可理解性,包括分解(Raft将领导选举、日志复制和安全性分离)和状态空间的减少(相对于Paxos,Raft减少了非确定性程度和服务器之间可能不一致的方式)。在两所大学进行的43名学生的用户研究表明,Raft比Paxos更容易理解:在了解这两种算法后,这些学生中有33名学生能够更好地回答关于Raft的问题而不是关于Paxos的问题。
Raft在许多方面与现有的共识算法相似(尤其是Oki和Liskov的Viewstamped Replication [29, 22]),但它有几个新颖的特点:
- 强大的领导者:Raft使用比其他共识算法更强大的领导形式。例如,**日志条目仅从领导者流向其他服务器。**这简化了复制日志的管理,并使Raft更易于理解。
- 选举领导者:Raft 使用随机计时器来选举领导者。这只为任何共识算法所需的心跳添加了一小部分机制,同时简单且迅速地解决冲突。
- 成员变动:Raft的机制用于在集群中更改服务器集的使用新的联合共识方法,在过渡期间两个不同配置的多数派重叠。这使得集群在配置变化期间可以正常运行。
我们相信Raft在教育目的和作为实现基础上都优于Paxos和其他一致性算法。它比其他算法更简单、更易理解;它的描述足够完整以满足实际系统的需求;它有几个开源实现并且被几家公司使用;它的安全性特性已被明确规定和证明;它的效率与其他算法相当。
本文的其余部分介绍了复制状态机问题(第2节),讨论了Paxos的优点和缺点(第3节),描述了我们对可理解性的一般方法(第4节),呈现了Raft共识算法(第5-8节),评估了Raft(第9节),并讨论了相关工作(第10节)。
二、复制状态机
共识算法通常出现在复制状态机的上下文中[37]。在这种方法中,一组服务器上的状态机计算相同的状态的相同副本,并且即使其中一些服务器宕机,它们也可以继续运行。复制状态机用于解决分布式系统中的各种容错问题。例如,具有单个集群领导者的大规模系统,例如GFS [8],HDFS [38]和RAMCloud [33],通常使用单独的复制状态机来管理领导者选举并存储必须幸免于领导者崩溃的配置信息。复制状态机的示例包括Chubby [2]和ZooKeeper [11]。
复制状态机通常使用复制日志进行实现,如图1所示。每个服务器存储包含一系列命令的日志,其状态机按顺序执行这些命令。**每个日志以相同的顺序包含相同的命令,因此每个状态机处理相同的命令序列。**由于状态机是确定性的,每个状态机计算相同的状态和相同的输出序列。
保持复制日志一致是共识算法的工作。①服务器上的共识模块接收来自客户端的命令并将其添加到其日志中。②它与其他服务器上的共识模块通信,以确保每个日志最终都包含相同的请求,并按相同的顺序,即使某些服务器失败。③一旦命令被正确复制,每个服务器的状态机按照日志顺序处理它们,④并将输出返回给客户端。结果,服务器们看起来形成了一个单一的、高度可靠的状态机。
对于实际系统而言,共识算法通常具有以下特性:
- 在所有非拜占庭条件下,包括网络延迟、分区、数据包丢失、复制和重排序,它们可以确保安全性(永不返回错误的结果)。
- 只要大多数服务器仍在运行且能够相互之间和与客户端通信,它们就具有完全功能(可用)。因此,一个典型的由五个服务器组成的集群能够容忍任意两个服务器的故障。假设服务器由于停止而发生故障;他们可以稍后从稳定存储的状态中恢复并重新加入集群。
- 它们并不依赖于时序来确保日志的一致性:有问题的时钟和极端的消息延迟最多会导致可用性问题。
- 在一般情况下,只要多数节点在进行一轮远程过程调用后作出响应,一个命令就能完成;少数较慢的服务器不会影响整体系统性能。
三、设计以增加可理解性
我们在设计Raft时有几个目标:它必须为系统建设提供完整而实用的基础,从而大大减少开发人员所需的设计工作量;在所有情况下都必须安全,并在典型的操作条件下可用;对于常见操作来说必须高效。但是我们最重要的目标,并且也是最困难的挑战,是可理解性。它必须能够让广大受众轻松理解算法。此外,还必须能够对算法产生直观感受,以便系统构建者能够在现实世界的实现中进行不可避免的扩展。
设计Raft时,我们在许多方面需要选择不同的方法。在这些情况下,我们通过可理解性来评估不同的选择:每一种选择有多难解释(例如,它的状态空间有多复杂,是否存在微妙的影响?),读者能够完全理解这种方法及其影响力有多容易?
我们认识到这样的分析具有很高的主观性;尽管如此,我们使用了一种普遍适用的两种技术。第一种技术是众所周知的问题分解方法:只要可能,我们将问题分解成可以相对独立地解决、解释和理解的独立部分。例如,在Raft中,我们将领导者选举、日志复制、安全性和成员变更进行了分离。
我们的第二种方法是通过减少要考虑的状态数量来简化状态空间,使系统更加连贯,并尽可能消除非确定性。具体而言,不允许日志有空洞,而且Raft限制了日志之间可能发生不一致的方式。尽管在大多数情况下我们试图消除非确定性,但在一些情况下,非确定性实际上会提高可理解性。特别是,随机化方法引入了非确定性,但它们倾向于通过以类似的方式处理所有可能的选择来减少状态空间(“选择任何一个都可以,没关系”)。我们使用随机化来简化Raft的领导者选举算法。
四、Raft算法
Raft是一种用于管理复制日志的算法,其形式在第2节中进行了描述。图2以简洁的形式总结了该算法供参考,图3列出了该算法的关键属性;这些图的要素在本节的其他部分逐步讨论。
Raft是通过首先选举出一位杰出的领导者,然后将完全负责管理复制日志的责任交给该领导者来实现一致性。领导者接受来自客户端的日志条目,将其复制到其他服务器上,并告诉服务器何时安全地将日志条目应用到其状态机上。拥有领导者简化了复制日志的管理。例如,领导者可以决定在日志中的哪个位置放置新的条目,而无需咨询其他服务器,数据从领导者流向其他服务器的方式也是简单的。领导者可能会失败或与其他服务器断开连接,这种情况下会选举出新的领导者。
领导者的作用:
- 日志复制
- 日志提交
- 简化复制管理,如领导者可以决定在日志中哪个位置放置新的条目,无需咨询其他服务器。数据从领导者流向其他服务器的方式也是简单的。领导者失败了,那么选出新的领导者
给定领导者的方法,Raft将一致性问题分解成三个相对独立的子问题,这些问题在接下来的小节中进行了讨论。
- 领导选举:当现任领导者失败时,必须选择新的领导者(第5.2节)。
- 日志复制:领导者必须接受来自客户端的日志条目,并将它们复制到整个集群中,迫使其他日志与其一致(第5.3节)。
- 安全性:Raft的关键安全属性是图3中的状态机安全属性:如果任何服务器已将特定日志项应用于其状态机,则其他服务器不得为相同的日志索引应用不同的命令。第5.4节描述了Raft如何确保此属性;解决方案涉及对第5.2节中描述的选举机制的额外限制。
在介绍共识算法之后,本节讨论系统的可用性问题和时间在其中的作用。
Raft基础
Raft集群包含多个服务器,五个是典型的数量,这使系统能够容忍两个故障。在任何给定的时间,每个服务器处于三种状态之一:领导者、追随者或候选人。在正常的运行中,正好有一个领导者,所有其他服务器都是追随者。追随者是被动的:他们自己不发出请求,只是回应领导者和候选人的请求。领导者处理所有客户端请求(如果客户端联系追随者,追随者将其重定向到领导者)。候选人这个状态用于在5.2小节中描述的选举新领导者。图4展示了这些状态及其转换过程;转换过程将在下面讨论。
五个服务器,容忍2个故障。
服务器处于三种状态之一:
- 领导人:处理所有客户端的请求
- 追随者:回应候选人和领导者的请求
正常的运行只有一个领导者,其余都是追随者。
Raft 将时间分为任意长度的时期,如图5所示。这些时期按连续整数进行编号。每个时期都从选举开始,候选人尝试成为领导者,如第5.2节所述。如果候选人赢得选举,那么它将在该时期的其余时间担任领导者。**在某些情况下,选举会导致投票分裂。在这种情况下,该时期将在没有领导者的情况下结束;**不久将开始一个新的时期(带有新的选举)。Raft 确保在给定时期内至多只有一个领导者。
不同的服务器可能在不同的时间观察到任期之间的转换,在某些情况下,服务器甚至可能没有观察到选举或整个任期。任期在Raft中充当逻辑时钟[14],它们允许服务器检测过时的信息,如陈旧的领导者。每个服务器存储一个当前任期号码,它随时间单调递增。当前任期在服务器通信时进行交换;**如果一个服务器的当前任期较小,则将其当前任期更新为较大的值。**如果候选人或领导者发现其任期已过时,则立即恢复为跟随者状态。如果服务器收到具有过时任期号码的请求,则拒绝该请求。
加粗,不理解。
Raft服务器使用远程过程调用(RPC)进行通信,基本的一致性算法只需要两种类型的RPC。RequestVote RPC由候选人在选举期间发起(第5.2节),AppendEntries RPC由领导者发起以复制日志条目并提供一种心跳机制(第5.3节)。第7节增加了第三种RPC用于在服务器之间传输快照。如果服务器未能及时收到响应,它们会重试RPC,并并行发出RPC以获得最佳性能。
两种RPC:
- RequestVote RPC:候选人在选举期间发起
- AppendEntries RPC:领导者发起,两种用途:
- 复制日志条目
- 提供心跳机制
增加了一种RPC用以在服务器之间传输快照。服务器未能及时响应,那么并行发出RPC以获得最佳性能。
领导人选举
Raft使用心跳机制来触发领导者选举。当服务器启动时,它们作为跟随者开始。只要服务器从领导者或候选人接收到有效的RPC(远程过程调用),它就保持在跟随者状态。领导者定期发送心跳(不携带日志条目的AppendEntries
RPC)给所有跟随者,以维持其权威性。如果一个跟随者在一段被称为选举超时的时间内没有通信,那么它就会假定没有可行的领导者,并开始进行选举以选择一个新的领导者。
心跳机制触发领导人选举:心跳过期,那么开始选举
开始选举,追随者增加其当前任期并转换为候选人状态。然后,它为自己投票并并行向集群中的每个其他服务器发出请求选票的RPC。候选人将保持在这个状态,直到以下三种情况发生之一:(a)它赢得选举,(b)另一个服务器建立为领导者,或者(c)一段时间过去没有获胜者。这些结果在下面的段落中分别讨论。
开始选举的动作:
- 追随者:追加任期,转换为候选人状态【所有的追随者都要追加任期吗?还是仅仅发起投票的追随者追加任期】
- 候选人:为自己投票,向集群中的其他每个服务器发出请求选票RPC
RequestVote
RPC,候选人将保持这个状态,直到:
- 赢得选举
- 另一个服务器成为领导者
- 一段时间过去了,但没有获胜者
一名候选人如果获得整个集群中大多数服务器在同一届中的选票,则赢得选举。每个服务器在给定届中最多只能为一个候选人投票,按照先到先得的原则(注:第5.4节增加了对投票的额外限制)。多数规则确保在特定届中最多只能有一个候选人获胜(图3中的选举安全特性)。一旦候选人赢得选举,它将成为领导者。然后,它向其他所有服务器发送心跳消息,以确立其权威并防止新的选举。
赢得选举的情况:获得大多数选票,确保只有一个候选人获胜
选举规则:先到先得(额外限制)【似乎是对日志条目有要求】
赢得选举后:
- 成为领导者
- 向其他所有服务器发送心跳信息,确立权威,防止新的选举
Note:会不会出现一次选举结束,还没来得及树立权威(RPC需要一定时间),另一个服务器心跳才刚刚过期,然后立马又开启选举的情况?
在等待投票时,候选人可能会收到一条来自另一台声称自己是领导者的服务器的AppendEntries
RPC。如果领导者的任期(包含在其RPC中)至少与候选人当前的任期一样大,则候选人将承认领导者的合法性,并返回到follower状态。如果RPC中的任期小于候选人当前的任期,则候选人拒绝该RPC,并继续处于候选人状态。
另一个服务器成为领导者的情况:
解决了上述问题,发起选举要加任期,可以根据收到的选举请求
RequestVote
的RPC请求中的任期进行操作。
- 任期>=自己的任期:新的领导人已经出现,放弃选举
- 任期小:可能是过期的消息,拒绝RPC,继续候选人。
第三种可能的结果是候选人既不赢得选举,也不输掉选举:如果许多追随者同时成为候选人,选票可能被分割,以至于没有候选人获得多数票。当出现这种情况时,每个候选人将超时并通过增加其任期并启动另一轮RequestVote
RPCs来开始新的选举。然而,如果没有额外措施,分割的选票可能会无限重复。
无获胜领导人:原因是多个候选人一起发起投票,选票瓜分
动作:
- 每个候选人超时,并启动新一轮选举
没有额外机制保障,分割选票的可能会无限重复
Raft利用随机的选举超时来确保分裂投票是罕见的,并且尽快解决它们。为了防止分裂投票,在第一次选举时,选举超时是从一个固定的时间间隔中随机选择的(例如,150-300毫秒)。这样分散了服务器,以至于在大多数情况下,只有一个服务器会超时;它赢得选举并在其他任何服务器超时之前发送心跳。同样的机制用于处理分裂投票。每个候选者在选举开始时重新启动其随机化的选举超时,并且在开始下一次选举之前等待超时的过去;这减少了新选举中发生另一次分裂投票的可能性。第9.3节显示了这种方法能够迅速选出一位领导者。
选票被瓜分,等待一段时间再启动新的一轮
RequestRPC
每个进程设置的等待时间是随机分散在150-300ms之间的,以分散服务器
大多数情况下只有一个服务器会超时
超时后会启动新的RPC,他的任期增加,比其他的follower任期都要大。其他的follower收到任期比他还大的任期,那么放弃选举。
Note:放弃选举的方式是什么?什么都不做?还是要投票?
选举是一个例子,说明了我们在设计选择之间是如何根据可理解性来做出选择的。最初,我们计划使用一个排名系统:每个候选人被分配一个唯一的排名,然后用该排名来选择竞争的候选人。如果一个候选人发现了一个排名更高的候选人,它将回到追随者状态,以便排名更高的候选人可以更容易地赢得下一次选举。我们发现这种方法在可用性方面存在一些微妙的问题(如果一个排名较低的服务器需要超时并再次成为候选人,但如果它这样做得太快,会重置选举领导者的进度)。我们对算法进行了几次调整,但每次调整后都出现了新的特殊情况。最终,我们得出结论,随机重试的方法更加明显和可理解。
日志复制
一旦领导人被选举出来,他们就开始为客户的请求提供服务。每个客户请求都包含要由复制的状态机执行的命令。领导者将命令作为新条目附加到其日志中,然后并行向每个其他服务器发出AppendEntries RPC以复制该条目。当条目安全地复制(如下所述)后,领导者将该条目应用于其状态机,并将执行结果返回给客户端。如果追随者崩溃、运行缓慢或者网络数据包丢失,领导者将无限次重试AppendEntries RPC(即使已经响应客户端),直到所有追随者最终存储所有日志条目。
领导者复制日志的动作:
- 追加客户请求到日志条目
- 并行发出
AppendEntries
RPC以复制日志条目到客户端- 安全复制(什么是安全复制?)后,领导者将条目应用于状态机
- 执行结果返回客户端
- 如果出现追随者崩溃等情况,领导者无限次尝试
AppendEntries
RPC,直到所有追随者最终存储所有的日志条目
日志按照图6所示进行组织。每个日志条目存储了在其被领导者接收时的状态机命令以及任期号。日志条目中的任期号用于检测日志之间的不一致,并确保图3中的某些属性。每个日志条目还有一个整数索引,用于标识其在日志中的位置。
日志条目内容:
- 状态机命令
- 任期号码:检测日志之间的不一致
- 整数索引:标识日志的位置
领导者决定何时安全地将日志项应用到状态机;这样的项被称为已提交。 Raft算法保证已提交的项是持久的,并且最终会被所有可用的状态机执行。一旦创建该项的领导者在大多数服务器上复制了该项(例如,在图6中的项7中),则日志项被提交。这还会提交领导者日志中的所有先前项,包括之前的领导者创建的项。第5.4节讨论了在领导者更改后应用此规则时的一些细微之处,并且还表明了这种承诺定义的安全性。领导者跟踪其已知的最高索引已被提交,并将该索引包括在以后的AppendEntries RPC中(包括心跳),以便其他服务器最终找出。一旦跟随者了解到日志项已提交,它将按日志顺序将该项应用于其本地状态机。
提交:将日志项应用到状态机
安全地复制:大多数服务器复制了该项,日志被提交。还会提交领导者日志中的所有先前项,包括之前领导者创建的项。
日志被提交后:被提交的最高索引将包括在以后的
AppendEntries
RPC中(包括心跳),保证被跟随者捕捉到日志提交信息。
- 提交之前的领导者的项==【会不会重复提交?】==
我们设计了Raft日志机制,以保持不同服务器上日志的高度一致性。这不仅简化了系统的行为,使其更加可预测,而且还是确保安全性的重要组成部分。Raft具有以下特性,这些特性共同构成图3中的日志匹配属性。
- 如果两个不同的日志中的条目具有相同的索引和任期,那么它们存储的是相同的命令。
- 如果两个不同日志中的条目具有相同的索引和任期,则在所有之前的条目中,这两个日志是相同的。
第一个属性源于一个事实:在给定的任期和给定的日志索引中,一个领导者最多创建一个条目。日志条目永远不会改变它们在日志中的位置。第二个属性通过AppendEntries执行的简单一致性检查保证。在发送AppendEntries RPC时,领导者在其日志中的新条目包含了有关条目之前的索引(preLogIndex
)和任期(preLogTerm
)的信息。如果跟随者在其日志中找不到具有相同索引和任期的条目,则拒绝新的条目。一致性检查作为归纳步骤:日志的初始空状态满足日志匹配属性,并且每当日志被扩展时,一致性检查都保持日志匹配属性。因此,每当AppendEntries成功返回时,领导者都知道跟随者的日志与其自身的日志一致,直到新条目。
拒绝新的条目会补发吗?【已解决】
如果两个日志中的条目在索引和任期上相同,则在所有先前条目中,这两个日志相同。在正常操作期间,领导者和跟随者的日志保持一致,因此AppendEntries一致性检查永远不会失败。然而,领导者崩溃可能会导致日志不一致(旧的领导者可能没有完全复制其日志中的所有条目)。这些不一致性可能会在一系列领导者和跟随者的崩溃中累积。图7说明了跟随者的日志可能与新领导者的日志不同的方式。跟随者可能会缺少领导者存在的条目,可能会有领导者不存在的额外条目,或者两者都可能存在。日志中缺少和多余的条目可以跨越多个任期。
不一致的原因:旧的领导者没有完全复制其日志
不一致情形:
- 日志多了
- 日志少了
可能跨越多个任期
在Raft中,领导者通过强制追随者的日志复制自己的日志来处理不一致。这意味着追随者日志中的冲突条目将被领导者日志中的条目覆盖。第5.4节将证明,当与一项额外限制相结合时,这是安全的。
解决方案:强制追随者的日志复制自己的日志
额外限制:领导者自己具有最新的日志条目
为了使跟随者的日志与自己的一致,领导者必须找到两个日志达成一致的最新日志条目,删除该点之后跟随者日志中的任何条目,并将该点之后领导者的所有条目发送给跟随者。所有这些操作都是作为AppendEntries RPC执行的一致性检查的响应而发生的。**领导者为每个跟随者维护一个nextIndex,它是领导者将发送给该跟随者的下一个日志条目的索引。**当领导者首次获得权力时,它初始化所有的nextIndex
值为其日志中最后一个索引之后的索引(在图7中为11)。如果一个跟随者的日志与领导者的日志不一致,在下一个AppendEntries RPC中,AppendEntries一致性检查将失败。在被拒绝之后,领导者会减小nextIndex并重试AppendEntries RPC。最终,nextIndex将达到一个领导者和跟随者日志匹配的点。当发生这种情况时,AppendEntries将成功,它会移除跟随者日志中的任何冲突条目,并追加来自领导者日志的条目(如果有)。一旦AppendEntries成功,跟随者的日志将与领导者保持一致,并且在该任期的剩余时间内保持一致。
如果需要的话,可以对协议进行优化,以减少被拒绝的AppendEntries RPC的数量。例如,在拒绝一个AppendEntries请求时,追随者可以包含冲突条目的任期和其存储的第一个索引。有了这些信息,领导者可以递减nextIndex以绕过所有该任期中的冲突条目;每个有冲突条目的任期只需要一个AppendEntries RPC,而不是每个条目一个RPC。实际上,我们怀疑这种优化是不必要的,因为故障发生得很少,而且不太可能存在许多不一致的条目。
AppendEntries
一致性检查达成一致的方式:
- 找到两个日志达成一致的最新日志条目
- 删除该点之后跟随者日志中的任何条目
- 领导者的所有条目发给跟随者
领导者为每个跟随者维护一个
nextIndex
,该值初始化为领导者最后一个日志的下一个日志的索引。
AppendEntries
失败的情况:
- 逐渐少
nextIndex
的值,直到和领导者匹配==(nextIndex
和preLogIndex
难道不是发送一个就行了吗?为什么要发送两个?)==AppendEntries
:匹配到一致的日志条目后,删除之后的条目,追加自己的条目。- 一旦一致,那么剩余任期内一致。
使用该机制,当权力更替发生时,领导者不需要采取任何特殊行动来恢复日志的一致性。它只需开始正常运行,日志会自动在附加条目一致性检查失败的情况下收敛。领导者永远不会覆盖或删除其自己日志中的条目(图 3 中的领导者仅附加属性)。
该日志复制机制展现了第 2 节中描述的理想共识属性:只要大多数服务器可用,Raft 就可以接受、复制和应用新的日志条目;在正常情况下,新条目可以通过向大多数集群进行一轮 RPC 复制;单个缓慢的跟随者不会影响性能。
安全
前面的章节描述了Raft如何选举领导者并复制日志条目。然而,迄今为止描述的机制还不足以确保每个状态机以相同的顺序执行完全相同的命令。例如,一个追随者在领导者提交多个日志条目时可能不可用,然后它可能会被选为领导者并用新的条目覆盖这些条目;结果,不同的状态机可能会执行不同的命令序列。
“不可用”日志的概念
- 领导者选举和日志复制的不一致:Raft算法通过复制日志条目来确保一致性。如果一个领导者在它的任期内没有将一个日志条目成功复制到大多数节点上**(即
AppendEntries
失败),那么这个条目就被认为是"不可用"**的。如果此时发生选举,并且另一个节点成为新的领导者,新领导者可能不知道之前未复制成功的日志条目。
- ——这种情况难道不是用领导者的日志直接覆盖这些日志吗?
- 新的领导者覆盖日志条目:在没有安全措施的情况下,新的领导者可能会在自己的日志中添加新条目,并强制其他节点接受这些新条目,从而覆盖了旧的、未复制成功的条目。这就导致了状态机在不同节点上执行不同的命令序列。
本部分通过对哪些服务器可以被选为领导者的限制,完成了Raft算法。该限制确保了给定任期的领导者包含在之前任期中已经被提交的所有条目(来自第3图的领导者完全性属性)。在给出选举限制之后,我们进一步将提交规则描述得更加精确。最后,我们提供了领导者完全性属性的证明概要,并展示了它如何导致复制状态机的正确行为。
选举限制
在任何以领导者为基础的共识算法中,领导者最终必须存储所有已提交的日志条目。在某些共识算法中,如Viewstamped Replication [22],即使最初不包含所有已提交的条目,也可以选举出领导者。这些算法包含额外的机制,以识别缺失的条目并将其传输给新领导者,可以在选举过程中或之后的短时间内完成。不幸的是,这导致了相当大的附加机制和复杂性。Raft使用了一种更简单的方法,它保证了上一任期的所有已提交的条目在每个新领导者的选举时刻都可以找到,而不需要将这些条目传输给领导者。这意味着日志条目只从领导者流向追随者,领导者从不覆盖其日志中的现有条目。
领导者的日志具有完整性——包含所有已经提交的日志条目
Raft保证上一任期所有已经提交的条目在每个领导者的选举时刻都可以找到,而不需要将这些条目传输给领导者。
Raft使用投票过程来防止候选人在选举中获胜,除非它的日志包含所有已提交的条目。候选人必须与大多数集群联系,才能当选,这意味着每个已提交的条目必须至少存在于这些服务器中的一个。如果候选人的日志至少与大多数其他日志一样更新(其中“更新”在下面被准确地定义),**那么它将包含所有已提交的条目。**RequestVote RPC实现了这一限制:RPC包括有关候选人日志的信息,如果投票者自己的日志比候选人的日志更新,则投票者将否决其投票。
这里使用的是“已经提交”,但是不是未完成复制的日志也可能会造成不一致的状态机吗?为何只要求领导者具有完整的已经提交的日志,而不要求他有目前最新的日志(该日志可以没被提交)。
如何判断包含所有的已提交条目?
- 如果其日志至少与其他日志一样更新,那么它将包含所有已提交的条目。
如何保障该机制?
RequestVote
RPC实现,RPC中包含候选人日志的信息,如果投票者自己的日志比候选人的日志更新,那么将否则其投票。上述实现是保障选举出的领导者的日志与大多数其他日志是一样新的。
(但是似乎还是不能避免第一个例子的情况?存在某个领导人他还没来得及复制完这些日志,比如5个机器,他只复制了1个,那么剩下四个机器选举,还是能得到三个选票而胜出,胜出后,是不是还是可能导致领导者强制复制日志)
Raft通过比较日志中最后条目的索引和任期来确定哪个日志更为最新。如果日志的最后条目具有不同的任期,则具有较晚任期的日志更为最新。如果日志以相同的任期结束,则较长的日志更为最新。
更长的日志为最新是什么机制?
- 这里的更长指的就是index更大。
即,更新的判断机制:
- 任期越大越新
- 任期相同,看索引号
提交过去任期的条目
根据第5.3节的描述,领导者知道只要其当前任期的条目在大多数服务器上存储,就会提交该条目。**如果领导者在提交条目之前崩溃,将来的领导者将尝试完成复制该条目的过程。**然而,领导者不能立即得出结论,即一条来自先前任期的条目一旦在大多数服务器上存储,就已经提交。图8阐明了这样一种情况,即旧的日志条目虽然存储在大多数服务器上,但仍然可以被将来的领导者覆盖。
图8:一个时间序列显示了为什么领导者无法使用旧任期的日志条目来确定承诺。在(a)中,S1是领导者,并且部分复制了索引2处的日志条目。在(b)中,S1崩溃;S5被选为第三个任期的领导者,获得了S3、S4和自己的选票,并接受了索引2处的不同条目。在(c)中,S5崩溃;S1重新启动,被选为领导者,并继续复制。此时,第2个任期的日志条目已在大多数服务器上复制,但尚未提交。如果S1像(d)中那样崩溃,S5可以被选为领导者(获得S2、S3和S4的选票),并用自己在第3个任期的条目覆盖该条目。然而,如果S1在崩溃之前在大多数服务器上复制了当前任期的条目,如(e)中的情况,那么该条目被提交(S5无法赢得选举)。此时,日志中的所有前面的条目也都被提交了。
只保证大多数机器正常的时候是成功的,如果大多数失败了,那么协议并不能保证一致性。
为了消除类似于图8中的问题,Raft通过计数副本的方式不会提交来自先前任期的日志条目。**只有来自领导者当前任期的日志条目被计数副本提交;****一旦通过这种方式提交了当前任期中的条目,那么之前的所有条目都间接地因为日志匹配特性而被提交。**在某些情况下,领导者可以安全地得出一个较老的日志条目被提交的结论(例如,如果该条目存储在每个服务器上),但是为了简化Raft采取了更加保守的方法。
Raft在提交规则中面临额外复杂性,因为日志条目在领导者从之前任期复制条目时保留其原始任期号。在其他共识算法中,如果新领导者复制以前 “任期” 的条目,它必须使用新的“任期号” 进行复制。Raft的方法使得对日志条目进行推理更容易,因为它们随着时间的推移和跨日志保持相同的任期号。此外,Raft中的新领导者发送的来自以前任期的日志条目比其他算法少(其他算法必须发送冗余的日志条目来重新编号,使它们能够被承诺)。
安全论据
给定完整的Raft算法,我们现在可以更准确地论证Leader Completeness Property成立(这个论证基于安全证明;参见第9.2节)。我们假设Leader Completeness Property不成立,然后证明一个矛盾。假设在任期T下,领导者(leaderT)提交了一个来自其任期的日志条目,但该日志条目未被某个未来任期的领导者存储。考虑最小的任期U > T,其领导者(leaderU)未存储该条目。
这篇关于Raft论文阅读笔记+翻译:In Search of Understandable Consensus Algorithm的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!