raft论文阅读理解&翻译

给新观众老爷的开场

大家好,我是弟弟!

redis哨兵中使用了raft算法。于是为了搞清楚redis 哨兵的工作原理,就在网上学习了一下raft算法。

网上已有 raft算法英文原版github上的raft中文翻译版,这两个版本我看了。
还有一个200页的完整详细版本,这个版本我没看过。
如果上面的链接打不开,可以尝试科学上网之后再试一下😏

自己翻译一遍的目的仅仅是为了加深学习印象,查漏补缺。
以下为用自己浅薄的英语知识的翻译的中文版 😂

寻找一种更容易理解的一致性算法(扩展版)

摘要

Raft 是一个为了管理复制日志的一致性算法。它的功能和(multi-)Paxos相当,并且效率相当。
但是它的结构与Paxos不同;这使Raft比Paxos更容易理解,并且为构建实际的系统提供了更好的理论基础。为了提高可理解性,Raft将一致性算法里的关键问题进行了分解,比如 leader选举,日志复制,安全性,并且强制实施了一个更强的一致性来减少必须考虑的状态的数量。一项用户研究的结果表明,对学生来说,Raft比Paxos更容易学习。对集群的成员变化,Raft引入了一项新的机制,使用重叠的大多数来保证安全。

1. 介绍

一致性算法允许一群机器像一个整体一样工作,就算其中一些机器故障也能正确工作下去。因此,一致性算法在可信赖的大规模软件系统中扮演着重要的角色。Paxos在过去10年中统治着一致性算法领域:
许多一致性实现是基于Paxos的 或者 受到了它的影响,并且Paxos 已经成为了给学生讲授一致性算法的主要示例。

不幸的是,尽管许多人试图让Paxos更容易理解,但依然十分难理解。而且,它的结构需要复杂的改变,才能在实际系统中应用。系统的构建者和学生都对Paxos感到头疼。
被Paxos折磨后,我们开始寻找一个新的一致性算法,为系统建设和教育提供更好的理论基础。我们的方法非同寻常,因为我们主要的目标是可理解性:我们能否为实际系统定义一个一致性算法,将它描述得明显比Paxos容易学习?而且,我们想要这个算法更加直观,这对于系统构建者来说非常必要。
重要的不仅仅是这个算法能正确工作,知道它为什么能正确工作同样重要。

一致性算法Raft就是这个工作的成果。为了设计Raft我们使用了特定的技巧来改善可理解性,包括问题分解(Raft 分解为 leader选举,日志复制和安全性几个子问题),减少状态(相对于Paxos,Raft减少了一些非确定性状态 和 服务间可能存在的非一致性状态)。一份包含了两所学校的43个学生的用户调研 展示了 Raft明显比Paxos更容易理解:在学习了两个算法之后,33个学生回答Raft的问题比回答Paxos的问题要好。

Raft在许多方面和现在的一致性算法很像(尤其是Oki and Liskov’s Viewstamped Replication [29, 22]),但是它有几个与众不同的特点:

  • 强leader: Raft比起其他一致性算法使用了更强的leader。比如说,日志条目只从leader节点流向其他服务节点。这简化了复制日志的管理,并且使Raft更容易理解
  • leader选举:Raft使用随机定时器来选举leader。这仅仅在各个一致性算法都需要的心跳功能中加入了很少的机制,就能简单、快速地解决冲突。
  • 成员变更:Raft解决集群中成员的变更,使用了一个新的 joint consensus 方法(使两种配置的大多数节点在过渡期间重叠)。这允许集群在配置改变期间能正常运行。

我们相信Raft比Paxos和其他一致性算法都更好,不管是出于教育目的还是作为生产实践的理论基础。它比其他算法都要简单并且更好理解。Raft的算法描述足够完整,能满足实际系统的需要;Raft有几个开源实现并且已被几个公司使用;Raft的安全性已经有正式的详细的描述 并且已经被证明。并且Raft的效率与其他的一致性算法相当。
论文剩下的部分会介绍复制状态机问题(第2段),讨论Paxos的优缺点(第3段),描述为了可理解性我们使用的方法(第4段),介绍Raft一致性算法(第5-8段),评估Raft(第9段),讨论相关的工作(第10段)。

2. 复制状态机

一致性算法是在复制状态机的背景下提出的[37]。在这个方法中,一个集群复制同一个状态下相同的拷贝并且可以在一些节点挂掉的情况下继续运行。
复制状态机在分布式系统中被用来解决许多种容错问题。举个例子,有一个leader的大规模系统集群,比如 GFS[8],HDFS[38],和RAMCloud[33],都使用了单独的复制状态机来管理leader选举和存储配置信息,就算leader挂掉了集群也必须能正常工作。比如 Chubby、ZooKeeper就是复制状态机的例子。
复制状态机通常通过使用复制日志来实现,如图例1所示。

图例1

图例1:复制状态机结构。一致性算法管理一条包含状态机命令的复制日志。状态机按日志中相同的顺序处理命令,所以他们可以产生相同的输出

每一个服务节点存储了包含一系列命令的日志,并且状态机会顺序执行这些命令。每个日志包含了相同顺序的相同命令,所以每个状态机以相同的顺序处理命令。由于状态机是确定的,每个状态机都计算相同的状态并且产生相同顺序的输出。
保持复制日志的一致性是一致性算法的任务。一个服务节点上的一致性模块从客户端接受命令并且将他们添加到日志中。它和其他服务节点上的一致性模块沟通交流来保证每个日志最终都包含了相同顺序的相同请求,即便与部分服务节点沟通交流失败。一旦命令被正确的复制,每个服务节点的状态机会按日志顺序来处理它们,并且输出会被返回给客户端。结果就是,多个服务节点形成了一个高可靠性的状态机。
实际系统中的一致性算法通常包括以下特性:

  • 在非拜占庭条件下(包括网络延迟,分区,丢包,重复,乱序),保证安全(永远不会返回一个错误的结果)
  • 高可用性,只要大部分服务节点可以运转并可以彼此通信、且可以和客户端通信,系统就能正常工作。因此。一个包含五个服务节点的集群,可以容忍任意两个服务节点挂掉。当服务节点停止工作会被认为服务节点挂了。挂掉的服务节点稍后可以靠存储在其他地方的数据恢复状态并重新加入集群中。
  • 不依靠时间来保证日志的一致性,错误的时间或者极其严重的消息延迟,最坏只能造成可用性问题。
  • 在一般情况下,当集群中大多数服务节点在一轮远程调用中响应了命令,命令就可以完成了。少数低速的服务节点不会影响到整体系统的性能。

3. Paxos有什么问题

在过去十年里,Leslie Lamport的Paxos协议几乎成为了一致性的同义词。这个协议被广泛的用于课程教学,并且非常多的一致性实现使用这个协议作为起点。Paxos是第一个定义了可以在一次决议中达成一致的协议,比如单条复制日志条目。我们把这个叫做 single-decree Paxos. Paxos 组合多个基于此协议的实例来加速一系列决策(multi-paxos).Paxos保证了安全性和活性,并且支持集群成员变更。它的正确性已经被证明,并且在正常情况下工作效率高。
不幸的是,Paxos有两个显著的缺点。

第一个缺点就是Paxos特别难理解。完整版本的说明是出了名的难懂,只有少部分人能成功理解它,并且需要花费巨大的努力。结果是,已经有一些用更简单的术语来解释Paxos的尝试。他们的解释聚焦在 single-decree 子集里,至今成功理解它仍然具有挑战性。通过一份正式的 关于 NSDI 2012 参会者的调查,我们发现很少人会对Paxos感觉到舒服,即使在经验丰富的研究者中。我们在Paxos中挣扎,我们没能理解完整的Paxos 直到 我们阅读了几份简化版的解释并且设计了我们自己的替代协议,这大概花了近1年的时间。

我们假设Paxos难懂是因为它选择了 single-decree 子集 作为了它的基础。signle-decree Paxos 是晦涩难懂的,它被分成两个阶段,没有一个简单直观的解释并且不能单独的去理解。因此,它很难直观的呈现出为什么 single-decree 协议能正确工作。构成multi-paxos 的规则增加了额外的复杂度。我们相信在多决策中达成一致性的问题可以用其他更直接、明确的方式分解。

Paxos的第二个问题就是它没有为生产实践提供一个好的理论基础。一个原因是 没有一个广泛认同的 multi-paxos 算法。Lamport 的描述几乎都是关于 single-decree paxos 的,他概述了可能实现multi-paxos的方法,但是缺失了许多的细节。已经有了许多完善paxos的具体尝试,比如说[26],[39]和[13],但是都各不相同并且与Lamport的概述也不同。像Chubby这样的系统实现了一个类似Paxos的算法,但是他们没有公开实现的细节。

而且,Paxos的结构对于构建实际系统来说是糟糕的;这是分解single-decree的另一个结果。举个例子,独立地选择一组日志条目并且将它们融合成一组顺序的日志,增加了复杂度,但只有一点点收益。围绕日志来更简单有效的设计系统的方法是,日志里新的条目按固定的方向被顺序的追加。另一个问题是,Paxos使用了点对点的方法作为它的内核(虽然最后建议使用弱领导的形式来优化性能)。在一个简单的只有一条决策会被制定的世界里,这是有意义的,但是很少有实际系统使用这种方法。如果一系列决策必须被制定,先选一个leader再让leader来协调决策会更简单更快速。

结果是,实际系统与Paxos只有一点点相似。以Paxos开始的每一个实现,后来都发现实现它太难了,并且开发出了明显不同的结构。这很费时间、容易出错并且加剧了理解Paxos的难度。Paxos‘s 的表达式对于在理论上证明它的正确性是比较优秀的,但是真实的实现与Paxos大不相同,理论证明则显得没什么帮助了。下面来自Chubby实现里的注释是很典型的:

在Paxos的算法描述与现实世界系统里的需求之间存在着鸿沟…现实系统最终建立在一个未经证明的协议上。

因为这些原因,我们得出的结论是,不管是对于系统构建还是教育,Paxos都没能提供一个好的理论基础。基于一致性算法在大规模软件系统中的重要性,我们决定看看我们能否设计一个比Paxos有更好的一致性替代算法。Raft是这个实验的结果。

4. 为了可理解性而做出的设计

我们设计Raft有好几个目标:它必须为系统构建提供完整的实践理论基础,这样能显著地减少开发者的工作量;在所有条件下它必须是安全的,并且在典型的运行条件下都是可用且高效的。但是我们最重要的目标并且难度最大的挑战–是可理解性。必须让大部分读者都能容易的理解这个算法。另外,它必须能直观呈现这个算法,这样系统构建者能作出 在生产实践中需要的扩展。
在设计Raft时,在许多点上我们需要在备选方法中选择。在这种情况下我们基于可理解性来评估备选方法;每个备选方案解释的难易程度(举个例子,它的状态空间的复杂度是多少?是否有隐晦的点?),并且对于一个读者来说 完全理解这个方法和它隐晦的点 是否容易?
我们认识到这样的分析判断是带有高度的主观性的,尽管如此,我们使用了常见的两个技巧。第一个技巧就是众所周知的 问题分解的方法: 只要有可能,我们就将问题分解成更小的子问题,让各个子问题能被解决,被解释,被相对独立的理解。举个例子,我们将Raft分解成了 leader选举,日志复制,安全性和成员变更。
我们的第二个方法就是通过减少需要被考虑的状态的数量 来简化状态空间,这样能使系统条理更加清楚并且尽可能的排除不确定性。特别的,日志不允许产生空洞,Raft限制了日志可能产生空洞的方式。尽管在许多场合我们尝试减少不确定性,但是有部分场合是,不确定性可以实实在在的提高可理解性。特别是,随机化的方法会引入不确定性,但是他们可以通过相似的方式来处理所有可能的选择,以此来减少状态空间(“选择哪一个,都可以,没啥问题”)。我们使用随机化来简化Raft的leader选举算法。

5. 一致性算法Raft

Raft是一个管理复制日志的算法,日志的形式就是第二段中描述的那样。图2 总结了算法的精炼形式作为参考,并且图3列出了算法的关键属性;图例的这些元素在接下来的段落里会被分段讨论。
在这里插入图片描述

图2: 一份关于Raft一致性算法的精炼总结(不包括成员变更和日志复制)。段落数字 如 §5.2 表示这个特性在5.2段落会被讨论。一份正式的详细说明[31]更清晰的描述了这个算法。

特性说明
Election Safety(选举安全性 ) 在一个任期里最多一个leader被选举 .§5.2
Leader Append-Only(leader只追加日志) leader从不覆盖或删除它日志里的条目;它只追加新的条目.§5.3
Log Matching Property(日志匹配) 如果两个服务节点的日志里都包含了一条拥有相同任期和序号的条目,那么在这两个服务节点的日志里,该条目之前的所有日志条目都是相同的
Leader Completeness(leader完整性) 如果一个日志条目在某一任期里被提交了,那么这个日志条目会出现在后续每一任leader的日志里。
State Machine Safety如果一个服务节点在状态机给定序号处应用了一个日志条目,其他服务节点将不会在同一个序号处应用不同的日志条目 §5.4.3

图3: Raft保证每一条特性在任何时刻都是正确的。段落数字显示了每一个特性在哪一个段落被讨论了。

Raft一致性的实现是,先选一个leader节点,然后让leader负责管理复制日志。leader从客户端接受日志条目,并将日志条目复制到其他服务节点,并且在安全的时候告诉其他服务节点应用日志条目到他们的状态机里。举个例子,leader可以决定新条目在日志里的位置,而不用咨询其他服务节点,并且日志流动的方向很简单,从leader节点流向其他服务节点。leader节点可以挂掉或者与其他服务节点失去链接,这种情况下一个新的leader会被选举出来。
在采用leader机制的背景下,raft将一致性问题分解成了3个相对独立的子问题,在接下来的子段落里将会详细讨论:

  • Leader选举: 一个新的leader必须被选举出来,当现任leader挂掉的时候(§5.2)
  • 日志复制: leader必须接受客户端发来的日志条目,并且将它们复制到集群里的其他服务节点,并且强制其他服务节点的日志与leader的日志保持一致。(§5.3)
  • 安全性: 对于Raft来说关键的安全特性是状态机的安全特性(在图3中有描述):如果任意一个服务节点已经应用了一条特定的日志条目到它的状态机里,那其他的服务节点就不能在相同位置的应用不同的日志条目。5.4段 描述了Raft如何确保这个特性;解决的办法是,在5.2段描述leader选举机制里,引入了额外的限制条件。

在展示了一致性算法之后,该段会讨论 关于系统可用性 和 时间在系统中的重要性。

5.1 Raft基础

一个Raft集群包含了许多服务节点,典型的是5个节点,这样系统可以容忍2个节点挂掉。在任意时刻,每个服务节点的状态是这三者之一: leader,follower,candidate。一般情况下系统里只有一个leader,剩下的都是follower。Followers 是被动的:他们不发起请求,只处理来自leader和candidates的请求。leader处理所有客户端的请求(如果一个客户端连上了一个follower,follower会将客户端重定向给leader)。第三个状态,candidate,被用来选举新的leader(5.2段中有描述)。图4 展示了这些状态和状态转移;状态转移接下来会讨论。

在这里插入图片描述

图4: 服务节点状态。Followers 仅仅响应其他服务节点的请求。如果一个follower收不到消息了,它会变成一个 candidate 并且发起一场选举。一个candidate收到来自集群中半数以上的选票,它将成为新一任leader。Leaders 会正常运转直到它挂掉。

Raft像图5展示的那样。将时间分割成不同长度的任期。

在这里插入图片描述

时间被分割成任期,并且每一任期,从该任期的选举开始。选举成功后,一个独立的leader将接管集群并直到它的任期结束。有时候 没有candidate能赢得选举, 本次选举将失败。任期的过度可能在不同的时间被不同的服务节点观察到。

任期被连续的数字标号。每一任期从选举开始,每次有一个或多个candidates参与竞选leader 就像5.2段里描述的那样。如果一个candidate 赢得了选举,然后在本轮任期里,它将作为leader节点向外提供服务。有时候一场选举会有分票的情况(没有cadidate获得超过半数以上的选票),这种情况下本轮任期没有leader;一个新的任期(伴随着一场新的选举)将在不久后开始。Raft保证一轮任期里最多只有一个leader。
不同的服务节点在不同的时间点可能会观察到任期过渡,在一些场景下,一个服务节点可能观察不到该任期的选举,甚至观察不到整个任期。在Raft中任期作为逻辑时钟存在,它允许服务节点发现过期的信息,比如过期的leader。每一个服务节点存储了一个单调递增的当前任期号。各个服务节点会交换彼此的当前任期号;如果一个服务节点的当前任期号小于另一个服务节点的,那么它将当前任期号更新成较大的那一个。如果一个candidate或者一个leader发现它的任期号过时了,它会立即转变成 follower状态。如果一个服务节点接收到了带有过期任期号的请求,它会拒绝这次请求。
Raft的服务节点通过RPC来进行交流,并且基本的一致性算法只要求两种RPC。一个是RequestVote RPC(请求投票RPC),会被candidate在选举阶段调用(5.2段)。另一个是 AppendEntries(追加日志条目的RPC),在leader进行日志条目的复制 或 提供一种形式的心跳时 调用(5.3段)。第7段增加了第三个RPC用来在服务节点之间传输快照。 当服务节点在 规定时间内没收到回复时,将会进行重试,重新调用RPC,并且他们会并发地发起RPC调用,为了优化性能。

5.2 Leader选举

Raft使用了一种心跳机制来触发Leader选举。当服务节点启动时,他们以follower身份开始。一个服务节点只要能收到有效的来自Leader或candidate的RPC调用 就会 一直保持着follower状态。Leader周期性的给所有follower发送心跳(一个不带任何日志条目的AppendEntries RPC)以此维持它的Leader身份。如果一个follower在一轮选举超时时间内都没有收到有效RPC调用,它就假设leader已经挂掉了,并会发起一场选举来选出新Leader。

为了发起一场选举,一个follower会自增它的当前任期号,并且转变成candidate状态。然后它会给自己投一票,并发地向集群中的其他服务节点发起 RequestVote RPC调用。 一个candidate维持这个状态,直到下面3件事里的其中一件发生:1. 它自己赢得了选举, 2. 其他服务节点赢得了选举, 3. 一轮选举超时时间又过去了,没有leader被选出。 这些结果会在接下来的段落里面被分别讨论。

  1. 一个candidate在一个任期下,如果收到了来自全部集群里半数以上的投票,它将赢得这场选举。每一个服务节点 在一个给定的任期下,最多投一个candidate,哪个candidate的RequestVote RPC先来,就投哪个candidate(5.4段增加了额外的投票限制)。超过半数的投票规则确保了一个任期里最多一个candidate会赢得选举(图3里描述的 Election Safety 特性)。一旦一个candidate赢得了一场选举,它将成为leader。然后它会向其他所有服务节点发送心跳来维持它的leader地位,并且阻止新的选举被发起。

  2. 在一场选举中,一个candidate可能收到来自leader节点的AppendEntries RPC调用,如果这个leader的任期大于等于这个candidate的当前任期,这个candidate就会识别出这是一个合法的leader并且自己变成follower状态。如果这个leader的任期比candidate的当前任期小,那candidate将会拒绝这次RPC调用并继续维持candidate的状态。

  3. 第三种情况就是这个candidate在本次选举中没有赢也没有输;如果许多个follower在同一时间变成candidate,选票就会被分走,导致没有一个candidate获取到了超过半数的选票。当这种情况发生,每一个candidate将会在一轮选举超时后,重新发起一轮新的选举并且会自增自己的当前任期号并且会向其他节点发起一轮新的 RequestVote RPC调用。然而,如果不采取额外的措施,分票可能会无限重复下去。

Raft使用随机选举超时时间来确保分票的发生概率是很低的,并且就算被分票了也能快速的被解决。为了在最开始就阻止分票,选举超时时间是在一个固定的范围里面随机选择的(比如在150-300ms中随机一个选举超时时间)。这样可以分散超时的服务节点,在大多数情况下只有一个服务节点会超时;它会在其他服务节点超时之前,发起选举、赢得选举并向其他所有服务节点发送心跳。当分票的情况出现时,可以用同样的方法来处理,每一个candidate在一轮选举超时后,会发起一场选举并且会重新随机下一次选举超时时间。这会减少新一轮选举里分票的可能性。9.3段展示了这个方法能快速的选举出一个leader。

选举是 可理解性指导我们在多种设计里做出选择的一个例子。最开始我们计划使用一个排名系统:每一个candidate被分配一个唯一的排名,这个排名被用来在竞争的candidate中进行选择。如果一个candidate发现其他candidata有更高的排名,它会转变成follower状态,所以高排名的candidate更容易赢得一场选举。我们发现这个方法制造了一个隐晦的可用性问题(当一个更高排名的服务节点挂掉后,一个低排名的服务节点需要超时来再次成为一个candidate,但是如果它发生的太早,它会重置选举leader的进程)。我们对这个算法做了几次调整,但是每次调整之后都会有新的极端的情况出现。最后我们得出了结论,随机重试的方法更显而易见且更能被理解。

5.3 日志复制

一旦一个leader被选举出来了,它就会开始服务于客户端的请求。每一个客户端请求包含了一条需要被复制状态机执行的命令。leader将命令作为一个新的条目追加到它的日志里,然后并发地向其他服务节点发起AppendEntries RPC调用,让其复制这个条目。当这个条目被安全地复制之后,leader应用这个条目到它的状态机里并且向客户端返回执行的结果。如果follower崩溃或者运行得太慢,或者网络丢包,leader会无限重复 AppendEntries RPC调用(就算leader节点已经将结果回复给客户端) 直到所有的follower都存储了这个日志条目。

日志像图6展示的那样被组织起来。每一个日志条目存储了一条状态机命令 和 从leader接收这个日志条目时的任期编号。日志条目里的任期编号被用来发现不同服务节点间不一致的日志,并被用来保证图3中的一些特性。每一条日志条目都有一个整数序号用来标示它在日志里的位置。
在这里插入图片描述

图6: 日志由日志条目组成,并且会按顺序标号。每一个日志条目包含了被创建时的任期 和 一条状态机命令。 当一个日志条目可以被安全的应用到状态机时,这个日志条目会被认为是已提交的。

Leader来决定什么时候可以安全的应用日志条目到状态机里;比如一个被提交的日志条目。Raft保证被提交的日志条目是持久化的 并且最终会被所有可用的状态机执行。一个日志条目一旦被大多数服务节点复制(像图6中的条目7) ,就会被leader提交。leader的日志里在此之前的日志都会被提交,包括被之前leader创建的日志条目。5.4段讨论了在leader发生变更的时候使用这个规则的微妙之处,并展示出对提交的定义是安全的。leader节点保持追踪被提交的最大的日志数组的序号,并且将其包含在未来的 AppendEntries RPC里(也包括心跳的)以便其他服务节点最终发现它。一旦一个follower认识到一个日志条目已经被提交,它会将这个日志条目应用到它的本地状态机里(按日志顺序)。

我们设计Raft的日志机制,用来维持 在不同的服务节点里的日志的高度一致性。不仅仅是因为它简化了系统的行为,让其更能被预测,还因为它是确保安全性的一个重要的组成部分。Raft维护了下述特性,这些特性一起组成了图3中的 Log Matching Property :

  • 在不同的服务节点的日志里,如果两个条目有一样的序号和任期,那么他们存储着相同的命令
  • 在不同的服务节点的日志里,如果两个条目有相同的序号和任期,那么在此之前的所有日志条目都是相同的。

第一个特性 遵循了这样一个事实,在给定任期里给定日志序号上只会产生最多一个日志条目,并且日志条目从来不会改变他们的位置。第二个特性使用 AppendEntries RPC执行的简单的一致性检查 来保证。当发起AppendEntries RPC调用时,leader将 要发送的新条目的前一个日志条目的日志序号和任期包含在AppendEntries RPC中。如果follower在他的日志没有找到相同序号和相同任期的日志条目,它将拒绝新的日志条目。这个一致性检查就像是一个归纳步骤: 最开始空的日志状态满足 Log Matching Property,并且只要有日志新增就会有一致性检查。所以,只要AppendEntries RPC返回成功,那leader知道 在这个新的日志条目之前 follower的日志 和 leader的日志 是一样的。

在一般情况下,leader与follower是保持一致的,所以AppendEntries RPC的一致性检查不会失败。
然而,当leader挂掉的时候,可能会造成日志不一致(老leader可能没有将它的日志成功的进行复制)。这些不一致性会在一系列的leader和follower节点崩溃后叠加。图7展示了follower的日志与新leader日志不一致的可能的情况。一个follower可能丢失曾经出现在leader里的日志,也可能拥有额外的没有出现在leader里的日志条目,或者两者都有可能,丢失或拥有额外的日志条目 这种情况可能跨越多个任期。

在这里插入图片描述

图7:当最上方的leader生效时,follower的日志状态可能是 (a-f)中的其中一种。每一个小格子表示一个日志条目;格子里的数字表示这个日志条目的任期。一个follower可能丢失日志条目(a-b),也可能拥有额外的未提交的日志条目(c-d),或者两者都有(e-f)。举个例子, f 这种情况在何种情况下会发生,如果这个服务节点在任期2的时候是leader节点,并且在任期2的时候增加了几个日志条目到它的日志里,然后它在提交这些日志条目之前挂掉了。它随后迅速的重启了,在任期3里成为了leader,并且在任期3里又追加了几条日志条目到它的日志里。在任期2或者任期3的日志被提交前,它又挂了,在随后的几个任期里都处于挂掉的状态。

在Raft中,leader处理与follower不一致的日志的方式是,强制follower复制自己的日志。这意味着,follower中与leader冲突的日志,会用leader的日志来重写。5.4段将展示在更多的限制条件下,这种方式是安全的。
为了与follower中的日志达到一致,leader必须找出与follower最近一致的日志,删除follower中在此之后的所有日志条目,并且向follower发送在此之后的所有的leader的日志条目。这些动作都发生在AppendEntries RPC执行的一致性检查里。leader对每一个follower都维护了一个 下一个序号, 用来表示leader给每一个follower发送的下一个日志条目是哪个。当一个leader节点刚生效时,它会初始化所有follower的下一个序号为leader的最后一个日志条目的序号+1 (就像图7里的序号11)。如果一个follower的日志与leader的不一致,那么AppendEntries RPC里的一致性检查将会失败,再被拒绝一次之后,leader会减小对应follower的 下一个序号,并重新调用AppendEntries RPC。最终leader与follower 会匹配上一个正确的下一个序号。当这发生的时候,AppendEntries RPC将会返回成功,并且移除follower中所有的冲突日志条目并追加leader的日志条目。一旦AppendEntries RPC调用成功,follower的日志与leader的日志就保持一致了,并且本轮任期里会一直保持。

如果需要,协议可以优化调整以减少被拒绝的AppendEntries RPC的次数。举个例子,当一个AppendEntries RPC被拒绝的时候,follower可以将冲突日志的任期的第一条日志序号返回。有了这个信息,leader可以一次性跳过冲突日志所在的整个任期的日志;当有冲突日志条目时,一个任期的日志只需要一次AppendEntries RPC调用,而不是每一个日志条目都需要一次AppendEntries RPC调用。在实践中,我们怀疑这种优化的必要性,因为宕机是很少发生的并且不会有特别多的不一致的日志条目。

在这种机制下,当一个leader生效时,它不需要采用特殊的动作来重新存储一致的日志,并且日志能 通 过AppendEntries RPC里一致性检查的失败回复 来自动的趋于一致。一个leader从来不覆盖和删除自己的日志条目(图3中的 Leader Append-Only Property)。

这个日志复制机制展示了第2段中描述的一致性属性;Raft可以接收,复制和应用新的日志条目,只要集群中的大多数服务节点能正常工作;一般情况下一个新的日志条目可以在一轮对集群中半数以上的服务节点的RPC调用中被复制,并且少量的慢速的follower不会影响整体的性能。

5.4 安全性

在前面的段落里描述了Raft如何选举leader以及复制日志条目。然而,这些机制的描述还不足够保证每一个状态机以相同的顺序执行相同的命令。举个例子,当一个leader提交了几个日志条目后,某一个folower可能是不可用的(也就是没有复制到leader的日志),然后它可能会被选举成leader并且覆盖这些日志条目;产生的结果就是,不同的状态机可能执行不同的命令序列。

这段内容将会通过增加一个额外的选举leader的限制条件,来完成全部的Raft算法。这个限制保证了在任意任期里的leader会包含所有在之前任期里被提交的日志条目(图3中的 the Leader Completeness Property)。 在给定的选举限制条件下,我们将更清晰准确的描述日志提交的规则。最后,我们会给出the Leader Completeness Property 的简要证明并且展示它如何将复制状态机引向正确的行为。

5.4.1 选举限制

在任何建立在leader选举基础上的一致性算法里,leader必须最后存储所有的已经被提交的日志条目。在一些一致性算法里,比如像 Viewstamped Replication[22], 一个leader可以在它不包含所有已提交条目的情况下被选举。这些算法包含了额外的机制来识别丢失的日志条目并将它们传给新的leader,不管是在选举过程中,还是在此之后。不幸的是,这将导致需要考虑额外的机制和复杂性。Raft使用了一个更简单的方法来保证在新leader被选出的时候就已经包含了之前所有任期里提交的日志 ,而不需要将这些日志转移给leader。这意味着日志条目只有一个流动方向,就是从leader节点流向其他follower节点,并且leader从来不会覆盖已存在的日志条目。

Raft使用投票的方法来阻止candidate赢得一场选举,除非它包含了所有已提交的日志条目。一个candidate为了选举必须联系集群中半数以上的节点,这意味着,每一个被提交的日志肯定会在 这些服务节点中的至少一个节点里出现过。如果candidate的日志 比 集群中半数以上服务节点的日志 至少一样新(日志的新旧在下面会详细描述),然后它才会持有所有已提交的日志条目。RequestVote RPC实现了这个限制:RequestVote RPC包含了 candidate的日志信息,如果投票者的日志比candidate的日志要新就会拒绝这次投票。

Raft通过比较两个日志里最后一个条目的序号和任期来判断哪一个日志更加新一点。如果两个日志的最后一个条目拥有不同的任期,那么任期大的那个日志更加新一点。如果两个日志的最后一条日志有相同的任期,那序号更大的日志更加新一点。

个人理解:这里隐藏地解决了这样一个case,假设集群中的某个节点,很长一段时间与其他节点失去联系,它陷入无限的选举超时并自增自己的任期号,然后如果它的任期号比其他服务节点的任期号都大之后,它又突然能与集群中的其他服务节点联系上了,并且它发起了一场新的选举。因为上述日志新旧程度的关系,如果它的日志比半数以上其他服务节点的都旧,就算它的任期号大,它也不会赢得选举,并且它发起的选举会被其他服务节点拒绝。
这个拥有大任期号,但日志老旧服务节点,会不会通过接收到来自小任期号但日志较新的leader的AppendEntries RPC后,发现自己过时了,然后主动变成follower呢? 可能是这样吧 🤔

5.4.2 之前任期里提交的日志条目

像5.3段里描述的那样,一个leader知道当前任期里的日志条目 只要被存储在半数以上服务节点里 就是被提交的日志。如果一个leader在提交一个日志条目之前挂掉了,未来的leader可能会试图完成这个条目的复制。然而,一个leader不能立即判断出先前任期里被存储在半数以上服务节点里的日志是否被提交。图8展示了一种情况,存储在了半数以上服务节点的未提交的老日志条目,可能会被未来的leader覆盖。

为了杜绝类似图8中的问题,Raft从不通过计算复制的数量来直接提交之前任期的日志。只有leader当前任期的日志会通过计算复制数来直接提交;一旦一个当前任期的日志条目用这种方式提交了,那之前所有的日志条目都会被间接提交,因为 the Log Matching Property。一些情况下leader可以安全地判断出一个老的日志条目是否被提交过(举个例子,如果一个条目被存储在半数以上的服务节点),但是Raft为了简单起见,采取了一个更为保守的方法。
在这里插入图片描述

图8: 一个时间顺序图展示了为什么leader节点无法判断先前任期里的日志条目是否提交。
在(a)时,S1是leader并且部分复制了序号为2的日志条目。在(b)中,S1挂了;任期3中,通过S3,S4和S5自己的投票,S5在被选举为leader,并且在在序号为2的的地方接收了一条不同的日志条目。
在( c )中,S5挂了,S1重启,在任期4里,S1被选举为leader,并且继续复制。在这个时候,来自任期2的日志条目已经被复制到半数以上的服务节点,但是还没有被提交。在(d)中,S1又挂了,S5被选举为leader(通过S2,S3,S4的投票)并且会用它的任期3里的日志条目来覆盖其他服务节点的 序号为2的日志条目。然而,在(e)中,如果S1在它挂掉之前把它当前任期的日志条目复制到半数以上的服务节点的话,然后这些条目将会被提交(因为此时S5已经不能赢得选举)。在这个时候,之前所有的日志条目将被妥善的提交。

PS.

  • 直接提交之前任期里的日志条目,隐藏了一个数据一致性的问题,在上述极端case中,该条已被提交的日志条目可能会被后面任期的leader覆盖,会造成数据不一致,已提交日志不应该丢失!

  • 直接提交当前任期里的日志,能够间接提交之前任期里的日志,并且可以规避这个问题。
    参考:

  1. CSDN 其他博主的博客 Raft 5.4.2 Committing entries from previous terms的理解
  2. 知乎 raft算法中,5.4.2节一疑问?

Raft在日志条目的提交规则中引入了额外的复杂性,因为日志条目需要保留他们原始的任期号当一个leader从之前任期里复制这个日志条目时。在其他的一致性算法里,如果一个新leader重新复制之前任期的日志条目,它必须以新的任期号来进行。Raft的方法能更简单的解释日志条目,因为他们在所有日志里维持原来的日志条目的任期号。额外的,Raft中新的leader比其他算法 发送 更少的之前任期里的日志条目(其他算法必须发送冗余的日志条目,在他们被提交之前重新编号)。

5.4.3 安全性讨论

在给出完整的Raft算法后,我们现在可以更加详细的讨论 the Leader Completeness Property 了(这个讨论以 安全性证明为基础;详情见9.2段)。我们假设Raft不具备 the Leader Completeness Property ,然后我们可以推出矛盾来(反证法)。假设任期T的leader-T在它的任期里提交了一个日志条目,但是这个日志条目没有被之后任期的leader存储。假设 未来没有存储这个日志条目的leader的最小任期号是U, U > T。

  1. 当leader-U发起选举时,这条被提交的日志条目必须没有出现在leader-U的日志里.(leader从来不会删除或覆盖自己的日志条目)
  2. leader-T 复制这个日志条目到了集群中半数以上的服务节点里,并且leader-U赢得了集群中半数以上的投票。因此,至少有一个服务节点(“投票者-V”) 同时接收了leader-T的日志条目,并且又给leader-U投票了,就像图9显示的那样。这个投票者-V是造成矛盾的关键。
  3. 这个投票者-V在投票给leader-U之前必须已经接收了leader-T中提交的这条日志条目
  4. 当这个投票者-V给leader-U投票时,它仍然存储着这个日志条目,(因为我们假设U是最小的不包含这个日志条目的任期),leader从来不会移动日志条目,并且follower仅在与leader冲突的时候会移动日志条目
  5. 投票者-V把选票投给了leader-U,这就说明leader-U的日志必须至少和投票者-V一样新。这会导致了出现下面2个矛盾里的其中一个。
  6. 如果投票者-V跟leader-U最后一条日志的任期相同,那么leader-U的日志必须至少和投票者-V的一样长,所以leader-U的日志里包含了投票者-V的每条日志条目。这是一个矛盾,因为投票者-V包含了一条leader-U假设不包含的日志条目。
  7. 否则,如果leader-U的最后一条日志条目的任期必须比投票者-V的日志条目任期大。此外,它还比Leader-T的大,因为投票者-V的最后一条日志条目的任期至少和T一样大(投票者-V包含了来自leader-T提交的日志条目)。创建了leader-U里最后一条日志条目的更早的leader节点的日志里必须包含该条已提交的日志条目在它的日志里(通过前提假设得出的结论,LeaderU是不包含该条日志条目最小的那个leader,在这之前的leader都包含那条日志条目)。所以,通过the Log Matching Property,leader-U的日志必须包含那条已经提交的日志条目,这是另一个矛盾
  8. 这里推出了整个矛盾,因此,所有任期比T大的leader必须包含所有来自任期T,在任期T里被提交的日志条目。
  9. The Log Mathcing Property 保证了未来的leader会包含被间接提交的日志条目,比如图8中(d)里的序号为2的日志条目。

在这里插入图片描述

图9: 如果S1(任期T里的leader) 在它的任期里提交了一个新的日志条目,并且S5在之后的任期U里被选举为leader,然后肯定至少存在一个服务节点S3,即接收了任期T里提交的那条日志条目,又给任期U里的S5投票了

在给出了the Leader Completeness Property 之后,我们可以证明图3中的the State Machine Safety Property ,如果一个服务节点在它的状态机的给定的序号处应用了一条日志条目,那么不会有其他服务节点在相同的序号出应用不同的日志条目。在一个服务节点应用一条日志条目到它的状态机里时,它的日志通过该日志条目必须与leader节点保持一致,并且这个日志条目必须是已被提交的。现在,考虑 在一个给定序号上任意一个服务节点应用的一条日志条目的最小任期号;the Log Completeness Property 保证了对于更高任期的的leader节点将会存储相同的日志条目,所以服务节点将会在之后的任期里,在相同的序号处应用相同的值。因此,the State Machine Safety Property 是成立的。
最后,Raft要求服务节点按日志序号的顺序来应用日志条目。通过和the State Machine Safety Property 结合,这意味着,所有服务节点将会以相同的顺序应用一组相同的日志条目到他们的状态机里。

5.5 follower 和 candidate 崩溃

目前为止,我们一直在讨论leader崩溃的情况。follower和candidate崩溃比起leader崩溃处理起来要简单的多,并且他们会用相同的方式来处理。如果一个follower或者candidata崩溃了,那之后发送给他们的RequestVote和AppendEntries 的RPC接口将会失败。Raft通过无限重试的方式来处理这些错误;如果崩溃的服务节点重启了,那RPC接口将会被成功完成。如果一个服务节点在完成一个RPC接口之后,但在回复之前崩溃了,在这个服务节点重启之后会再次收到相同的RPC接口。Raft的RPC接口是幂等的,所以就算这种情况发生了,也没什么影响。举个例子,如果一个follower收到了一个AppendEntries RPC,包含了已经在该服务节点中出现过的日志条目。那该服务节点会忽略这些日志条目。

5.6 时间和可用性

我们对Raft的其中一个要求就是 安全性必须不依赖时间来保证;系统一定不能因为 一些比预期发生的更快或者更慢事件 而产生不正确的结果。然而,可用性(系统在可接收时间范围内响应客户端请求的能力) 不可避免的会依赖时间。举个例子,如果交换一条信息花费的时间,比一般情况下两个服务节点崩溃的间隔时间还要长的话,candidate将不会为了赢得选举而等待足够长的时间(选举超时之后会发起一轮新的选举)。如果没有一个稳定的leader,Raft不能正常工作。
leader选举是时间在Raft中很关键的一个方面的体现。只要系统能满足下述时间要求,Raft就能够选举并维护一个稳定的leader:
broadcastTime ≪ electionTimeout ≪ MTBF
在这个不等式中,broadcastTime是 服务节点并发地向集群中的各个服务节点发送RPC请求并且接收它们的回复的平均时间;electionTimeout是5.2段中描述的选举超时时间;MTBF是两次单节点崩溃的平均间隔时间。broadcastTime应该比electionTimeout小几个数量级,这样leader节点才能可靠的向follower节点发送心跳,以阻止它们发起一场选举;在给electionTimeout加随机时间的背景下,这个不等式也能让分票的情况不太容易发生。electionTimeout应该比MTBF小一点点数量级,这样系统才能稳定运行。当leader崩溃的时候,系统在大致一个electionTimeout时间内是不可用的;我们希望在整个故障恢复期间,系统不可用的时间只占很小一部分时间。
broadcastTime 和 MTBF 是系统的属性,我们可以调整的是选举超时时间。Raft的RPC调用一般要求接收者将信息持久化到稳定的存储里,所以broadcastTime的时间范围可能在 0.5ms 到 20ms之间,取决于存储技术。选举超时时间 可能在 10ms 到 500ms之间。一般情况下
服务节点的MTBF时间大概是几个月或者更多,这一点很容易满足时间要求。

6. 集群成员变更

直到现在,我们都假定集群的配置(哪些服务节点参与到了一致性算法)是固定的。实际上,偶尔需要改变配置,举个例子,替换挂掉的服务节点,或者修改复制程度。尽管这些操作可以通过让整个集群下线,更新配置文件然后重启这个集群来实现,但在整个变更过程中集群都是不可用的。如果还有需要手动操作的地方,还需要冒手动操作失误的风险。为了避免这些问题,我们决定自动化变更配置,并且将其合并到Raft一致性算法里。
为了配置变更机制的安全性考虑,在配置过渡期间,在同一个任期里不允许产生两个leader。不幸的是,所有直接将老配置替换为新配置的方法都是不安全的。因为,不可能原子的将所有服务节点的配置同时替换。在过度期间,集群存在分裂成两个独立的多数派的可能(如图10所示)
在这里插入图片描述

图10: 从老配置直接变更成新配置是不安全的,因为不同的服务节点替换配置的时间不一样。比如上面这个例子,集群从3个节点增长为5个。不幸的是,存在一个时刻,在同一任期里可能会产生两个leader,一个leader由老配置里半数以上的节点选举产生(C-old),另一个leader由新配置里的半数以上的节点选举产生(C-new)

为了保证安全性,配置的变更必须使用tow-phase(两阶段)方法。有许多两阶段方法的变种实现。举个例子,一些系统(e.g.,[22])第一阶段禁用所有老配置,所以系统此时无法处理客户端请求了;然后第二阶段启用新配置。在Raft里,集群第一阶段变更为 过度配置 我们称之为 joint-consensus; 一旦joint consensus 被提交了,系统就会变更为新配置。 joint consensus 由新老配置一起组成:

  • 日志条目会被复制到新老配置里的所有服务节点
  • 新老配置里的任何一个服务节点都有可能被选举为leader节点
  • 为 选举 和 日志条目提交 达成一致需要获得新配置里半数以上节点的支持,以及老配置里半数以上节点的支持

joint consensus 允许每个服务节点在不同的时间变更配置。而且,joint consensus 集群在配置变更期间可以继续处理客户端请求。

集群的配置 使用 复制日志中特殊的条目来存储并与其他服务节点交流;下面的图11 展示了配置变更的过程。当leader收到了一条将配置从C-old变更为C-new的请求时,它将joint consensus (图里的C-old,new 配置) 存储为一条日志条目,并且使用前面描述的机制复制这个日志条目。一旦一个服务节点将新配置条目添加到了它的日志里面,它就会开始使用新的配置来做决定(一个服务节点总是使用它日志里最新的配置条目,而不管这条配置条目是否被提交)。这意味着,leader将使用C-old,new日志条目里的规则来确定C-old,new配置条目何时能被提交。如果leader挂掉了,一个使用 C-old配置或 C-old,new配置的leader将会被选举出来,取决于获胜的候选者是否接收了C-old,new配置。在这个阶段的任何一个场景下,C-new都不能单独作出决定。

在这里插入图片描述

图11: 最下面的横坐标是配置变更的时间线。虚线表示配置条目被创建,但还未被提交,实线表示最近被提交的配置条目。leader首先在它的日志里创建C-old,new配置条目,然后将它提交到C-old,new。(两个多数派,一派是半数以上的旧配置里的服务节点,一派是半数以上的新配置里的服务节点)。然后leader创建C-new配置条目并且将它提交到C-new(半数以上的新配置里的服务节点)。这个过程中 没有C-old,C-new可以同时独立作出决定的时刻。

一旦C-old,new被提交,C-old,C-new 都不能在没有对方同意的情况下单独作出决定。并且the Leader Completeness Property 保证了拥有C-old,new配置条目的服务节点,才能被选举为leader。这个时候,leader可以安全的创建C-new配置条目并且将它复制到集群里了。另一方面,这个配置日志条目将会在每一个服务节点收到的时候立即生效。当新的配置日志条目在C-new的规则下被提交后,老配置就变得无关紧要了,并且不在新配置日志条目里的服务节点会被关掉。像图11里显示的那样,在这个过程中,C-old 和 Cnew 不能同时单独作出决定,这保证了安全性。
这里有三个关于 配置变更的问题。第一个问题就是,新加入的服务节点可能没有存储任何日志条目,如果它们以这种状态被添加到集群里,那它需要很长一段时间来复制leader的日志,并且在这段时间里,它不可能提交新的日志条目。为了避免可用性间隙,Raft在配置变更前引入了一个额外的阶段,新服务节点作为一个没有投票权的成员加入到集群中(leader会复制日志条目给他们,但是不认为它们可以投票)。一旦新的服务节点的复制的日志条目追赶上来了,上面描述的配置变更就可以开始进行了。
第二个问题是,集群的leader可能不在新配置里。这种情况下,leader会在提交完C-new的配置日志条目后退位(回到follower状态)。这意味着有那么一段时间(leader提交C-new期间) leader管理着一个不包括他自己的集群,他复制的日志条目不会将自己算在 需要复制日志条目半数以上的服务节点内。当C-new配置日志条目被提交后,新的配置在这个时候第一次可以独立生效了,这个时候leader需要过渡了(这个时候新leader总能在新配置里的服务节点里产生)。
第三个问题就是,被移除的服务节点(不在新配置里的服务节点)会扰乱集群。这些服务节点将不会再收到leader的心跳,所以他们会超时并发起一轮新的选举。他们将用新的任期号发送新的RequestVote RPC,并且这会造成当前的leader退回follower状态。一个新leader将最终被选举出来,但是被移除的服务节点会再次超时并且重复进行上述操作,最后会导致系统可用性降低。
为了阻止这种问题发生,服务节点在确认当前leader存在时,会忽略RequestVote RPC请求。特别的,如果一个服务节点 在收到leader心跳且在最小选举超时时间之内,收到了一个RequestVote RPC,它不会更新自己的任期 或者 投出自己的选票。 这不会影响正常的选举,每一个服务节点会至少等待最小超时时间之后才会发起一个选举。然而,它可以帮助避免 被移除节点扰乱集群:如果一个leader可以向集群发送心跳,那它就不会被大任期号的RequestVote RPC扰乱。

7. 日志压缩

Raft的日志会 随着处理更多的客户端请求而增长,但是在实际系统中,它不能无限增长。日志增长得越长,它会耗费更多的空间和时间来来回顾这些日志。如果不采取一些机制 丢弃在日志中累积的陈旧信息,最终将会造成可用性问题。
快照是最简单的压缩方法。在快照中,当前系统的所有状态都会被写入 被稳定存储的快照中,然后在此之前的所有日志将被丢弃。在Chubby和ZooKeeper中使用了快照,并且这一段剩余的部分将会描述Raft中的快照。
增量压缩方法,比如日志清除 、日志结构化合并树[30,5], 也是可以的。他们一次处理一部分数据,随着时间的推移,他们日志压缩负载分散的更加的均匀。他们首先会选择一块区域的 累计了许多删除和覆盖结构的日志,然后重写这块区域还活跃的对象然后释放这块儿区域。比起 快照 这些方法明显增加了额外的机制的复杂度,快照通过处理整块数据集来简化问题。状态机可以像快照一样用相同的接口来实现Lsm trees,但日志清除需要修改Raft。

图12 展示了Raft中基本的快照想法。每一个服务节点独立地产生快照,包括刚刚被提交到日志里的条目。由状态机组成的许多工作将它们当前的状态写入到快照中。Raft会将少量元数据写入快照: 比如 这个快照包含的日志里最后一条日志条目的 序号 和 任期号。这些数据是为了 在快照产生后AppendEntries 对第一条复制日志进行一致性检查保留的,因为AppendEntries需要前一条日志条目的序号 和 任期号。为了集群成员变更(第6段)可用,快照会包含日志里最新的配置日志条目。一旦一个服务节点完整的产生了一个快照,它就会删除所有 在快照最后一条日志条目及以前的日志条目,也会删除之前老的快照。
在这里插入图片描述

图12 一个服务节点会用新的快照(序号1-5) 来替换已经提交的日志条目,这个快照中存储了截止序号5时的状态(举个例子就是变量x,y的值)。这个快照包含的最后一条日志条目的 序号号和任期号。

尽管服务节点一般都独立的产生快照,但leader在某些情况下必须发送快照给落后的follower。当leader将给follower发送的日志条目写进快照并且丢弃之后,就会发生上述的情况。幸运的是,这种情况不太容易发生:leader的follower一般都已经复制了被leader写入快照并丢弃的日志条目。然而,特别慢的follower 或者 一个新加入集群的服务节点(第6段) 不会有上述日志。给更新这些节点的方式就是leader通过网络向这些服务节点发送自己的快照。

leader使用一个叫InstallSnapshot的新RPC接口向落后太多的follower发送快照;详情见图13。当一个follower接收到了一个快照时,它在它现有的日志条目 和 快照里 进行抉择。通常情况下,快照会包含最新的没在接收者日志里出现过的日志条目,这种情况下,follower丢弃它的所有日志;全部被快照替代。如果follower收到了一个快照,里面的内容是它日志里的前面一部分(可能因为重传 或者 其他错误导致),然后它会删除快照里已经存在的那部分日志条目,但是快照之后的日志条目仍然是有效的且必须被维护在日志里面。

在这里插入图片描述

图13: 一份InstallSnapshot RPC 的总结。为了传输快照被分成许多块;每次InstallSnapshot RPC 调用会重置follower的选举超时时间。

这个快照方法似乎违背了Raft的强领导原则,因为follower可以自己产生快照了,而不用知晓leader的信息。然而,我们认为这没有什么问题,因为为了达成一致性,需要在leader的帮助下避免冲突,当服务节点生成自己的快照时,一致性已经达成了,所以没有冲突需要解决。数据仍然是从leder流向follower的,只不过follower现在可以重新组织自己的数据了。
我们考虑过一种基于leader方法的备选方案,只有leader可以创建快照,然后它将快照发送给每个follower。然而,这个方法有两个缺点,首先,发送快照给每个follower会浪费网络带宽,并且降低快照的处理速度。每一个follower已经拥有生成自己快照的信息了,并且一般来说从本地的状态里生成快照比通过网络接收快照 好太多了。第二,leader的实现会变得更复杂。举个例子,为了不阻塞客户端请求,leader需要 将快照和新的日志条目一起 并发地的发送给follower。
这里有两个以上的影响快照性能的问题。首先,服务节点必须决定何时进行快照。如果一个服务节点太频繁的产生快照,它会浪费磁盘带宽和性能;如果一个快照频率太低,它会冒着内存容量耗尽的风险,并且这会增加它在重启回顾这些日志时使用的时间。一个简单的策略是,当日志增长超过一个固定的大小时,就进行快照。如果这个大小被设置得比预期的快照大小明显要打的化,磁盘带宽过载的风险就会变小。
第二个性能问题就是,在写入快照时,可能耗费大量的时间,并且我们不希望因为这个增加系统正常的延迟。解决办法是,使用 copy-on-wirte技术,这样接收新的更新时,可以不影响快照的写入。举个例子,具有函数式数据结构的状态机天然支持这一点。或者,操作系统的 copy-on-write 支持(比如说 Linux 中的 fork+copy on write) 可以被用来创建一个整个状态机的内存快照(我们的实现使用了这个方法)。

8. 客户端交互

这一段描述了,客户端如何与Raft交互。包括客户端如何找到集群leader 并且 Raft如何支持线性化语义[10]. 这些问题在所有的一致性系统里都会有,并且Raft的解决方法和其他系统很像。
Raft里的客户端会发送他们所有的请求给leader。当一个客户端第一次启动时,它会随机选择一个服务节点连接。如果客户端第一次选择的不是leader节点,那这个服务节点会拒绝客户端的请求,并回复它最近了解到leader的信息(AppendEntries 请求会包含leader的网络地址)。如果一个leader崩溃,客户端请求会超时,然周客户端会再次随机选择一个服务节点连接。
我们对Raft的期望是,能够实现线性化语义(每一个操作会被立即执行,并且刚好一次,在它调用和收到回复之间)。然而,目前描述的Raft可能多次执行同一个命令:举个例子,如果一个leader在提交日志条目之后 但在回复客户端之前崩溃了,客户端会重新选择一个leader节点连接,并重试命令,这会造成一条命令被执行两次。解决办法是 客户端对每次请求制定一个唯一的序列号。然后,状态机跟踪每个客户端的最新的序列号,和回复关联起来。如果它收到了一条命令,并且这条命令的序列号被执行过了,它将立刻回复这个请求,而不会再次执行它。
只读操作可能被处理而不需要记录日志。然而,如果没有额外的机制,这会有返回过期数据的风险,因为 leader在回复客户端请求之前,可能已经被新的leader取代了。线性读不允许返回过期数据,并且Raft需要两个额外的预防措施来保证不使用日志是安全的。首先,leader必须有最新的被提交的日志条目。the Leader Completeness Property 保证了一个leader有所有的被提交的日志条目,但是在一个leader刚启动的时候,他不知道哪些日志是已经提交了的(图8里的例子)。为了解决这个问题,每个leader需要在它任期开始的时候提交一条空的 no-op 日志条目(解决图8里的问题)。第二,leader必须检查它在回复只读请求前是否被废除了leader的身份(当前leader的信息可能是过期的,如果一个新的leader被选举产生了的话)。Raft通过 让leader在回复只读请求前与集群中半数以上服务节点交换心跳信息 来处理这个问题。 可供选择的方案是,一个leader可以依靠心跳机制来提供一种形式的租约[9],但是这样做,安全性就会依赖时间。

9. 实现和评价

我们将Raft作为复制状态机的一部分实现了,该状态存储了RAMClound[33]的配置信息,并且帮助RAMClound协调故障恢复。这个版本的Raft实现包含了大约2000行C++代码,不包括测试代码,注释,或者空行。这份源代码是免费可用的[23].基于这份论文,有大约25种针对各种不同场景的第三方开源实现。并且,许多公司正在开发基于Raft的系统。
这段剩余的部分将从三个标准来评估Raft算法,可理解性,正确性和性能。

9.1 可理解性

为了测量相对于Paxos Raft的可理解性如何,我们对 在斯坦福大学高级操作系统课程 和 加州伯克利大学的分布式计算课程 里对本科生和研究生进行了一场实验研究。 我们录制了Raft和Paxos的教学视频,并且创建了相应的测试题。Raft的教学视频覆盖了这篇论文除了日志压缩的内容;Paxos的教学视频覆盖了足够多的材料以创建和Raft 相等的状态复制机,包括single-decree Paxos, multi-decree Paxos, 配置变更,和一些在实践中需要的优化(比如leader选举)。这些问题测试了学生对算法基本的理解并且要求学生解释一些边边角角的场景。每一个学生看一个视频,做一个相应的测试题,看第二个视频,再做第二个测试题。大概一半的学生先学Paxos,另外一半的学生先学Raft ,这是为了对比 个体差异表现 和 从先学习内容里获取到的经验。我们对比了参与者在每一个测试题上的得分来判断确定 是否参与者表现出Raft更好地理解。
我们尝试尽可能公平的比较Paxos和Raft。实验在两个方面更加有利于Paxos: 43个参与者里有15个表示对Paxos已经有一些经验了,并且Paxos的视频比Raft的视频长14%。表1是一份总结,我们已经采取了许多措施来缓和潜在的偏见。我们所有的材料都是可以被回放的的[28,31].
在这里插入图片描述

在每个测试题中,参与者Raft的得分平均比Paxos的得分高出4.9分(总分60分,Raft平均25.7分,Paxos平均20.8分);图14展示了他们各自的得分。一对t-test表明,在95%的可信度下,真是的Raft分数分布比Paxos真是的分数分布至少高2.5分。

在这里插入图片描述

图14: 一张散点图 对比了 43个参与者在Raft和Paxos测试题中的得分情况。分数在对角线智商的表示参与者在Raft中的得分更高。

我们也创建了一个线性回归模型用来预测新同学在测试题中的得分,基于三个因素: 做了哪个测试题,他们之前对Paxos的了解程度,以及他们学习算法的顺序。模型预测,选择测试题会产生 12.5分的有利于Raft的差别。这比前面观察到的4.9分高很多,因为许多学生在这之前已经有了Paxos的学习经验,这会对Paxos帮助很多,但对Raft帮助很少。奇怪的是,模型同样预测,对于先进行Paxos测试题的学生,Raft的得分会少6.3分;尽管我们不知道这是为什么,但这可能有统计学意义。
在参与者做完测试题之后,为了看看哪一个算法他们会觉得更容易实现或者解释,我们对他们进行了调查;在图15中展示了调查结果。大多数参与者表示他们觉得Raft比Paxos更容易实现和解释(对于每一个问题,41个人中有33个这样说)。然而,这些自己反馈的感受 不如 参与者的测试题分数来讲 可靠,并且参与者可能已经 因为 我们假设Raft比Paxos更容易理解 而产生了偏见。
一份详细的关于Raft用户研究讨论是可以被查看的[31].

9.2 正确性

我们已经形成了一份正式的详细说明 和 一份安全性证明(对于第5段中描述的一致性机制)。正式的详细说明[31] 使用TLA+ 详细说明语言 完整清晰的描述了在 图2中总结的信息[17]。这份证明的主题大概有400行。它对于任何想实现Raft的人来说都是有用的。我们已经使用TLA证明系统[7]机械的证明了 the Log Complete Property.然而,这份证明依赖的不变量,还没有被检查确认。(举个例子,我们还没有详细证明类型安全)。我们写了一份关于 the State Machine Safety Property完整且相对清晰的非正式的证明[31]。(大约包含3500个字)

9.3 性能

Raft的性能和其他一致性算法差不多,比如Paxos。 最重要的性能场景就是 leader复制新的日志条目。Raft使用了最小的信息条数来完成(在leader到集群中半数以上节点的一个往返里)。Raft的性能也是可以被改进的,举个例子,很容易通过支持批处理和流水线来提高吞吐量以及降低延时。许多优化已经在别的算法里被提出来了,这些优化里有很多是可以应用到Raft上的,但是我们把这些留到以后的工作里去做。
我们使用我们自己的Raft实现来测量Raft的leader选举算法的性能并回答两个问题。第一,选举能快速恢复吗?第二,leader挂掉之后最小的系统不可用时间是多少?
为了测量leader选举,我们重复让拥有5个服务节点集群中的leader节点崩溃,并记录集群需要多长时间发现leader崩溃并选举出一个新的leader(看图16)。为了产生最坏的场景,在每一次实验中的服务节点都有不同的日志长度,并且一些候选者没有资格成为leader。而且,为了鼓励分票情况的发生,我们使用测试脚本在leader挂掉前同步触发一次leader的心跳广播(这是 leader在挂掉之前复制了一个新的日志条目 的近似行为)。 leader会均匀的在心跳间隔时间范围内挂掉,这意味着对于所有的测试来说,这大概是最小选举超时时间的一半。因此,最小的系统可能的不可用时间是 最小选举超时时间的一半。
在这里插入图片描述

图16: 发现和替换一个崩溃leader所用的时间。最上面的图列出了不同随机间隔的选举超时时间的情况,下面的图测量了最小的选举超时间隔时间。每一条线代表了1000次测试(除了150-150ms,进行了100次) 爱你的 相应的是一次特别选择的选举超时时间;举个例子,“150-155ms” 意味着,选举超时时间将会随机、均匀的在150ms和155ms之前产生。这分测试结果是在5个服务节点 并且广播请求的时间大概是15ms 的集群上产生的。 在9个服务节点的集群上,产生的结果类似。

图16里最上面的图展示了很少量的随机时间范围 就能足够有效的在选举中避免分票情况。如果缺少随机时间范围,leader选举将会因为太多的分票情况导致10秒以上才能选举出leader。仅仅增加了5ms的随机时间范围就有非常显著的帮助,在这种情况下集群不可用的时间的中位数是 287ms。使用更多的随机时间范围能改善最坏场景下的表现: 有50ms随机时间范围的最坏情况下完成选举的时间是513ms(进行了1000次测试)。
在图16底部的图展示了系统不可用时间可以通过缩短选举超时时间来缩短。在选举超时时间范围是12-24ms范围的情况下,平均需要花费35ms来选举出leader。(测试中最长的时间需要花费152ms)。然而,更低的选举超时时间将破坏Raft的时间要求: leader很难在其他服务节点选举超时之前将心跳发送给他们。这会操作不必要的leader变更并且降低系统整体的可用性。我们推荐使用保守的选举超时时间,比如 150-300ms;这样超时将不太可能造成不必要的leader变更,并且仍然能提供不错的可用性。

10. 相关工作

已经有许多已公布的和一致性算法相关的工作,可以大致分为下述几类

  • Lamport 最原始的Paxos描述[15],并且尝试更清晰的描述它[16,20,21].
  • 详细描述的Paxos (e-paxos),补充了缺失的细节以及 为了给 生产实现 提供更好的理论基础 修改了算法[26,39,13].
  • 实现了一致性算法的系统,比如Chubby[2,4],ZooKeeper[11,12],以及Spanner[6]. Chubby和Spanner的算法没有公开细节,尽管他们都声称是基于Paxos的。ZooKeeper的算法公开了很多细节,但是它与Paxos非常不一样。
  • 可以应用到Paxos的性能优化[18,19,3,25,1,27].
  • Oki和Liskov的Viewstamped Replication (VR),一种与Paxos同一时期的可供选择的达成一致性的方法。原始的算法描述[29]与分布式传输耦合在了一起,但是核心的一致性协议在最近的更新中被剥离了出来[22].VR使用了一种基于leader的方法,与Raft有许多相同的地方。

Raft与Paxos最不同的地方在于 Raft的强领导力: 在Raft中leader选举是一致性协议里必不可少的一部分,并且很多的功能都放在了leader上。这种方式能让算法更简单并且更容易被理解。举个例子,在Paxos中,leader选举在基本的一致性协议里不是必需的,它仅仅作为一种性能优化的手段 并且不会参与到完成一致性的工作里。然而这导致Paxos需要增加额外的机制,Paxos必须为基本的一致性算法包含两阶段协议 并且 为leader选举包含另外一种机制。相比之下,Raft将leader选举直接合并到了一致性算法里,并且使用leader选举作为两阶段一致性的起点。这样Raft里的机制比Paxos更少。
像Raft,VR和ZooKeeper 都是基于leader的 因此也有Raft中的很多有点。然而Raft比VR或ZooKeeper有更少的机制,因为它简化了非leader的功能。举个例子,日志条目在Raft里只有一个流向:从leader节点通过AppendEntries RPC向其他服务节点流动。 在VR里,日志条目可以向两个方向流动(leader可以在选举过程中接收日志条目);这导致VR增加了额外的机制和复杂性。ZooKeeper公开的描述里日志条目也可以在两个方向上流动,但是ZooKeeper的实现更像Raft[35].
Raft比起其他基于日志复制的一致性算法 有更少的消息类型。举个例子。我们计算了VR和ZooKeeper中基本一致性和成员变更所需要使用到的消息类型的数量(不包括日志压缩和客户端交互)。VR和ZooKeeper分别定义了10中不同的消息类型,然而Raft仅仅只有4中消息类型(2个RPC请求和相应的回复)。Raft消息的内容比其他算法的要密集一些,但是只是简单的聚集起来而已。此外,VR和ZooKeeper在leader发生变化时都需要传输整个日志条目;为了实际的生产实现,会要求增加额外的消息类型来优化这些机制。
Raft的强领导方法简化了算法,但也舍弃了一些性能优化。举个例子,平等主义的Paxos(EPaxos)能在一些没有leader的情况下拥有更高的性能[27]。EPaxos 利用了状态机命令的交换性。任何一个服务节点都可以在一轮交流后提交一条命令,只要其他命令同时被提出并与之交换。然而,如果命令被同时提出但是并没有进行交换,那么EPaxos会增加一轮新的交流沟通来提交命令。因为任何一个节点都可以提交命令,所以EPaxos可以通过比较好的负载均衡来获得在WAN网络环境下 比Raft更低的延迟。然而,这显著的在Paxos中增加了复杂性。
有几种不同的集群成员变更的方法已经被提出或被实现了,包括 Lamport’s最初的想法[15],VR[22],和SMART[24]. 我们为Raft选择了 joint consensus 方法因为它可以利用Raft的一致性协议,所以对于Raft来说只需要非常少的额外机制就能实现成员变更。Lamport的α-based方法不是Raft的选择,因为它假定在没有leader的情况下也能达到一致性。相比于VR和SMART,Raft的重新配置算法具有在成员变更时不限制处理正常请求的优点;相反,VR在配置变更时会暂停所有正常请求的处理,并且SMART在大多数请求上实施了一个 α-like的限制。Raft的方法比起VR或者SMART增加了很少的机制。

11. 结论

算法设计通常将 正确性、效率、简洁 作为主要的目标。尽管这些目标都是值得的,但我们相信可理解性也同样中央。在开发整将算法转换成工程实现之前,正确性、效率、简洁这些目标都是没有实现的,这不可避免的会让工程实现偏离公布的算法。除非开发者对算法有足够深的理解并且有直观的认识,否则很难在开发者的实现中维持算法希望的特性。
在这片论文中,我们列举了这个分布式一致性算法的相关的问题,也就是被广泛接受但是难以理解的Paxos,并且已经为难了学生和开发者许多年。我们开发了一个新的算法,Raft,我们认为它比Paxos能更容易被理解。我们相信Raft为系统构建提供了更好的理论基础。使用可理解性作为主要的设计目标改变了我们设计Raft的方式;当设计Raft时,我们发现我们复用了一些技巧,比如问题分解,简化状态空间。这些技巧不仅仅改善了Raft的可理解性,同时也更容易是我们相信Raft的正确性。

12. 感谢

如果没有 Ali Ghodsi, David Mazie`res, and the students of CS 294-91 at Berkeley and CS 240 at Stan- ford 的支持,用户研究实验是不可能完成的。Scott Klemmer 帮助我们设计了用户研究实验,并且Nelson Ray 建议我们进行统计学分析。用户研究实验用到的Paxos幻灯片大部分借鉴了是Lorenzo Alvisi制作的幻灯片。特别感谢DavidMazieres 和 EzraHoch 找到了raft中的一些bug。许多人提供了关于论文有用的反馈 和 用户研究实验的素材,包括 Ed Bugnion, Michael Chan, Hugues Evrard,Daniel Giffin, Arjun Gopalan, Jon Howell, Vimalkumar Jeyakumar, Ankita Kejriwal, Aleksandar Kracun, Amit Levy, Joel Martin, Satoshi Matsushita, Oleg Pesok, David Ramos, Robbert van Renesse, Mendel Rosenblum, Nico- las Schiper, Deian Stefan, Andrew Stone, Ryan Stutsman, David Terei, Stephen Yang, Matei Zaharia, 24 位匿名的评论者 (可能有重复), and 尤其感谢我们的指导人 Eddie Kohler。
Werner Vogels 发送了一条关于早期raft草稿的推特链接, 这给了Raft非常重要的曝光。这项工作被the Gigascale Sys- tems Research Center 和 the Multiscale Systems Center 这两个中心支持。这两个中心是the Focus Center Research Program 旗下 六个研究中心基金里的两个。一个是 Semiconductor Research Corporation program 由STARnet支持。一个是 Semiconductor Research Corporation program 由MARCO和DARPA赞助,由国家科学基金批准 No.0963859,并且得到了来自 Facebook, Google, Mel- lanox, NEC, NetApp, SAP, 和 Samsung 的支持。Diego Ongaro 是被 The Junglee Corporation Stanford Graduate Fellowship 支持的。

参考

[1] BOLOSKY, W. J., BRADSHAW, D., HAAGENS, R. B., KUSTERS, N. P., AND LI, P. Paxos replicated state machines as the basis of a high-performance data store. In Proc. NSDI’11, USENIX Conference on Networked Systems Design and Implementation (2011), USENIX, pp. 141–154.
[2] BURROWS, M. The Chubby lock service for loosely- coupled distributed systems. In Proc. OSDI’06, Sympo- sium on Operating Systems Design and Implementation (2006), USENIX, pp. 335–350.
[3] CAMARGOS, L. J., SCHMIDT, R. M., AND PEDONE, F. Multicoordinated Paxos. In Proc. PODC’07, ACM Sym- posium on Principles of Distributed Computing (2007), ACM, pp. 316–317.
[4] CHANDRA, T. D., GRIESEMER, R., AND REDSTONE, J. Paxos made live: an engineering perspective. In Proc. PODC’07, ACM Symposium on Principles of Distributed Computing (2007), ACM, pp. 398–407.
[5] CHANG, F., DEAN, J., GHEMAWAT, S., HSIEH, W. C., WALLACH, D. A., BURROWS, M., CHANDRA, T., FIKES, A., AND GRUBER, R. E. Bigtable: a distributed storage system for structured data. In Proc. OSDI’06, USENIX Symposium on Operating Systems Design and Implementation (2006), USENIX, pp. 205–218.
[6] CORBETT, J. C., DEAN, J., EPSTEIN, M., FIKES, A., FROST, C., FURMAN, J. J., GHEMAWAT, S., GUBAREV, A., HEISER, C., HOCHSCHILD, P., HSIEH, W., KAN- THAK, S., KOGAN, E., LI, H., LLOYD, A., MELNIK, S., MWAURA, D., NAGLE, D., QUINLAN, S., RAO, R., ROLIG, L., SAITO, Y., SZYMANIAK, M., TAYLOR, C., WANG, R., AND WOODFORD, D. Spanner: Google’s globally-distributed database. In Proc. OSDI’12, USENIX Conference on Operating Systems Design and Implemen- tation (2012), USENIX, pp. 251–264.
[7] COUSINEAU, D., DOLIGEZ, D., LAMPORT, L., MERZ, S., RICKETTS, D., AND VANZETTO, H. TLA+ proofs. In Proc. FM’12, Symposium on Formal Methods (2012), D. Giannakopoulou and D. Me ́ry, Eds., vol. 7436 of Lec- ture Notes in Computer Science, Springer, pp. 147–154.
[8] GHEMAWAT, S., GOBIOFF, H., AND LEUNG, S.-T. The Google file system. In Proc. SOSP’03, ACM Symposium on Operating Systems Principles (2003), ACM, pp. 29–43.
[9] GRAY,C.,ANDCHERITON,D.Leases:Anefficientfault- tolerant mechanism for distributed file cache consistency. In Proceedings of the 12th ACM Ssymposium on Operating Systems Principles (1989), pp. 202–210.
[10] HERLIHY, M. P., AND WING, J. M. Linearizability: a correctness condition for concurrent objects. ACM Trans- actions on Programming Languages and Systems 12 (July 1990), 463–492.
[11] HUNT, P., KONAR, M., JUNQUEIRA, F. P., AND REED, B . ZooKeeper: wait-free coordination for internet-scale systems. In Proc ATC’10, USENIX Annual Technical Con- ference (2010), USENIX, pp. 145–158.
[12] JUNQUEIRA, F. P., REED, B. C., AND SERAFINI, M. Zab: High-performance broadcast for primary-backup sys- tems. In Proc. DSN’11, IEEE/IFIP Int’l Conf. on Depend- able Systems & Networks (2011), IEEE Computer Society, pp. 245–256.
[13] KIRSCH, J., AND AMIR, Y. Paxos for system builders. Tech. Rep. CNDS-2008-2, Johns Hopkins University, 2008.
[14] L A M P O RT, L . Time, clocks, and the ordering of events in a distributed system. Commununications of the ACM 21, 7 (July 1978), 558–565.
[15] L A M P O RT, L . The part-time parliament. ACM Transac- tions on Computer Systems 16, 2 (May 1998), 133–169.
[16] LAMPORT, L. Paxos made simple. ACM SIGACT News 32, 4 (Dec. 2001), 18–25.
[17] L A M P O RT, L . Specifying Systems, The TLA+ Language and Tools for Hardware and Software Engineers. Addison- Wesley, 2002.
[18] LAMPORT, L. Generalized consensus and Paxos. Tech. Rep. MSR-TR-2005-33, Microsoft Research, 2005.
[19] L A M P O RT, L . Fast paxos. Distributed Computing 19, 2 (2006), 79–103.
[20] LAMPSON, B. W. How to build a highly available system using consensus. In Distributed Algorithms, O. Baboaglu and K. Marzullo, Eds. Springer-Verlag, 1996, pp. 1–17.
[21] LAMPSON, B. W. The ABCD’s of Paxos. In Proc. PODC’01, ACM Symposium on Principles of Distributed Computing (2001), ACM, pp. 13–13.
[22] LISKOV, B., AND COWLING, J. Viewstamped replica- tion revisited. Tech. Rep. MIT-CSAIL-TR-2012-021, MIT, July 2012.
[23] LogCabin source code. http://github.com/ logcabin/logcabin.
[24] LORCH, J. R., ADYA, A., BOLOSKY, W. J., CHAIKEN, R., DOUCEUR, J. R., AND HOWELL, J. The SMART way to migrate replicated stateful services. In Proc. Eu- roSys’06, ACM SIGOPS/EuroSys European Conference on Computer Systems (2006), ACM, pp. 103–115.
[25] MAO, Y., JUNQUEIRA, F. P., AND MARZULLO, K. Mencius: building efficient replicated state machines for
WANs. In Proc. OSDI’08, USENIX Conference on Operating Systems Design and Implementation (2008), USENIX, pp. 369–384.
[26] MAZIE` RES, D. Paxos made practical.
//www.scs.stanford.edu/ ̃dm/home/ papers/paxos.pdf, Jan. 2007.
http:
[27] MORARU, I., ANDERSEN, D. G., AND KAMINSKY, M. There is more consensus in egalitarian parliaments. In Proc. SOSP’13, ACM Symposium on Operating System Principles (2013), ACM.

[28] Raft user study. http://ramcloud.stanford. edu/ ̃ongaro/userstudy/.
[29] OKI, B. M., AND LISKOV, B. H. Viewstamped replication: A new primary copy method to support highly-available distributed systems. In Proc. PODC’88, ACM Symposium on Principles of Distributed Computing (1988), ACM, pp. 8–17.
[30] O’NEIL, P., CHENG, E., GAWLICK, D., AND ONEIL, E. The log-structured merge-tree (LSM-tree). Acta Informat- ica 33, 4 (1996), 351–385.
[31] ONGARO, D. Consensus: Bridging Theory and Practice. PhD thesis, Stanford University, 2014 (work in progress).
http://ramcloud.stanford.edu/ ̃ongaro/ thesis.pdf.
[32] ONGARO, D., AND OUSTERHOUT, J. In search of an understandable consensus algorithm. In Proc ATC’14, USENIX Annual Technical Conference (2014), USENIX.
[33] OUSTERHOUT, J., AGRAWAL, P., ERICKSON, D., KOZYRAKIS, C., LEVERICH, J., MAZIE`RES, D., MI- TRA, S., NARAYANAN, A., ONGARO, D., PARULKAR, G., ROSENBLUM, M., RUMBLE, S. M., STRATMANN, E., AND STUTSMAN, R. The case for RAMCloud. Com- munications of the ACM 54 (July 2011), 121–130.
[34] Raft consensus algorithm website. http://raftconsensus.github.io.
[35] REED, B. Personal communications, May 17, 2013.
[36] ROSENBLUM, M., AND OUSTERHOUT, J. K. The design and implementation of a log-structured file system. ACM Trans. Comput. Syst. 10 (February 1992), 26–52.
[37] S C H N E I D E R , F. B . Implementing fault-tolerant services using the state machine approach: a tutorial. ACM Com- puting Surveys 22, 4 (Dec. 1990), 299–319.
[38] SHVACHKO, K., KUANG, H., RADIA, S., AND CHANSLER, R. The Hadoop distributed file system. In Proc. MSST’10, Symposium on Mass Storage Sys- tems and Technologies (2010), IEEE Computer Society, pp. 1–10.
[39] VAN RENESSE, R. Paxos made moderately complex. Tech. rep., Cornell University, 2012.

翻译后感

  1. 论文刚开始看的时候没有看明白,每多看一次,就会发现之前没有注意到的点。
  2. 虽然对Raft的理论有了一个大概的认识,但还没有看过一个完整的工业界的工程实现,需要看看实际生产中的工程实现,下一篇博客,写Redis哨兵中的Raft实现。
  3. Raft论文中提到的好多东西都没有了解过,比如Paxos以及各种变种Paxos。比如存储相关的LSM。
  4. 翻译下来感觉英语水平提高了不少,阅读英文技术文章也感觉顺了一些😂
  5. 看论文,讨论研究论文还是有点意思。

往期博客回顾

  1. redis6.0源码阅读 主从模式之数据同步

  2. redis内存回收与内存淘汰策略

  3. redis持久化 之 反面面试官

  4. redisObject 以及 对抽象的理解

  5. redis 基础数据结构 之 有序集合

  6. redis 基础数据结构 之 集合

  7. redis不稳定字典的遍历

  8. redis 基础数据结构之 hash表

  9. redis的基础数据结构 之 ziplist

  10. redis的基础数据结构之 list

  11. redis的基础数据结构之 sds

  12. GET命令背后的源码逻辑

  13. redis服务器的部分启动过程

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值