分布式环境Raft一致性共识算法解读

Raft是分布式环境下的一致性算法,它经过少数服从多数的选举来维持集群内数据的一致性。它与RBFT算法名称有点像,然而Raft算法里不能存在拜占庭节点,而RBFT则能容忍BFT节点的存在。Raft很是相似于paxos协议(参见个人这篇文章《paxos算法如何容错的–讲述五虎将的实践》),然而它比paxos协议好理解许多(由于paxos协议难以具体实现,因此zookeeper参考paxos实现了它本身的Zab算法)。一样,Raft有一个用GO语言实现的etcd服务,它的功能与Zookeeper相同,在容器操做系统CoreOS做为核心组件被使用。html

本文先从算法总体上说明其特色,再细说其实现。为方便你们理解,本文仍是以图为主,没有过多涉及算法的细节。Raft易理解易实现,是咱们入门分布式一致性算法的捷径!git

1.算法易被理解

Raft协议的性能并不比paxos的各类实现更高,它的优势主要在于协议的可理解性好,且很是具有可操做性,很容易照着协议就能够实现出稳定、健壮的算法。论文做者在斯坦福和加州大学作过测试,对本科及研究生分别学习paxos和Raft协议课程后测验,在总分60分的测试里其得分以下图所示:github

raft与paxos学生应用成绩

从上图可见,Raft协议的得分(平均25.7)明显高于paxos(平均20.8)。我相信学习过paxos算法的人都有心得:很辛苦的理解后,一段时间后就彻底不记得细节了,至少我本人是如此。而Raft协议很是简单清爽,这个测试也很能反映问题。在测试后,做者还发起了一个调查问卷,询问学生这两个算法哪一个更容易实现?哪一个更容易理解?其答案以下图所示:raft与paxos学生调查问卷算法

可见,这个带有主观性的调查问卷呈现压倒性优点:Raft是一个容易理解、容易实现的算法。数据库

2.算法实现etcd的性能测试数据

Raft有不少语言的实现,包括C++、GO、Scala、Java等(详见https://raft.github.io/),但最有名的实现就是etcd了,它做为CoreOS生态的重要组件而闻名。咱们能够经过etcd的性能数据看一看Raft算法的实际表现。安全

测试集群由3台服务器构成,其配置以下:服务器

  • Google Cloud Compute Engine
  • 3 machines of 8 vCPUs + 16GB Memory + 50GB SSD
  • 1 machine(client) of 16 vCPUs + 30GB Memory + 50GB SSD
  • Ubuntu 17.04
  • etcd 3.2.0, go 1.8.3

下面分别测试写和读的性能。写性能数据以下表所示:网络

Number of keys Key size in bytes Value size in bytes Number of connections Number of clients Target etcd server Average write QPS Average latency per request Average server RSS
10,000 8 256 1 1 leader only 583 1.6ms 48 MB
100,000 8 256 100 1000 leader only 44,341 22ms 124MB
100,000 8 256 100 1000 all members 50,104 20ms 126MB

而读性能数据以下表所示:架构

Number of requests Key size in bytes Value size in bytes Number of connections Number of clients Consistency Average read QPS Average latency per request
10,000 8 256 1 1 Linearizable 1,353 0.7ms
10,000 8 256 1 1 Serializable 2,909 0.3ms
100,000 8 256 100 1000 Linearizable 141,578 5.5ms
100,000 8 256 100 1000 Serializable 185,758 2.2ms

在读性能数据中,Linearizable一致性高于Serializable,故性能稍差。其原始页面在这里:https://coreos.com/etcd/docs/latest/op-guide/performance.html分布式

事实上,在算法的正常运行中与paxos并没有差别。而一旦leader宕机,从发现到从新选举出新leader、新leader开始工做的这段时间的长短,是影响性能的重要指标。下图中对5个节点构成的集群反复的让leader宕机,观察恢复的时间,其结果以下:

raft选举出新领导人耗时数据

在上图中有如下几个关注点:

  • 不一样颜色及线条表明着follower的定时器。如在300ms内没有收到leader的心跳,则发起选举。其中150-300ms这样的数据代表,这5台follower的定时器分布在150ms到300ms之间,呈现随机化。而150-150ms表示没有随机化,全部节点的超时时间是同样的。
  • 横坐标表明着发现到替换掉宕机leader开始服务的时间。数值的单位为毫秒。
  • 纵坐标表示测试时间在所有测试数据中的比例。以上每条线都作了1000次试验(除150-150ms只试验了100次)。

上图代表,增长随机化后,能够大幅减小选举的平均用时。下面的图代表,经过下降最短的超时时间,也能够减小宕机时间。Raft推荐的时间为150-300ms。

三、Raft算法概述

复杂的问题能够经过分解为多个简单的子问题来解决,Raft正是如此(paxos很难分解)。Raft首先定义本身是一个key/value数据库。那么,请求就分为读和写。Raft将问题分解为如下几个要点:

  • 集群里有一台为leader节点服务器,且读写请求都只能向该节点发送,以此保证一致性;
  • 当集群内没有leader节点时,leader节点被多数节点选出来。好比集群有3个节点,那么2个节点赞成的话,就能够选出1个做为leader;
  • 除leader节点外,其余节点叫作follower追随者。leader节点向follower节点同步每条写请求;

所以Raft将一致性问题转换为leader的选举上,以及leader与follower之间的数据同步。咱们先来谈leader与 follower之间的数据同步问题。每一次写请求会修改数据,读请求则不会,因此把leader收到的写请求看成一次操做日志记录下来,且同时把操做日志同步给全部的follower节点,而有一个最终状态数据库记录某一个key的最终值,以下图所示(事实上这与fabric区块链里,多条交易日志构成的世界状态数据库很是类似,详情请参见《区块链开源实现hyperledger fabric架构详解》):

上图中其步骤含义以下:

  1. leader收到写请求y=9,此时状态数据库是y=1;
  2. 将y=9这条日志追加到Log的末尾,同时将该条日志同步给其余follower;
  3. 当多数follower成功收到这条y=9的日志后,leader将状态数据库从y=1更新为y=9;
  4. 返回client表示y=9设置成功。

四、如何选举出leader

当一个集群刚启动时,全部的节点都是follower,follower只能被动的接收leader的消息并响应。此时通过一段时间若follower节点发现全集群没有leader,开始把本身做为leader的候选人向你们征询投票,此时该节点叫作Candidate候选人。若多数follower节点赞成后,则升级为leader节点。而leader节点有义务定时心跳通知全部的 follower节点,使follower节点知道此时集群中的leader是谁。以下图所示:

上图的状态变迁里,follower在一个随机定时器(例如150ms到300ms之间)内没有收到leader的心跳,则开始发起选举,而候选人就是本身,因此本身转化为Candidate,且本身首先投本身一票。若在投票未完成时,发现新的leader出现,则取消投票,由candidate转换为follower。

每次选举是一个任期,这个任期叫作term。每次任期有一个任期号,它是全局的、递增的,当网络中断时虽然会暂时不一致,但网络畅通后会很快同步,以下图所示:

term任期与leader选举

如上图中,蓝色是选举期,绿色是产生leader后。若是不出现意外,这个leader会一直当下云,因此term周期会很长。出现宕机或者网络波动时,从新选举因而出现term2。在term3时也可能一直选举不出新的leader,此时极可能多个candidate发起了投票,票数被平摊后谁也没拿到大多数(因为每台follower的定时器时间是随机的,所以该状况发生几率很小,且发生后也能很快回归正常)。因而会进入term4。

五、操做日志的同步

leader须要把写日志同步到大多数follower后才能更新状态数据库,并向client回复写成功。若是没有获得多数follower的成功应答,leader会重复发送这条日志更新请求。下图中有8条日志3个任期5个节点,每条日志里除记录了操做行为外还记录了当时的任期:

entries

上图中,绿色是第一个任期,其中3条日志条目中第3条日志y=9没有被第4个节点接收到。黄色是第2个任期。绿色与黄色任期内,全部的日志皆被多数节点收到,所以都是写入状态数据库的,这些日志的状态都是commited已提交状态。蓝色是第3个任期,其第8条日志x=4没有被多数节点收到,所以该日志不是committed状态。

leader与 follower之间的日志也可能存在不一致的状况,follower或者少了一些日志,或者多了一些日志,以下图所示:

raft日志不一致

上图中最上面一行是leader的日志,而follower的日志存在如下状况:

  • a、b表示follower相比leader少了几条日志;
  • c、d表示follower相比leader多了几条日志;
  • e、f表示同时少了一些日志,又多了一些日志。好比f状况就是这台follower在任期2时被选为leader,刚添加3条日志尚未提交呢就宕机了,重启后被选为leader,又迅速收到5个写请求加了5条日志,还没提交又宕机了,此时再启动做为follower存在时的状态就是上图f的状态。

leader若是肯定多数机器收到日志,天然能够提交。若是新leader刚被选出来,它会试图把多数机器上保存的日志(即便它本身没有这条日志)–也就是前任的日志也提交,但这未必保证必定成功,以下图所示:

在上图中,d和e就是提交前任日志努力下可能致使的两种情况:

  1. 在a中,S1是leader,前写入日志2并只同步日志到S1和S2,还未到其余节点时就宕机了;
  2. 在b中,S5经过它本身、S三、S4的投票被选为leader,所以它并不知道日志2的存在。此时它收到client的新请求写入日志3,而刚写入日志3就宕机了;
  3. 在c中,S1从新被选为leader,此时它发现日志2还未被复制到多数follower,开始复制日志2。此时S1收到新请求,并记录了日志4;
  4. 在d中是第一种场景,此时老的日志2被复制到了S3上,然而此时的日志2虽然被S一、S二、S3多数节点持有,但倒是经过2次任期完成的,且新任期里的日志4并未被复制到多数机器上,因此日志2并不能认定能够处于commited状态。若此时S1宕机,S5从新当选,则日志2会被覆盖丢弃,固然也包括未被复制到多数机器的日志4;
  5. 在e中是接着c的第二种场景,若日志4也被复制到S一、S二、S3这多数机器上,则日志2与日志3同时处于commited状态,永远不会被覆盖。

六、集群规模的配置变化

一般咱们把raft集群配置为3或者5个节点,特别是5个节点时能够容忍2个节点宕机。这给咱们平滑升级时带来了好处:1台台升级时仍然能够容忍1台宕机。但若咱们的集群原来是3个节点的组合,却改成5个节点,若是这个过程是不中止服务动态完成的,这可能出现问题,以下图所示:

Raft直接从3台谈到5台不安全

在上图中,绿色的老配置只有一、二、3这三台server组成集群,而在蓝色的新配置里则在一、二、三、四、5这五台server组成的新集群。因而,存在红色箭头指标的点,在该点上,可能一、2这两台server根据老配置在它们2个中选出第1个leader,而三、四、5根据新配置在它们3个中选出了第2个leader。同一时刻出现了2个leader,这样数据就会不一致。

为了解决上述问题,Raft提出了一个共同一致状态,该状态处于老配置和新配置生效的中间阶段。首先,咱们设C(old)为老配置,而新配置为C(new),欲从C(old)状态置C(new),必须经历C(old,new)状态。其中,更新到C(old,new)以及C(new)时,仍然以复制日志的方式进行,即:先进行日志复制,当肯定多数节点收到该日志后,则该日志为commited已提交状态。以下图所示:

Raft更新配置时的共同一致状态

从上图中能够看到:

  1. 在C(old,new)日志开始复制时,仍然仅使用C(old)这一种配置,因此不会出现双leader;
  2. 而C(old,new)一旦进入commited提交状态,此时若leader宕机从新选举,则要求必须是具有C(old,new)的candidate才能被选为新leader;
  3. 以后,leader开始复制日志C(new),从这一刻起leader的新配置开始生效。

七、日志的优化

能够看到,Raft算法的核心就是leader选举以及日志复制。而日志的无限增加,必然带来性能问题,这是从工程角度必须解决的问题。日志表示的是过程,状态数据库表示的是结果;一样,咱们能够按期把某一时间点以前的日志作成状态数据库,或者称为快照,仅保留该时间点后的日志,这样就能够大幅减小日志的数量。以下图所示:

raft日志快照

在上图中,原先的已经被提交的5条日志最终致使的状态是x=0&&y=9,故能够被快照替代,这便减小了日志量。

八、小结

Raft还有一个很是形象的算法演示动画,包含了一致性算法的由来、leader的选举、隔离网络下的leader选举、日志的复制等场景,请打开RaftUnderstandable Distributed Consensus连接观看。

学习Raft算法有助于咱们理解分布式环境下的一致性解决方案,并且它确实比paxos好理解许多,能够做为咱们的入门算法。

 

(转载本站文章请注明做者和出处 陶辉笔记 ,请勿用于任何商业用途)