分布式一致性算法:Raft 算法(论文翻译)

Raft 算法是能够用来替代 Paxos 算法的分布式一致性算法,并且 raft 算法比 Paxos 算法更易懂且更容易实现。本文对 raft 论文进行翻译,但愿能有助于读者更方便地理解 raft 的思想。若是对 Paxos 算法感兴趣,能够看个人另外一篇文章:分布式系列文章——Paxos算法原理与推导git

摘要

Raft 是用来管理复制日志(replicated log)的一致性协议。它跟 multi-Paxos 做用相同,效率也至关,可是它的组织结构跟 Paxos 不一样。这使得 Raft 比 Paxos 更容易理解而且更容易在工程实践中实现。为了使 Raft 协议更易懂,Raft 将一致性的关键元素分开,如 leader 选举、日志复制和安全性,而且它实施更强的一致性以减小必须考虑的状态的数量。用户研究的结果代表,Raft 比 Paxos 更容易学习。 Raft 还包括一个用于变动集群成员的新机制,它使用重叠的大多数(overlapping majorities)来保证安全性。github

1 介绍

一致性算法容许多台机器做为一个集群协同工做,而且在其中的某几台机器出故障时集群仍然能正常工做。 正由于如此,一致性算法在创建可靠的大规模软件系统方面发挥了关键做用。 在过去十年中,Paxos [15,16] 主导了关于一致性算法的讨论:大多数一致性的实现都是基于 Paxos 或受其影响,Paxos 已成为用于教授学生一致性相关知识的主要工具。web

不幸的是,Paxos 实在是太难以理解,尽管许多人一直在努力尝试使其更易懂。 此外,其架构须要复杂的改变来支持实际系统。 结果是,系统开发者和学生都在与 Paxos 斗争。算法

在咱们本身与 Paxos 斗争以后,咱们开始着手寻找一个新的一致性算法,能够为系统开发和教学提供更好的基础。 咱们的方法是不寻常的,由于咱们的主要目标是可理解性:咱们能够为实际系统定义一个一致性算法,并以比 Paxos 更容易学习的方式描述它吗?在该算法的设计过程当中,重要的不只是如何让该算法起做用,还有清晰地知道该算法为何会起做用。安全

这项工做的结果是一个称为 Raft 的一致性算法。 在设计 Raft 时,咱们使用了特定的技术来提升可理解性,包括分解(Raft 分离 leader 选举,日志复制和安全)和状态空间减小(相对于 Paxos ,Raft 减小了不肯定性程度和服务器之间彼此不一致的方式 )。 一项针对两个大学的 43 名学生的用户研究代表,Raft 比 Paxos 更容易理解:在学习两种算法后,其中 33 名学生可以更好地回答关于 Raft 的问题。服务器

Raft 在许多方面相似于现有的一致性算法(尤为是 Oki 和 Liskov 的 Viewstamped Replication [29,22]),但它有几个新特性:网络

  • Strong leader:在 Raft 中,日志条目(log entries)只从 leader 流向其余服务器。 这简化了复制日志的管理,使得 raft 更容易理解。
  • Leader 选举:Raft 使用随机计时器进行 leader 选举。 这只需在任何一致性算法都须要的心跳(heartbeats)上增长少许机制,同时可以简单快速地解决冲突。
  • 成员变动:Raft 使用了一种新的联合一致性方法,其中两个不一样配置的大多数在过渡期间重叠。 这容许集群在配置更改期间继续正常运行。

咱们认为,Raft 优于 Paxos 和其余一致性算法,不只在教学方面,在工程实现方面也是。 它比其余算法更简单且更易于理解; 它被描述得十分详细足以知足实际系统的须要; 它有多个开源实现,并被多家公司使用; 它的安全性已被正式规定和验证; 它的效率与其余算法至关。数据结构

本文的剩余部分介绍了复制状态机问题(第 2 节),讨论了 Paxos 的优势和缺点(第3节),描述了咱们实现易理解性的方法(第 4 节),提出了 Raft 一致性算法(第 5-8 节),评估 Raft(第 9 节),并讨论了相关工做(第 10 节)。架构

2 复制状态机

一致性算法是在复制状态机[37]的背景下产生的。 在这种方法中,一组服务器上的状态机计算相同状态的相同副本,而且即便某些服务器宕机,也能够继续运行。app

复制状态机用于解决分布式系统中的各类容错问题。 例如,具备单个 leader 的大规模系统,如 GFS [8],HDFS [38] 和 RAMCloud [33] ,一般使用单独的复制状态机来进行 leader 选举和存储 leader 崩溃后从新选举须要的配置信息。Chubby [2] 和 ZooKeeper [11] 都是复制状态机。

复制状态机一般使用复制日志实现,如图 1 所示。每一个服务器存储一个包含一系列命令的日志,其状态机按顺序执行日志中的命令。 每一个日志中命令都相同而且顺序也同样,所以每一个状态机处理相同的命令序列。 这样就能获得相同的状态和相同的输出序列。

图1

一致性算法的工做就是保证复制日志的一致性。 每台服务器上的一致性模块接收来自客户端的命令,并将它们添加到其日志中。 它与其余服务器上的一致性模块通讯,以确保每一个日志最终以相同的顺序包含相同的命令,即便有一些服务器失败。 一旦命令被正确复制,每一个服务器上的状态机按日志顺序处理它们,并将输出返回给客户端。 这样就造成了高可用的复制状态机。

实际系统中的一致性算法一般具备如下属性:

  • 它们确保在全部非拜占庭条件下(包括网络延迟,分区和数据包丢失,重复和乱序)的安全性(不会返回不正确的结果)。

  • 只要任何大多数(过半)服务器均可以运行,而且能够相互通讯和与客户通讯,一致性算法就可用。 所以,五台服务器的典型集群能够容忍任何两台服务器的故障。 假设服务器忽然宕机,它们能够稍后从状态恢复并从新加入群集。

  • 它们不依赖于时序来确保日志的一致性:错误的时钟和极端消息延迟在最坏的状况下会致使可用性问题(译者注:言外之意是能够保证一致性)。

  • 在一般状况下,只要集群的大部分(过半服务器)已经响应了单轮远程过程调用,命令就能够完成; 少数(一半如下)慢服务器不会影响整个系统性能。

3 Paxos 存在的问题

在过去十年里,Leslie Lamport 的 Paxos 协议[15]几乎成为一致性的同义词:它是课堂上教授最多的一致性协议,而且大多数一致性的实现也以它为起点。 Paxos 首先定义了可以在单个决策(例如单个复制日志条目)上达成一致的协议。 咱们将这个子集称为 single-decree Paxos。 而后 Paxos 组合该协议的多个实例以促进一系列决策,例如日志(multi-Paxos)。 Paxos可以确保安全性和活性,而且支持集群成员的变动。它的正确性已被证实,而且在正常状况下是高效的。

不幸的是,Paxos 有两个显著的缺点。 第一个缺点是 Paxos 很是难以理解。 Paxos 的描述晦涩难懂,臭名昭著(译者注:《The Part-time Parliament》比较晦涩难懂,可是《Paxos Made Simple》就比较容易理解); 不多有人成功地理解它,即便能理解也必须付出巨大的努力。 所以,已有几个尝试用更简单的方式来描述 Paxos [16,20,21] 。 这些描述集中在 single-degree Paxos ,但它们仍然具备挑战性。 在对 NSDI 2012 参会者的非正式调查中,咱们发现不多有人喜欢 Paxos ,即便是经验丰富的研究人员。 咱们本身也跟 Paxos 进行了艰苦的斗争; 咱们也没法彻底理解整个协议,直到阅读了几个更简单的描述和本身设计替代 Paxos 的协议,整个过程花了将近一年。

Paxos 晦涩难懂的缘由是做者选择了single-degree Paxos做为基础。Single-decree Paxos 分红两个阶段,这两个阶段没有简单直观的说明,而且不能被单独理解。所以,很难理解为何该算法能起做用。Multi-Paxos 的合成规则又增长了许多复杂性。咱们相信,对多个决定(日志而不是单个日志条目)达成一致的整体问题能够用其余更直接和更明显的方式进行分解。

Paxos的第二个问题是它不能为构建实际的实现提供良好的基础。 一个缘由是没有针对 multi-Paxos 的普遍赞成的算法。 Lamport的描述主要是关于 single-decree Paxos; 他描述了 multi-Paxos 的可能方法,但缺乏许多细节。 已经有几个尝试来具体化和优化 Paxos ,例如[26],[39]和[13],但这些彼此各不相同而且跟 Lamport 描述的也不一样。 像Chubby [4] 这样的系统已经实现了类 Paxos(Paxos-like)算法,但大多数状况下,它们的细节并无公布。

此外,Paxos 的架构对于构建实际系统来讲是一个糟糕的设计,这是 single-decree 分解的另外一个结果。 例如,独立地选择日志条目集合,而后再将它们合并到顺序日志中几乎没有任何好处,这只会增长复杂性。 围绕日志设计系统是更简单和有效的方法,新日志条目按照约束顺序地添加到日志中。 Paxos 的作法适用于只须要作一次决策的状况,若是须要作一系列决策,更简单和快速的方法是先选择一个 leader ,而后让该 leader 协调这些决策。

所以,实际的系统跟 Paxos 相差很大。几乎全部的实现都是从 Paxos 开始,而后发现不少实现上的难题,接着就开发了一种和 Paxos 彻底不同的架构。这样既费时又容易出错,并且 Paxos 自己晦涩难懂使得该问题更加严重。Paxos 的公式可能能够很好地证实它的正确性,可是现实的系统和 Paxos 差异是如此之大,以致于这些证实并无什么太大的价值。下面来自 Chubby 做者的评论很是典型:

在Paxos算法描述和实现现实系统之间有着巨大的鸿沟。最终的系统每每创建在一个还未被证实的协议之上。

因为以上问题,咱们得出的结论是 Paxos 算法没有为系统实践和教学提供一个良好的基础。考虑到一致性问题在大规模软件系统中的重要性,咱们决定尝试设计一个可以替代 Paxos 而且具备更好特性的一致性算法。Raft算法就是此次实验的结果。

4 为可理解性而设计

在设计 Raft 算法过程当中咱们有几个目标:它必须提供一个完整的实际的系统实现基础,这样才能大大减小开发者的工做;它必须在任何状况下都是安全的而且在典型的应用条件下是可用的;而且在正常状况下是高效的。可是咱们最重要的目标也是最大的挑战是可理解性。它必须保证可以被大多数人容易地理解。另外,它必须可以让人造成直观的认识,这样系统的构建者才可以在现实中进行扩展。

在设计 Raft 算法的时候,不少状况下咱们须要在多个备选方案中进行选择。在这种状况下,咱们基于可理解性来评估备选方案:解释各个备选方案的难道有多大(例如,Raft 的状态空间有多复杂,是否有微妙的含义)?对于一个读者而言,彻底理解这个方案和含义是否容易?

咱们意识到这样的分析具备高度的主观性;可是咱们使用了两种通用的技术来解决这个问题。第一个技术就是众所周知的问题分解:只要有可能,咱们就将问题分解成几个相对独立的,可被解决的、可解释的和可理解的子问题。例如,Raft 算法被咱们分红 leader 选举,日志复制,安全性和成员变动几个部分。

咱们使用的第二个方法是经过减小状态的数量来简化状态空间,使得系统更加连贯而且尽量消除不肯定性。特别的,全部的日志是不容许有空洞的,而且 Raft 限制了使日志之间不一致的方式。尽管在大多数状况下咱们都试图去消除不肯定性,可是在某些状况下不肯定性能够提升可理解性。特别是,随机化方法虽然引入了不肯定性,可是他们每每可以经过使用相近的方法处理可能的选择来减小状态空间。咱们使用随机化来简化 Raft 中的 leader 选举算法。

5 Raft 一致性算法

Raft 是一种用来管理第 2 节中描述的复制日志的算法。图 2 是该算法的浓缩,可用做参考,图 3 列举了该算法的一些关键特性。图中的这些内容将在剩下的章节中逐一介绍。

图2

图3

Raft 经过首先选举一个 distinguished leader,而后让它全权负责管理复制日志来实现一致性。Leader 从客户端接收日志条目,把日志条目复制到其余服务器上,而且在保证安全性的时候通知其余服务器将日志条目应用到他们的状态机中。拥有一个 leader 大大简化了对复制日志的管理。例如,leader 能够决定新的日志条目须要放在日志中的什么位置而不须要和其余服务器商议,而且数据都是从 leader 流向其余服务器。leader 可能宕机,也可能和其余服务器断开链接,这时一个新的 leader 会被选举出来。

经过选举一个 leader 的方式,Raft 将一致性问题分解成了三个相对独立的子问题,这些问题将会在接下来的子章节中进行讨论:

  • Leader 选举:当前的 leader 宕机时,一个新的 leader 必须被选举出来。(5.2 节)
  • 日志复制:Leader 必须从客户端接收日志条目而后复制到集群中的其余节点,而且强制要求其余节点的日志和本身的保持一致。
  • 安全性:Raft 中安全性的关键是图 3 中状态机的安全性:若是有任何的服务器节点已经应用了一个特定的日志条目到它的状态机中,那么其余服务器节点不能在同一个日志索引位置应用一条不一样的指令。章节 5.4 阐述了 Raft 算法是如何保证这个特性的;该解决方案在选举机制(5.2 节)上增长了额外的限制。

在展现一致性算法以后,本章节将讨论可用性的一些问题以及时序在系统中的做用。

5.1 Raft 基础

一个 Raft 集群包含若干个服务器节点;一般是 5 个,这样的系统能够容忍 2 个节点的失效。在任什么时候刻,每个服务器节点都处于这三个状态之一:leader、follower 或者 candidate 。在正常状况下,集群中只有一个 leader 而且其余的节点所有都是 follower 。Follower 都是被动的:他们不会发送任何请求,只是简单的响应来自 leader 和 candidate 的请求。Leader 处理全部的客户端请求(若是一个客户端和 follower 通讯,follower 会将请求重定向给 leader)。第三种状态,candidate ,是用来选举一个新的 leader(章节 5.2)。图 4 展现了这些状态和他们之间的转换关系;这些转换关系在接下来会进行讨论。

图4

Raft 把时间分割成任意长度的任期(term),如图 5 所示。任期用连续的整数标记。每一段任期从一次选举开始,一个或者多个 candidate 尝试成为 leader 。若是一个 candidate 赢得选举,而后他就在该任期剩下的时间里充当 leader 。在某些状况下,一次选举没法选出 leader 。在这种状况下,这一任期会以没有 leader 结束;一个新的任期(包含一次新的选举)会很快从新开始。Raft 保证了在任意一个任期内,最多只有一个 leader 。

图5

不一样的服务器节点观察到的任期转换的次数可能不一样,在某些状况下,一个服务器节点可能没有看到 leader 选举过程或者甚至整个任期全程。任期在 Raft 算法中充当逻辑时钟的做用,这使得服务器节点能够发现一些过时的信息好比过期的 leader 。每个服务器节点存储一个当前任期号,该编号随着时间单调递增。服务器之间通讯的时候会交换当前任期号;若是一个服务器的当前任期号比其余的小,该服务器会将本身的任期号更新为较大的那个值。若是一个 candidate 或者 leader 发现本身的任期号过时了,它会当即回到 follower 状态。若是一个节点接收到一个包含过时的任期号的请求,它会直接拒绝这个请求。

Raft 算法中服务器节点之间使用 RPC 进行通讯,而且基本的一致性算法只须要两种类型的 RPC。请求投票(RequestVote) 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)来维持本身的地位。若是一个 follower 在一段选举超时时间内没有接收到任何消息,它就假设系统中没有可用的 leader ,而后开始进行选举以选出新的 leader 。

要开始一次选举过程,follower 先增长本身的当前任期号而且转换到 candidate 状态。而后投票给本身而且并行地向集群中的其余服务器节点发送 RequestVote RPC(让其余服务器节点投票给它)。Candidate 会一直保持当前状态直到如下三件事情之一发生:(a) 它本身赢得了此次的选举(收到过半的投票),(b) 其余的服务器节点成为 leader ,(c) 一段时间以后没有任何获胜者。这些结果会在下面的章节里分别讨论。

当一个 candidate 得到集群中过半服务器节点针对同一个任期的投票,它就赢得了此次选举并成为 leader 。对于同一个任期,每一个服务器节点只会投给一个 candidate ,按照先来先服务(first-come-first-served)的原则(注意:5.4 节在投票上增长了额外的限制)。要求得到过半投票的规则确保了最多只有一个 candidate 赢得这次选举(图 3 中的选举安全性)。一旦 candidate 赢得选举,就当即成为 leader 。而后它会向其余的服务器节点发送心跳消息来肯定本身的地位并阻止新的选举。

在等待投票期间,candidate 可能会收到另外一个声称本身是 leader 的服务器节点发来的 AppendEntries RPC 。若是这个 leader 的任期号(包含在RPC中)不小于 candidate 当前的任期号,那么 candidate 会认可该 leader 的合法地位并回到 follower 状态。 若是 RPC 中的任期号比本身的小,那么 candidate 就会拒绝此次的 RPC 而且继续保持 candidate 状态。

第三种可能的结果是 candidate 既没有赢得选举也没有输:若是有多个 follower 同时成为 candidate ,那么选票可能会被瓜分以致于没有 candidate 赢得过半的投票。当这种状况发生时,每个候选人都会超时,而后经过增长当前任期号来开始一轮新的选举。然而,若是没有其余机制的话,该状况可能会无限重复。

Raft 算法使用随机选举超时时间的方法来确保不多发生选票瓜分的状况,就算发生也能很快地解决。为了阻止选票一开始就被瓜分,选举超时时间是从一个固定的区间(例如 150-300 毫秒)随机选择。这样能够把服务器都分散开以致于在大多数状况下只有一个服务器会选举超时;而后该服务器赢得选举并在其余服务器超时以前发送心跳。一样的机制被用来解决选票被瓜分的状况。每一个 candidate 在开始一次选举的时候会重置一个随机的选举超时时间,而后一直等待直到选举超时;这样减少了在新的选举中再次发生选票瓜分状况的可能性。9.3 节展现了该方案可以快速地选出一个 leader 。

选举的例子能够很好地展现可理解性是如何指导咱们选择设计方案的。起初咱们打算使用一种等级系统(ranking system):每个 candidate 都被赋予一个惟一的等级(rank),等级用来在竞争的 candidate 之间进行选择。若是一个 candidate 发现另外一个 candidate 拥有更高的等级,它就会回到 follower 状态,这样高等级的 candidate 可以更加容易地赢得下一次选举。可是咱们发现这种方法在可用性方面会有一下小问题。咱们对该算法进行了屡次调整,可是每次调整以后都会有新的小问题。最终咱们认为随机重试的方法更加显然且易于理解。

5.3 日志复制

Leader 一旦被选举出来,就开始为客户端请求提供服务。客户端的每个请求都包含一条将被复制状态机执行的指令。Leader 把该指令做为一个新的条目追加到日志中去,而后并行的发起 AppendEntries RPC 给其余的服务器,让它们复制该条目。当该条目被安全地复制(下面会介绍),leader 会应用该条目到它的状态机中(状态机执行该指令)而后把执行的结果返回给客户端。若是 follower 崩溃或者运行缓慢,或者网络丢包,leader 会不断地重试 AppendEntries RPC(即便已经回复了客户端)直到全部的 follower 最终都存储了全部的日志条目。

日志以图 6 展现的方式组织。每一个日志条目存储一条状态机指令和 leader 收到该指令时的任期号。任期号用来检测多个日志副本之间的不一致状况,同时也用来保证图 3 中的某些性质。每一个日志条目都有一个整数索引值来代表它在日志中的位置。

图6

Leader 决定何时把日志条目应用到状态机中是安全的;这种日志条目被称为已提交的。Raft 算法保证全部已提交的日志条目都是持久化的而且最终会被全部可用的状态机执行。一旦建立该日志条目的 leader 将它复制到过半的服务器上,该日志条目就会被提交(例如在图 6 中的条目 7)。同时,leader 日志中该日志条目以前的全部日志条目也都会被提交,包括由其余 leader 建立的条目。5.4 节讨论在 leader 变动以后应用该规则的一些细节,而且证实了这种提交的规则是安全的。Leader 追踪将会被提交的日志条目的最大索引,将来的全部 AppendEntries RPC 都会包含该索引,这样其余的服务器才能最终知道哪些日志条目须要被提交。Follower 一旦知道某个日志条目已经被提交就会将该日志条目应用到本身的本地状态机中(按照日志的顺序)。

咱们设计了 Raft 日志机制来维持不一样服务器之间日志高层次的一致性。这么作不只简化了系统的行为也使得系统行为更加可预测,同时该机制也是保证安全性的重要组成部分。Raft 维护着如下特性,这些同时也构成了图 3 中的日志匹配特性:

  • 若是不一样日志中的两个条目拥有相同的索引和任期号,那么他们存储了相同的指令。
  • 若是不一样日志中的两个条目拥有相同的索引和任期号,那么他们以前的全部日志条目也都相同。

Leader 在特定的任期号内的一个日志索引处最多建立一个日志条目,同时日志条目在日志中的位置也历来不会改变。该点保证了上面的第一条特性。第二个特性是由 AppendEntries RPC 执行一个简单的一致性检查所保证的。在发送 AppendEntries RPC 的时候,leader 会将前一个日志条目的索引位置和任期号包含在里面。若是 follower 在它的日志中找不到包含相同索引位置和任期号的条目,那么他就会拒绝该新的日志条目。一致性检查就像一个概括步骤:一开始空的日志状态确定是知足 Log Matching Property(日志匹配特性) 的,而后一致性检查保证了日志扩展时的日志匹配特性。所以,每当 AppendEntries RPC 返回成功时,leader 就知道 follower 的日志必定和本身相同(从第一个日志条目到最新条目)。

正常操做期间,leader 和 follower 的日志保持一致,因此 AppendEntries RPC 的一致性检查历来不会失败。然而,leader 崩溃的状况会使日志处于不一致的状态(老的 leader 可能尚未彻底复制它日志里的全部条目)。这种不一致会在一系列的 leader 和 follower 崩溃的状况下加重。图 7 展现了在什么状况下 follower 的日志可能和新的 leader 的日志不一样。Follower 可能缺乏一些在新 leader 中有的日志条目,也可能拥有一些新 leader 没有的日志条目,或者同时发生。缺失或多出日志条目的状况可能会涉及到多个任期。

图7

图 7:当一个 leader 成功当选时(最上面那条日志),follower 多是(a-f)中的任何状况。每个盒子表示一个日志条目;里面的数字表示任期号。Follower 可能会缺乏一些日志条目(a-b),可能会有一些未被提交的日志条目(c-d),或者两种状况都存在(e-f)。例如,场景 f 可能这样发生,f 对应的服务器在任期 2 的时候是 leader ,追加了一些日志条目到本身的日志中,一条都还没提交(commit)就崩溃了;该服务器很快重启,在任期 3 从新被选为 leader,又追加了一些日志条目到本身的日志中;在这些任期 2 和任期 3 中的日志都还没被提交以前,该服务器又宕机了,而且在接下来的几个任期里一直处于宕机状态。

在 Raft 算法中,leader 经过强制 follower 复制它的日志来解决不一致的问题。这意味着 follower 中跟 leader 冲突的日志条目会被 leader 的日志条目覆盖。5.4 节会证实经过增长一个限制能够保证安全性。

要使得 follower 的日志跟本身一致,leader 必须找到二者达成一致的最大的日志条目(索引最大),删除 follower 日志中从那个点以后的全部日志条目,而且将本身从那个点以后的全部日志条目发送给 follower 。全部的这些操做都发生在对 AppendEntries RPCs 中一致性检查的回复中。Leader 针对每个 follower 都维护了一个 nextIndex ,表示 leader 要发送给 follower 的下一个日志条目的索引。当选出一个新 leader 时,该 leader 将全部 nextIndex 的值都初始化为本身最后一个日志条目的 index 加1(图 7 中的 11)。若是 follower 的日志和 leader 的不一致,那么下一次 AppendEntries RPC 中的一致性检查就会失败。在被 follower 拒绝以后,leaer 就会减少 nextIndex 值并重试 AppendEntries RPC 。最终 nextIndex 会在某个位置使得 leader 和 follower 的日志达成一致。此时,AppendEntries RPC 就会成功,将 follower 中跟 leader 冲突的日志条目所有删除而后追加 leader 中的日志条目(若是有须要追加的日志条目的话)。一旦 AppendEntries RPC 成功,follower 的日志就和 leader 一致,而且在该任期接下来的时间里保持一致。

若是想要的话,该协议能够被优化来减小被拒绝的 AppendEntries RPC 的个数。例如,当拒绝一个 AppendEntries RPC 的请求的时候,follower 能够包含冲突条目的任期号和本身存储的那个任期的第一个 index 。借助这些信息,leader 能够跳过那个任期内全部冲突的日志条目来减少 nextIndex;这样就变成每一个有冲突日志条目的任期须要一个 AppendEntries RPC 而不是每一个条目一次。在实践中,咱们认为这种优化是没有必要的,由于失败不常常发生而且也不可能有不少不一致的日志条目。

经过这种机制,leader 在当权以后就不须要任何特殊的操做来使日志恢复到一致状态。Leader 只须要进行正常的操做,而后日志就能在回复 AppendEntries 一致性检查失败的时候自动趋于一致。Leader 历来不会覆盖或者删除本身的日志条目(图 3 的 Leader Append-Only 属性)。

这样的日志复制机制展现了第 2 节中描述的一致性特性:只要过半的服务器能正常运行,Raft 就可以接受,复制并应用新的日志条目;在正常状况下,新的日志条目能够在一个 RPC 来回中被复制给集群中的过半机器;而且单个运行慢的 follower 不会影响总体的性能。

5.4 安全性

前面的章节里描述了 Raft 算法是如何进行 leader 选举和日志复制的。然而,到目前为止描述的机制并不能充分地保证每个状态机会按照相同的顺序执行相同的指令。例如,一个 follower 可能会进入不可用状态,在此期间,leader 可能提交了若干的日志条目,而后这个 follower 可能会被选举为 leader 而且用新的日志条目覆盖这些日志条目;结果,不一样的状态机可能会执行不一样的指令序列。

这节经过对 leader 选举增长一个限制来完善 Raft 算法。这一限制保证了对于给定的任意任期号, leader 都包含了以前各个任期全部被提交的日志条目(图 3 中的 Leader Completeness 性质)。有了这一 leader 选举的限制,咱们也使得提交规则更加清晰。最后,咱们展现了对于 Leader Completeness 性质的简要证实而且说明该性质是如何领导复制状态机执行正确的行为的。

5.4.1 选举限制

在任何基于 leader 的一致性算法中,leader 最终都必须存储全部已经提交的日志条目。在某些一致性算法中,例如 Viewstamped Replication[22],一开始并无包含全部已经提交的日志条目的服务器也可能被选为 leader 。这种算法包含一些额外的机制来识别丢失的日志条目并将它们传送给新的 leader ,要么是在选举阶段要么在以后很快进行。不幸的是,这种方法会致使至关大的额外的机制和复杂性。Raft 使用了一种更加简单的方法,它能够保证新 leader 在当选时就包含了以前全部任期号中已经提交的日志条目,不须要再传送这些日志条目给新 leader 。这意味着日志条目的传送是单向的,只从 leader 到 follower,而且 leader 从不会覆盖本地日志中已经存在的条目。

Raft 使用投票的方式来阻止 candidate 赢得选举除非该 candidate 包含了全部已经提交的日志条目。候选人为了赢得选举必须与集群中的过半节点通讯,这意味着至少其中一个服务器节点包含了全部已提交的日志条目。若是 candidate 的日志至少和过半的服务器节点同样新(接下来会精确地定义“新”),那么他必定包含了全部已经提交的日志条目。RequestVote RPC 执行了这样的限制: RPC 中包含了 candidate 的日志信息,若是投票者本身的日志比 candidate 的还新,它会拒绝掉该投票请求。

Raft 经过比较两份日志中最后一条日志条目的索引值和任期号来定义谁的日志比较新。若是两份日志最后条目的任期号不一样,那么任期号大的日志更新。若是两份日志最后条目的任期号相同,那么日志较长的那个更新。

5.4.2 提交以前任期内的日志条目

如同 5.3 节描述的那样,一旦当前任期内的某个日志条目已经存储到过半的服务器节点上,leader 就知道该日志条目已经被提交了。若是某个 leader 在提交某个日志条目以前崩溃了,之后的 leader 会试图完成该日志条目的复制。然而,若是是以前任期内的某个日志条目已经存储到过半的服务器节点上,leader 也没法当即判定该日志条目已经被提交了。图 8 展现了一种状况,一个已经被存储到过半节点上的老日志条目,仍然有可能会被将来的 leader 覆盖掉。

图8

图 8:如图的时间序列展现了为何 leader 没法判断老的任期号内的日志是否已经被提交。在 (a) 中,S1 是 leader ,部分地复制了索引位置 2 的日志条目。在 (b) 中,S1 崩溃了,而后 S5 在任期 3 中经过 S三、S4 和本身的选票赢得选举,而后从客户端接收了一条不同的日志条目放在了索引 2 处。而后到 (c),S5 又崩溃了;S1 从新启动,选举成功,继续复制日志。此时,来自任期 2 的那条日志已经被复制到了集群中的大多数机器上,可是尚未被提交。若是 S1 在 (d) 中又崩溃了,S5 能够从新被选举成功(经过来自 S2,S3 和 S4 的选票),而后覆盖了他们在索引 2 处的日志。可是,在崩溃以前,若是 S1 在本身的任期里复制了日志条目到大多数机器上,如 (e) 中,而后这个条目就会被提交(S5 就不可能选举成功)。 在这种状况下,以前的全部日志也被提交了。

为了消除图 8 中描述的问题,Raft 永远不会经过计算副本数目的方式来提交以前任期内的日志条目。只有 leader 当前任期内的日志条目才经过计算副本数目的方式来提交;一旦当前任期的某个日志条目以这种方式被提交,那么因为日志匹配特性,以前的全部日志条目也都会被间接地提交。在某些状况下,领导人能够安全地判定一个老的日志条目已经被提交(例如,若是该条目已经存储到全部服务器上),可是 Raft 为了简化问题使用了一种更加保守的方法。

Raft 会在提交规则上增长额外的复杂性是由于当 leader 复制以前任期内的日志条目时,这些日志条目都保留原来的任期号。在其余的一致性算法中,若是一个新的 leader 要从新复制以前的任期里的日志时,它必须使用当前新的任期号。Raft 的作法使得更加容易推导出(reason about)日志条目,由于他们自始至终都使用同一个任期号。另外,和其余的算法相比,Raft 中的新 leader 只须要发送更少的日志条目(其余算法中必须在它们被提交以前发送更多的冗余日志条目来给它们从新编号)。

5.4.3 安全性论证

在给出了完整的 Raft 算法以后,咱们如今能够更加精确的讨论 leader 完整性特性(Leader Completeness Prop-erty)(这一讨论基于 9.2 节的安全性证实)。咱们假设 leader 完整性特性是不知足的,而后咱们推出矛盾来。假设任期 T 的 leader(leader T)在任期内提交了一个日志条目,可是该日志条目没有被存储到将来某些任期的 leader 中。假设 U 是大于 T 的没有存储该日志条目的最小任期号。

图9

图 9:若是 S1 (任期 T 的 leader)在它的任期里提交了一个新的日志条目,而后 S5 在以后的任期 U 里被选举为 leader ,那么确定至少会有一个节点,如 S3,既接收了来自 S1 的日志条目,也给 S5 投票了。

  1. U 必定在刚成为 leader 的时候就没有那条被提交的日志条目了(leader 从不会删除或者覆盖任何条目)。

  2. Leader T 复制该日志条目给集群中的过半节点,同时,leader U 从集群中的过半节点赢得了选票。所以,至少有一个节点(投票者)同时接受了来自 leader T 的日志条目和给 leader U 投票了,如图 9。该投票者是产生矛盾的关键。

  3. 该投票者必须在给 leader U 投票以前先接受了从 leader T 发来的已经被提交的日志条目;不然它就会拒绝来自 leader T 的 AppendEntries 请求(由于此时它的任期号会比 T 大)。

  4. 该投票者在给 leader U 投票时依然保有这该日志条目,由于任何 U 、T 之间的 leader 都包含该日志条目(根据上述的假设),leader 从不会删除条目,而且 follower 只有跟 leader 冲突的时候才会删除条目。

  5. 该投票者把本身选票投给 leader U 时,leader U 的日志必须至少和投票者的同样新。这就致使了如下两个矛盾之一。

  6. 首先,若是该投票者和 leader U 的最后一个日志条目的任期号相同,那么 leader U 的日志至少和该投票者的同样长,因此 leader U 的日志必定包含该投票者日志中的全部日志条目。这是一个矛盾,由于该投票者包含了该已被提交的日志条目,可是在上述的假设里,leader U 是不包含的。

  7. 不然,leader U 的最后一个日志条目的任期号就必须比该投票者的大了。此外,该任期号也比 T 大,由于该投票者的最后一个日志条目的任期号至少和 T 同样大(它包含了来自任期 T 的已提交的日志)。建立了 leader U 最后一个日志条目的以前的 leader 必定已经包含了该已被提交的日志条目(根据上述假设,leader U 是第一个不包含该日志条目的 leader)。因此,根据日志匹配特性,leader U 必定也包含该已被提交的日志条目,这里产生了矛盾。

  8. 所以,全部比 T 大的任期的 leader 必定都包含了任期 T 中提交的全部日志条目。

  9. 日志匹配特性保证了将来的 leader 也会包含被间接提交的日志条目,例如图 8 (d) 中的索引 2。

经过 Leader 完整性特性,咱们就能证实图 3 中的状态机安全特性,即若是某个服务器已经将某个给定的索引处的日志条目应用到本身的状态机里了,那么其余的服务器就不会在相同的索引处应用一个不一样的日志条目。在一个服务器应用一个日志条目到本身的状态机中时,它的日志和 leader 的日志从开始到该日志条目都相同,而且该日志条目必须被提交。如今考虑以下最小任期号:某服务器在该任期号中某个特定的索引处应用了一个日志条目;日志完整性特性保证拥有更高任期号的 leader 会存储相同的日志条目,因此以后任期里服务器应用该索引处的日志条目也会是相同的值。所以,状态机安全特性是成立的。

最后,Raft 要求服务器按照日志索引顺序应用日志条目。再加上状态机安全特性,这就意味着全部的服务器都会按照相同的顺序应用相同的日志条目到本身的状态机中。

5.5 Follower 和 candidate 崩溃

到目前为止,咱们只关注了 leader 崩溃的状况。Follower 和 candidate 崩溃后的处理方式比 leader 崩溃要简单的多,而且二者的处理方式是相同的。若是 follower 或者 candidate 崩溃了,那么后续发送给他们的 RequestVote 和 AppendEntries RPCs 都会失败。Raft 经过无限的重试来处理这种失败;若是崩溃的机器重启了,那么这些 RPC 就会成功地完成。若是一个服务器在完成了一个 RPC,可是尚未响应的时候崩溃了,那么在它重启以后就会再次收到一样的请求。Raft 的 RPCs 都是幂等的,因此这样的重试不会形成任何伤害。例如,一个 follower 若是收到 AppendEntries 请求可是它的日志中已经包含了这些日志条目,它就会直接忽略这个新的请求中的这些日志条目。

5.6 定时(timing)和可用性

Raft 的要求之一就是安全性不能依赖定时:整个系统不能由于某些事件运行得比预期快一点或者慢一点就产生错误的结果。可是,可用性(系统可以及时响应客户端)不可避免的要依赖于定时。例如,当有服务器崩溃时,消息交换的时间就会比正常状况下长,candidate 将不会等待太长的时间来赢得选举;没有一个稳定的 leader ,Raft 将没法工做。

Leader 选举是 Raft 中定时最为关键的方面。 只要整个系统知足下面的时间要求,Raft 就能够选举出并维持一个稳定的 leader:

广播时间(broadcastTime) << 选举超时时间(electionTimeout) << 平均故障间隔时间(MTBF)

在这个不等式中,广播时间指的是一个服务器并行地发送 RPCs 给集群中全部的其余服务器并接收到响应的平均时间;选举超时时间就是在 5.2 节中介绍的选举超时时间;平均故障间隔时间就是对于一台服务器而言,两次故障间隔时间的平均值。广播时间必须比选举超时时间小一个量级,这样 leader 才可以可靠地发送心跳消息来阻止 follower 开始进入选举状态;再加上随机化选举超时时间的方法,这个不等式也使得选票瓜分的状况变得不可能。选举超时时间须要比平均故障间隔时间小上几个数量级,这样整个系统才能稳定地运行。当 leader 崩溃后,整个系统会有大约选举超时时间不可用;咱们但愿该状况在整个时间里只占一小部分。

广播时间和平均故障间隔时间是由系统决定的,可是选举超时时间是咱们本身选择的。Raft 的 RPCs 须要接收方将信息持久化地保存到稳定存储中去,因此广播时间大约是 0.5 毫秒到 20 毫秒之间,取决于存储的技术。所以,选举超时时间可能须要在 10 毫秒到 500 毫秒之间。大多数的服务器的平均故障间隔时间都在几个月甚至更长,很容易知足时间的要求。

6 集群成员变动

到目前为止,咱们都假设集群的配置(参与一致性算法的服务器集合)是固定不变的。可是在实践中,偶尔会改变集群的配置的,例如替换那些宕机的机器或者改变复制程度。尽管能够经过使整个集群下线,更新全部配置,而后重启整个集群的方式来实现,可是在更改期间集群会不可用。另外,若是存在手工操做步骤,那么就会有操做失误的风险。为了不这样的问题,咱们决定将配置变动自动化并将其归入到 Raft 一致性算法中来。

为了使配置变动机制可以安全,在转换的过程当中不可以存在任什么时候间点使得同一个任期里可能选出两个 leader 。不幸的是,任何服务器直接从旧的配置转换到新的配置的方案都是不安全的。一次性自动地转换全部服务器是不可能的,因此在转换期间整个集群可能划分红两个独立的大多数(见图 10)。

图10

图 10:直接从一种配置转到另外一种配置是不安全的,由于各个机器会在不一样的时候进行转换。在这个例子中,集群从 3 台机器变成了 5 台。不幸的是,存在这样的一个时间点,同一个任期里两个不一样的 leader 会被选出。一个得到旧配置里过半机器的投票,一个得到新配置里过半机器的投票。

为了保证安全性,配置变动必须采用一种两阶段方法。目前有不少种两阶段的实现。例如,有些系统(好比,[22])在第一阶段停掉旧的配置因此不能处理客户端请求;而后在第二阶段在启用新的配置。在 Raft 中,集群先切换到一个过渡的配置,咱们称之为联合一致(joint consensus);一旦联合一致已经被提交了,那么系统就切换到新的配置上。联合一致结合了老配置和新配置:

  • 日志条目被复制给集群中新、老配置的全部服务器。
  • 新、旧配置的服务器均可以成为 leader 。
  • 达成一致(针对选举和提交)须要分别在两种配置上得到过半的支持。

联合一致容许独立的服务器在不妥协安全性的前提下,在不一样的时刻进行配置转换过程。此外,联合一致容许集群在配置变动期间依然响应客户端请求。

图11

集群配置在复制日志中以特殊的日志条目来存储和通讯;图 11 展现了配置变动过程。当一个 leader 接收到一个改变配置从 C-old 到 C-new 的请求,它就为联合一致将该配置(图中的 C-old,new)存储为一个日志条目,并之前面描述的方式复制该条目。一旦某个服务器将该新配置日志条目增长到本身的日志中,它就会用该配置来作出将来全部的决策(服务器老是使用它日志中最新的配置,不管该配置日志是否已经被提交)。这就意味着 leader 会使用 C-old,new 的规则来决定 C-old,new 的日志条目是何时被提交的。若是 leader 崩溃了,新 leader 多是在 C-old 配置也多是在 C-old,new 配置下选出来的,这取决于赢得选举的 candidate 是否已经接收到了 C-old,new 配置。在任何状况下, C-new 在这一时期都不能作出单方面决定。

一旦 C-old,new 被提交,那么 C-old 和 C-new 都不能在没有获得对方承认的状况下作出决定,而且 leader 完整性特性保证了只有拥有 C-old,new 日志条目的服务器才能被选举为 leader 。如今 leader 建立一个描述 C-new 配置的日志条目并复制到集群其余节点就是安全的了。此外,新的配置被服务器收到后就会当即生效。当新的配置在 C-new 的规则下被提交,旧的配置就变得可有可无,同时不使用新配置的服务器就能够被关闭了。如图 11 所示,任什么时候刻 C-old 和 C-new 都不能单方面作出决定;这保证了安全性。

在关于配置变动还有三个问题须要解决。第一个问题是,新的服务器开始时可能没有存储任何的日志条目。当这些服务器以这种状态加入到集群中,它们须要一段时间来更新来遇上其余服务器,这段它们没法提交新的日志条目。为了不所以而形成的系统短期的不可用,Raft 在配置变动前引入了一个额外的阶段,在该阶段,新的服务器以没有投票权身份加入到集群中来(leader 也复制日志给它们,可是考虑过半的时候不用考虑它们)。一旦该新的服务器追遇上了集群中的其余机器,配置变动就能够按上面描述的方式进行。

第二个问题是,集群的 leader 可能不是新配置中的一员。在这种状况下,leader 一旦提交了 C-new 日志条目就会退位(回到 follower 状态)。这意味着有这样的一段时间(leader 提交 C-new 期间),leader 管理着一个不包括本身的集群;它复制着日志但不把本身算在过半里面。Leader 转换发生在 C-new 被提交的时候,由于这是新配置能够独立运转的最先时刻(将老是可以在 C-new 配置下选出新的领导人)。在此以前,可能只能从 C-old 中选出领导人。

第三个问题是,那些被移除的服务器(不在 C-new 中)可能会扰乱集群。这些服务器将不会再接收到心跳,因此当选举超时,它们就会进行新的选举过程。它们会发送带有新任期号的 RequestVote RPCs ,这样会致使当前的 leader 回到 follower 状态。新的 leader 最终会被选出来,可是被移除的服务器将会再次超时,而后这个过程会再次重复,致使系统可用性不好。

为了防止这种问题,当服务器认为当前 leader 存在时,服务器会忽略RequestVote RPCs 。特别的,当服务器在最小选举超时时间内收到一个 RequestVote RPC,它不会更新任期号或者投票。这不会影响正常的选举,每一个服务器在开始一次选举以前,至少等待最小选举超时时间。相反,这有利于避免被移除的服务器的扰乱:若是 leader 可以发送心跳给集群,那它就不会被更大的任期号废黜。

7 日志压缩

Raft 的日志在正常操做中随着包含更多的客户端请求不断地增加,可是在实际的系统中,日志不能无限制地增加。随着日志愈来愈长,它会占用愈来愈多的空间,而且须要花更多的时间来回放。若是没有必定的机制来清除日志中积累的过时的信息,最终就会带来可用性问题。

快照技术是日志压缩最简单的方法。在快照技术中,整个当前系统的状态都以快照的形式持久化到稳定的存储中,该时间点以前的日志所有丢弃。快照技术被使用在 Chubby 和 ZooKeeper 中,接下来的章节会介绍 Raft 中的快照技术。

增量压缩方法,例如日志清理或者日志结构合并树(log-structured merge trees,LSM 树),都是可行的。这些方法每次只对一小部分数据进行操做,这样就分散了压缩的负载压力。首先,它们先选择一个积累了大量已经被删除或者被覆盖的对象的数据区域,而后重写该区域还活着的对象,以后释放该区域。和快照技术相比,它们须要大量额外的机制和复杂性,快照技术经过操做整个数据集来简化该问题。状态机能够用和快照技术相同的接口来实现 LSM 树,可是日志清除方法就须要修改 Raft 了。

图12

一台服务器用一个新快照替代了它日志中已经提交了的条目(索引 1 到 5),该快照只存储了当前的状态(变量 x 和 y 的值)。快照的 last included index 和 last included term 被保存来定位日志中条目 6 以前的快照

图 12 展现了 Raft 中快照的基本思想。每一个服务器独立地建立快照,快照只包括本身日志中已经被提交的条目。主要的工做是状态机将本身的状态写入快照中。Raft 快照中也包含了少许的元数据:the last included index 指的是最后一个被快照取代的日志条目的索引值(状态机最后应用的日志条目),the last included term 是该条目的任期号。保留这些元数据是为了支持快照后第一个条目的 AppendEntries 一致性检查,由于该条目须要以前的索引值和任期号。为了支持集群成员变动(第 6 节),快照中也包括日志中最新的配置做为 last included index 。一旦服务器完成写快照,他就能够删除 last included index 以前的全部日志条目,包括以前的快照。

尽管一般服务器都是独立地建立快照,可是 leader 必须偶尔发送快照给一些落后的跟随者。这一般发生在 leader 已经丢弃了须要发送给 follower 的下一条日志条目的时候。幸运的是这种状况在常规操做中是不可能的:一个与 leader 保持同步的 follower 一般都会有该日志条目。然而一个例外的运行缓慢的 follower 或者新加入集群的服务器(第 6 节)将不会有这个条目。这时让该 follower 更新到最新的状态的方式就是经过网络把快照发送给它。

Leader 使用 InstallSnapshot RPC 来发送快照给太落后的 follower ;见图 13。当 follower 收到带有这种 RPC 的快照时,它必须决定如何处理已经存在的日志条目。一般该快照会包含接收者日志中没有的信息。在这种状况下,follower 丢弃它全部的日志;这些会被该快照所取代,而且可能一些没有提交的条目会和该快照产生冲突。若是接收到的快照是本身日志的前面部分(因为网络重传或者错误),那么被快照包含的条目将会被所有删除,可是快照以后的条目仍然有用并保留。

图13

这种快照的方式违反了 Raft 的 strong leader 原则,由于 follower 能够在不知道 leader 状态的状况下建立快照。可是咱们认为这种违背是合乎情理的。Leader 的存在,是为了防止在达成一致性的时候的冲突,可是在建立快照的时候,一致性已经达成,所以没有决策会冲突。数据依然只能从 leader 流到 follower ,只是 follower 能够从新组织它们的数据了。

咱们考虑过一种可替代的基于 leader 的快照方案,在该方案中,只有leader 会建立快照,而后 leader 会发送它的快照给全部的 follower 。可是这样作有两个缺点。第一,发送快照会浪费网络带宽而且延缓了快照过程。每一个 follower 都已经拥有了建立本身的快照所须要的信息,并且很显然,follower 从本地的状态中建立快照远比经过网络接收别人发来的要来得经济。第二,leader 的实现会更加复杂。例如,leader 发送快照给 follower 的同时也要并行地将新的日志条目发送给它们,这样才不会阻塞新的客户端请求。

还有两个问题会影响快照的性能。首先,服务器必须决定何时建立快照。若是快照建立过于频繁,那么就会浪费大量的磁盘带宽和其余资源;若是建立快照频率过低,就要承担耗尽存储容量的风险,同时也增长了重启时日志回放的时间。一个简单的策略就是当日志大小达到一个固定大小的时候就建立一次快照。若是这个阈值设置得显著大于指望的快照的大小,那么快照的磁盘带宽负载就会很小。

第二个性能问题就是写入快照须要花费一段时间,而且咱们不但愿它影响到正常的操做。解决方案是经过写时复制的技术,这样新的更新就能够在不影响正在写的快照的状况下被接收。例如,具备泛函数据结构的状态机自然支持这样的功能。另外,操做系统对写时复制技术的支持(如 Linux 上的 fork)能够被用来建立整个状态机的内存快照(咱们的实现用的就是这种方法)。

8 客户端交互

本节介绍客户端如何和 Raft 进行交互,包括客户端如何找到 leader 和 Raft 是如何支持线性化语义的。这些问题对于全部基于一致性的系统都存在,而且 Raft 的解决方案和其余的也差很少。

Raft 的客户端发送全部的请求给 leader 。当客户端第一次启动的时候,它会随机挑选一个服务器进行通讯。若是客户端第一次挑选的服务器不是 leader ,那么该服务器会拒绝客户端的请求而且提供关于它最近接收到的领导人的信息(AppendEntries 请求包含了 leader 的网络地址)。若是 leader 已经崩溃了,客户端请求就会超时;客户端以后会再次随机挑选服务器进行重试。

咱们 Raft 的目标是要实现线性化语义(每一次操做当即执行,只执行一次,在它的调用和回复之间)。可是,如上述,Raft 可能执行同一条命令屡次:例如,若是 leader 在提交了该日志条目以后,响应客户端以前崩溃了,那么客户端会和新的 leader 重试这条指令,致使这条命令被再次执行。解决方案就是客户端对于每一条指令都赋予一个惟一的序列号。而后,状态机跟踪每一个客户端已经处理的最新的序列号以及相关联的回复。若是接收到一条指令,该指令的序列号已经被执行过了,就当即返回结果,而不从新执行该请求。

只读的操做能够直接处理而不须要记录日志。可是,若是不采起任何其余措施,这么作可能会有返回过期数据(stale data)的风险,由于 leader 响应客户端请求时可能已经被新的 leader 替代了,可是它还不知道本身已经不是最新的 leader 了。线性化的读操做确定不会返回过期数据,Raft 须要使用两个额外的预防措施来在不使用日志的状况下保证这一点。首先,leader 必须有关于哪些日志条目被提交了的最新信息。Leader 完整性特性保证了 leader 必定拥有全部已经被提交的日志条目,可是在它任期开始的时候,它可能不知道哪些是已经被提交的。为了知道这些信息,它须要在它的任期里提交一个日志条目。Raft 经过让 leader 在任期开始的时候提交一个空的没有任何操做的日志条目到日志中来处理该问题。第二,leader 在处理只读请求以前必须检查本身是否已经被替代了(若是一个更新的 leader 被选举出来了,它的信息就是过期的了)。Raft 经过让 leader 在响应只读请求以前,先和集群中的过半节点交换一次心跳信息来处理该问题。另外一种可选的方案,leader 能够依赖心跳机制来实现一种租约的形式,可是这种方法依赖 timing 来保证安全性(假设时间偏差是有界的)。

参考资料

  • [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] LAMPORT, L. Time, clocks, and the ordering of events in a distributed system. Commununications of the ACM 21, 7 (July 1978), 558–565.
  • [15] LAMPORT, 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] LAMPORT, 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] LAMPORT, 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.
    17
  • [23] LogCabin source code. logcabin/logcabin.
    http://github.com/
  • [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.
  • [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] SCHNEIDER, 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.

Raft 网站

linbingdong.com
欢迎进入博客 :linbingdong.com 获取最新文章

FullStackPlan 欢迎关注公众号: FullStackPlan 获取更多干货

相关文章
相关标签/搜索