Raft协议中文翻译(3)

写在前面

一直以来, 对Raft协议的理解感觉都没有非常到位, 本着眼过千遍, 不如手过一遍的原则, 利用空闲时间, 就自己把Raft翻译一遍, 加深自己的理解, 也方便其他的同学参考。
Raft协议英文版

计划分三部分:
第一部分: 原文的1 ~ 4章;
第二部分: 原文的第5 章;
第三部分: 原文的6 ~ 10 章

寻找一种可以理解的一致性算法

6 集群成员关系变化

到目前为止, 我们都假定集群的配置(参加一致性算符的服务器集合)是固定的, 实际上, 配置有时候是需要改变的, 比如, 当服务器故障的时候,要替换它或者改变复制的级别。尽管这可以通过下线整个集群,然后重启集群的方式来实现, 但是它会导致集群在变化过程中不可用。另外,如果有手工操作的步骤, 有操作错误的风险。 为了避免这些问题, 我们决定自动变更配置并且把它合并到Raft一致算法里面。

图10
为了使配置变更安全, 在变更过程中,一定不能有一个时间点会有两个leader被选举出来。 不幸的是, 任何将服务器从旧的配置转到新的配置都是不安全的, 不太可能自动的将集群内所有的服务器更换配置, 因为集群在转变过程中有可能分裂成两个独立的大多数。 (参考图10)。

为了保证安全, 配置变更必须使用两阶段的方法。 比如, 有些系统(像【20】)采用第一个阶段是旧的配置失效以便它不能处理客户端的请求, 然后在第二个阶段使新的配置生效。在Raft内, 集群首先转移到一个过渡的配置我们称之为joint 一致性, 一旦joint一致性被提交了, 系统转移到新的配置。 joint一致性联合了新旧两种配置:

  • 日志被复制被复制到两种配置下;
  • 从任何一种配置下的服务器都有可能成为leader;
  • 得出结论(选举和日志提交)需要在新旧两种配置分别达到大多数;

joint 一致性允许单个的服务器在不同的时间点在不同配置间转变而不需要承诺安全性, 而且, 它允许集群在配置变更的整个过程持续对外提供服务。

这里写图片描述
集群的配置使用特殊的复制日志条目进行保存和通信,图11 描述了配置变更的过程。当leader收到一个变更配置Cold到Cnew的请求, 它把joint一致性配置(图中的Cold,new)作为一条日志存储, 并且采用前面描述的机制复制该日志。 一旦一个服务器把新的配置放进它的日志它使用该配置做未来的所有决定(一个服务器总是使用最新的配置, 不管改配置是否已经提交)。 这意味着leader会使用Cold,new 规则来决定什么时候日志Cold,new应该被提交。 如果leader故障, 一个新的采用Cold或者Cold,new的leader被选举出来,这依赖于赢得选举的candidate是否收到了Cold,new。 在任何情况下, Cnew 在这段时间不能单方面做出决定。

一旦Cold,new被提交, Cold或者Cnew没有对方的同意无法做出决定,Leader Completeness Property 保证只有服务器包含了Cold,new 日志才能被选举为leader。现在让leader产生一个新的Cnew 日志并且复制到集群其他成员, 当该日志被看到的时候, 新的排至就开始起作用。 当新的配置在Cnew下被提交, 旧的配置就不再相关, 并且不在新的配置里面的服务器可以被关机。如图11 所示, 没有一个时间点Cold和Cnew都可以作出决定, 这确保了安全性。

还有3个关于重新配置的问题需要指出来。第一个问题是新加入的服务器有可能没有包含任何日志,在这种状态下加入集群, 它要花费相当长的时间来同步日志, 在这段时间内, 它不太可能提交新的日志。为了避免可用性的缺口, Raft在配置改变之前引入了一个额外的阶段, 在该阶段, 新的服务器作为不可投票的成员加入集群(leader复制日志给它们, 但是它们不被认为是大多数成员之一)。 一旦新的服务器赶上了集群中其他的服务器, 可以进行上面描述的变更配置。

第二个问题是集群的leader有可能不是新的配置的一部分。 在这种情况下, leader退出(返回到follower状态)一旦它完成了Cnew日志的提交,这意味着会有一段时间(在它提交Cnew的时候)leader管理一个集群但是它不属于该集群, 它复制日志给集群成员但是自己不包含在大多数内。转变发生在Cnew被提交的时候, 此时是新的配置第一次可以独立的执行操作(通常很有可能从Cnew选举出来一个leader), 在该时间点之前只有一个来自于Cold的服务器能被选举为leader。

第三个问题是已删除的服务器(不包含在Cnew)能破坏集群。 这些服务器不会收到心跳, 所以会造成超时并且开始一次新的选举, 它们会采用新的term发送requestVote RPC, 这会造成当前的leader变成follower状态。 有一个新的leader最终会被选举出来,但是已删除的服务器会再一次超时, 重复上述过程, 造成很差的可用性。

为了避免这个问题, 当服务器认为当前的leader存在, 它们会忽视RequestVote RPC, 特别是, 如果一个服务器在最小超时时间内收到了来自当前leader的RequestVote RPC, 它不会更新自己的term或者授权投票,这不会影响正常的选举, 在开始一轮选举之前, 一个服务器等待至少一个最小选举超时时间。 但是这种方式能够避免已经删除的服务器破坏集群: 如果一个leader能够从它的集群内收到心跳,它不会被大的term数字免职。

7 客户端和日志压缩

由于篇幅的原因本节被省略了, 但是资料可以从这篇论文的扩展版本得到【29】。它描述了客户端如何和Raft交互, 包括客户端如何找到Raft的leader以及Raft如何支持线性化语义【8】。扩展版本也描述了复制日志空间如何通过snapshot被回收。 这些方式被用于所有的基于一致性的系统, Raft的方法和其他的系统是类似的。

8 实现和演化

我们实现了Raft, 作为RAMCloud【30】存储复制状态机配置信息和支持RAMCloud failover坐标的一部分, 这个Raft实现包含大约2000行C++代码, 不包含测试, 评论或者空白行, 这些源码可以免费获得【21】。 还有大约25个独立的第三方Raft开源实现【31】, 依据本论文的草稿用在开发的不同阶段,此外, 各种公司都在部署基于Raft的系统。

本节剩下的部分采用3个准则来评估Raft: 可理解性, 正确性和性能

8.1 可理解性

为了衡量Raft相对于Paxos的可理解性, 我们在斯坦福大学高年级本科生和研究生的高级操作系统课程和U.C.伯克利大学的分布式计算课程进行了一次实验性的研究, 我们用视频记录了Raft课程和Paxos课程, 并且进行了相应的测试。Raft课程覆盖了这篇论文的内容, Paxos课程包括创建一个等价的复制状态机, 包括single-decress Paxos, multi-decree Paxos, 变配以及一些实际中需要的优化(比如选举leader)。考试测验了对算法的基本理解, 并且也要求学生们对特殊状况的处理, 每一个学生观看第一段视频, 参加相应的考试, 观看第二段视频, 参加第二个考试。 大约一半的参加者首先进行Paxos部分, 另外一半首先进行Raft部分,以便说明个人在表现和经验的获得上的差异,我们比较参加者的每一次测试的分数来判断参加者是否显示Raft更容易理解。

![表格1]
我们尽可能使Raft和Paxos的比较公平, 这个实验在两个方面更加有利于Paxos:43个人中间有15人之前有Paxos的经验, 并且Paxos的视频比Raft长14%。如表格1 总结, 我们已经采取了措施来减少潜在的偏见来源。 我们所有的资料可以得到,参考【26,28】。

图12
平均的, 参赛者的Raft 测试的分数比Paxos测试的分数多4.9分(可能60分, Raft的平均分数是25.7, Paxos的平均分数是20.8), 图12显示了个体的分数, 一个成对的t-测试表明, 我们有95% 的信息说真正的Raft分数分布比整整的Paxos分数分布至少高2.5分。

我们也创建了一个线性回归模型, 它依据3个因素预测学生的测试分数:参加哪一种测试. 之前Paxos经验的程度以及他们学习两种算法的顺序。这个模型预测对该测试的选择产生了12.5分的有利于Raft的差异。这远高于之前观察到的4.9分的差异, 因为很多学生之前有Paxos的经验,这能够比较大的帮助Paxos, 然而对Raft的帮助比较少。奇怪的是, 该模型也预测已经参加过Paxos测验的人会少6.3分, 尽管我们不知道为什么会这样,但是它具有统计的意义。

图13
我们在参赛者参加完测验后,也调查了参赛者来看一下哪一种算法让他们觉得更容易实现或者解释, 结果显示在图13. 绝大部分的参赛者称Raft会更容易实现和解释(44个人中的33个)。然而, 这些自我感觉得回答可能没有参赛者的分数那么可靠, 而且参赛者可能已经被我们Raft更加容易理解的假设而带有偏见。
一份详细的Raft用户调研可以在【28】得到。

8.2 正确性

我们已经开发了一份正式的文档换和一份在第五章描述的一致性机制的安全性的证明, 正式的文档【28】采用TLA+ 说明语言来使图2 的总结完全的精确, 它大约400行是证明的主题, 它对任何正在实现Raft的人也很有帮助。 我们已经机械的使用TLA证明系统【6】证明了Log Completeness Property, 但是, 这个证明依赖于还没有被机械的检查过(比如, 我们还没有证明文档的类型安全性)的不变性。而且, 我们也写了一份非正式的状态机安全属性的证明【28】, 它是完整的(它只是依赖于说明)并且相对的准确(它有3500单词长)。

8.3 性能

Raft的性能和其他的一致性算法类似, 比如Paxos。关于性能最重要的情况是leader复制新的日志, Raft完成日志复制使用了最少数目的消息(从leader到一半的集群成员的遍历)。有可能进一步提高Raft的性能, 比如, 很容易支持批量和管道的请求来得到更高系统吞吐和更低的时延, 在其他的算法的著作里面, 已经有各种优化措施被提出来, 其中很多适用于Raft, 但是我们把这些留到未来的工作里。

我们使用Raft实现来度量Raft选主的性能并未回答两个问题, 第一, 选举过程能不能很快的合并?第二, leader故障后系统挂掉的最短时间?

题14
要衡量选主,我们不断地5个节点的集群leader挂掉, 计时检测到leader挂掉和新的leader选举出来的时间(参考图14)。为了产生最坏的场景, 在每一次试验中, 服务器有不同长度的日志文件, 以便有一些candidate不适合成为leader, 而且, 为了更容易得到脑裂, 我们的测试脚本从leader在被终止之前触发一个同步的心跳RPC的广播(这接近于leader在挂掉之前复制新的日志)。leader在均匀的的随机的心跳间隔内被crash掉, 在所有的测试都是最短选举超时时间的一半, 这样最短的故障时间就是最短选举超时时间的一半。

在图14 上面的那个图显示在选举中少量的选举超时时间的随机性就最易防止脑裂, 在没有随机性下, leader的选举中由于很多额外的脑裂一直需要超过10秒钟。 只是添加5毫秒的随机性时间就有很大的帮助, 导致中位数是287毫秒的故障时间, 使用更多的随机性, 能够提高最坏情况的行为:用50毫秒的随机性时间最差情况完成的时间是513毫秒(超过1000个实验)。

在图14 下面的一幅图显示故障时间可以通过减少选举超时时间来减少, 用超时时间是12 ~ 24 毫秒, 平均花费35毫秒来选举leader(最长的时延花费152毫秒)。 但是, 将超时时间减少到小于这个点违反了Raft的计时要求:leader在其他服务器开始新一轮选举之前进行广播是有困难的, 这会造成不必要的leader变化并且降低整体的可用性。 我们建议使用一个保守点的选举超时时间, 比如150 ~ 300毫秒,这样的超时时间不太可能造成不必要的leader切换并还会提供好的可用性。

9 相关的工作

已经有无数的关于一致性算法的出版物, 很多可以归入如下的类别:

  • Lamport的Paxos最初的描述【13】, 并且试图解释更清楚的尝试【14,18,19】。
  • 阐述Paxos, 填补缺失的资料以及修改算法来为实现提供更好的基础【24,35,11】。
  • 实现一致性算法的系统, 比如Chubby【2,4】, Zookeeper【9, 10】, 和Spanner【5】。Chubby和Spanner的算法没有详细的公布, 尽管它们都声称基于Paxos, Zookeeper的算法很详细的公布, 但是它和Paxos有很大的不同。
  • 应用到Paxos的性能优化【16,17,3,23,1,25】。
  • Oki和Liskov的Viewstamped Replication (VR), 大约和Paxos同时被开发的另外一种一致性方法, 最初的描述【27】和分布式事物协议交织在一起, 但是核心的一致性协议已经被最近的更新【20】分离出来。 VR采用了基于leader的方法和Raft有很多相似性。

Raft和Paxos最大的不同在于Raft的强leadership: Raft使用leader作为一致性协议的一个必须的部分, 并且它集中了尽可能多的功能在leader上, 这种方法导致了更容易理解的简单的算法。 比如, 在Paxos, leader选举和基本的一致性协议是正交的,leader只是用来做性能优化, 不是达到一致性的条件。 然而,这就导致了额外的机制:Paxos包含了2阶段的基本的一致性协议和一个单独的leader选举机制。 相反, Raft把leader选举直接合并到一致性算法并且使用它作为一致性2个阶段的第一个阶段, 这使得它比Paxos有更少的机制。

像Raft一样, VR和Zookeeper都是使用基于leader的方式, 因此相比Paxos有更多Raft这方面发的优点。但是Raft比VR和Zookeeper有更少的机制,因为它将非leader的功能限制到最小。 比如, 在Raft, 日志的只有一个流向:从leader的AppendEntries RPC流出, 在VR, 日志有双向的流向(leader可以在选举过程中接收日志)), 这造成额外的机制和复杂性。 在Zookeeper出版的说明里, 日志也是双向传送, 传入和来自于leader, 但是实现看起来和Raft很相似【32】。

Raft和其他我们知道的的基于一致性日志复制算法相比, 有更少的消息类型, 比如, VR和Zookeeper可以定义10中消息类型, 而Raft仅有4种消息类型(2种RPC请求和它们的响应)。 Raft的消息比其他算法有点更密集, 但是更简单。 另外, VR 和 Zookeeper在leader变化时要传送整个term的所有日志, 需要额外的优化措施来使得额外的消息类型实际可用。

对集群成员改变的几种方法,在其他的作品里已经被提出或者实现, 包括Lamport的最初的设想【13】, VR【20】, 和SMART【22】。我们对Raft采用joint 一致性, 因为它利用了其他的一致性协议以便对成员的改变需要非常少的其它机制。 Lamport的α-based方法是Raft的一个可用选择因为它假定没有leader也能够达成一致性。对比VR和SMART, Raft的变更配置算法有它的优点, 成员的变化不需要限制正常的请求。相比之下, VR要停止所有的正常的进程,SMART施加了一个α-like的在突出的请求数目的限制。 Raft的方法比VR或者SMART增加更少的机制。

10 总结

算法通常被设计成以正确性, 有效性, 和/或简要作为主要目标。 尽管它们都是有价值的目标, 我们相信可理解性一样重要, 一直到开发者把算法应用到实际的开发中, 其他的目标才能够达成, 这不可避免的偏离和扩大出版物的原有样子。 只有当开发者深入理解了算法并且能产生直觉, 他们才能在实现中得到预期的属性。

在这篇文章里, 我们提出一个广泛接受但是费解的算法,Paxos,已经折磨学生和开发者多年的一个分布式一致性问题, 我们开发了一种新的算法, Raft, 我们展示了它比Paxos更容易理解, 我们也相信Raft能够为系统构建提供更好的基础。 使用了可理解性作为设计目标改变了我们设计Raft的方法, 因为在设计过程中, 我们发现我们自己一直重复使用一些技术, 就像分解问题和简化状态空间。这些技术 不仅提高了Raft的可理解性, 而且能更容易使我们确认它的正确性。

猜你喜欢

转载自blog.csdn.net/baijiwei/article/details/78819381