Raft协议比paxos的优点是 容易理解,容易实现。它强化了leader的地位,把整个协议可以清楚的分割成两个部分,并利用日志的连续性做了一些简化:
(1)Leader在时。由Leader向Follower同步日志
(2)Leader挂掉了,选一个新Leader,Leader选举算法。
但是本质上来说,它容易的地方在于流程清晰,描述更清晰,关键之处都给出了伪代码级别的描述,可以直接用于实现,而paxos最初的描述是针对非常理论的一致性问题,真正能应用于工程实现的mulit-paxos,Lamport老爷爷就提了个大概,之后也有人没尝试对multi-paxos做出更为完整详细的描述,但是每个人描述的都不大一样。
Zookeeper的ZAB,Viewstamped Replication(VR),raft,multi-paxos,这些都可以被称之为Leader-based一致性协议。不同的是,multi-paxos leader是作为对经典paxos的优化而提出,通过选择一个proposer作为leader降低多个proposer引起冲突的频率,提升性能将一次决议的平均消息代价缩小到最优的两次,实际上就算有多个leader存在,算法还是安全的,只是退化为经典的paxos算法。而经典的paxos,从一个提案被提出到被接受分为两个阶段,第一个阶段去询问值,第二阶段根据询问的结果提出值。这两个阶段是无法分割的,两个阶段的每个细节都是精心设计的,相互关联,共同保障了协议的一致性。而VR,ZAB,Raft这些强调唯一的leader的协议,它们直接从leader的角度描述协议的流程,但是实际上它们使用了和Paxos完全一样的原理来保证协议的安全性,当同时存在多个节点同时尝试成为leader或者多个节点都认为自己时leader时,它们都可以视作是经典Paxos流程。
Paxos和raft都是一旦一个entries(raft协议叫日志,paxos叫提案,叫法而已)得到多数派的赞成,这个entries就会定下来,不丢失,值不更改,最终所有节点都会赞成它。Paxos中称为提案被决定,Raft,ZAB,VR称为日志被提交,这只是说法问题。一个日志一旦被提交(或者决定),就不会丢失,也不可能更改,这一点这4个协议都是一致的。Multi-paxos和Raft都用一个数字来标识leader的合法性,multi-paxos中叫proposer-id,Raft叫term,意义是一样的,multi-paxos proposer-id最大的Leader提出的决议才是有效的,raft协议中term最大的leader才是合法的。实际上raft协议在leader选举阶段,由于老leader可能也还存活,也会存在不只一个leader的情形,只是不存在term一样的两个leader,因为选举算法要求leader得到同一个term的多数派的同意,同时赞同的成员会承诺不接受term更小的任何消息。这样可以根据term大小来区分谁是合法的leader。Multi-paxos的区分leader的合法性策略其实是一样的,谁的proproser-id大谁合法,而proposer-id是唯一的。因此它们其实在同一个时刻,都只会存在一个合法的leader。同时raft协议的Leader选举算法,新选举出的Leader已经拥有全部的可以被提交的日志,而multi-paxos择不需要保证这一点,这也意味multi-paxos需要额外的流程从其它节点获取已经被提交的日志。因此raft协议日志可以简单的只从leader流向follower在raft协议中,而multi-paxos则需要额外的流程补全已提交的日志。需要注意的是日志可以被提交和日志已经被提交是两个概念,它们的区别就像是我前方有块石头和我得知我前方有块石头。但是实际上,Raft和multi-Paxos一旦日志可以被提交,就能会保证不丢失,multi-paxos天然保证了这一点,这也是为什么新leader对于尚未被确认已经提交的日志需要重新执行经典paxos的阶段一,来补全可能缺失的已经被提交的日志,Raft协议通过强制新Leader首先提交一个本term的no-op 日志,配合前面提到的Leader选举算法所保证的性质,确保了这一点。一条日志一旦被多数派的节点写入本地日志文件中,就可以被提交,但是leader只有得知这一点后,才会真正commit这条日志,此时日志才是已经被提交的。
Raft协议强调日志的连续性,multi-paxos则允许日志有空洞。日志的连续性蕴含了这样一条性质:如果两个不同节点上相同序号的日志,term相同,那么这和这之前的日志必然也相同的。raft协议利用日志的连续性,leader可以很方便的得知自己的follower拥有的日志的情况,Follower只要告诉Leader自己本地日志文件的最后一个日志的序号和term就可以了;同时由于已经commit的日志本身也是连续的,只需要记录最后一条已经commit的日志的位置,就可以判定这条日志之前所有的日志都已被提交。而multi-paxos则不行,所以当新leader产生时需要每个日志重新用proposer-id重走一遍所有的日志。可以举个列子,A,B,C三台机器,C是Leader,term是3,A告诉C它们最后一个日志的序列号都是4,term是3,那么C就知道A肯定有序列号为1,2,3,4的日志,而且和C中的序列号为1,2,3,4的日志一样,这是raft协议日志的连续性所强调的,好了那么Leader知道日志1,2,3,4已经被多数派(A,C)拥有了,可以提交了。同时,这也保证raft协议在leader选举的时候,只需要从一个多数集中选择出最后一条日志term最大并且日志数目最多的节点,新leader同样必定拥有所有的已commit的日志。这是由于任意一条commit的日志,至少被多数派记录,而由于日志的连续性,拥有最后一条commit的日志也就意味着拥有全部的commit日志,因此raft协议中一个多数派必然存在一个节点拥有全部的已提交的日志。而对于multi-paxos每个日志需要单独被确认是否可以提交,因此当新leader产生后,它只好重新对每个日志进行确认,已确定它们是否可以被提交,甚至于新leader可能缺失可以被提交的日志,需要向其它节点学习到缺失的可以被提交的日志,当然这都可以通过向一个多数派询问完成(这个流程存在着很大的优化空间,例如可以将这个流程合并到leader选举阶段,可以将所有日志的确认和学习合并到一轮消息中,减少消息数目等)。所以本质上,两者是一样的。一个日志被多数派拥有,那么它就可以被提交,但是Leader需要通过某种方式得知这一点,同时为了已经被提交的日志不被新leader覆写,新leader需要拥有所有已经被提交的日志(或者说可以被提交,因为有时候并没有办法得知一条可以被提交的日志是否已经被提交),之后才能正常工作。两者的区别在于Leader确认提交和获取所有可以被提交日志的方式上,而方式上的区别又是由于是日志是否连续造成的,Raft协议利用日志连续性,简化了这个过程。
在Raft和multi-paxos协议确保安全性的原理上,更进一步的说,所有的凡是 满足 集群中存活的节点数还能构成一个多数派,一致性就能满足的算法,raft协议,paxos,zab,viewstamp都是利用了同一个性质:两个多数派集合之间存在一个公共成员这个特性。对于一致性协议来说,就是一旦一个变量的值被确定,那么这个变量的值应该是唯一的,不再更改的。Raft,paoxos等协议,对于一个变量v来说,一个由节点n1提出的值a只有被一个多数集q1认可并记录后,才会正式令v=a,如果另一个节点n2想要修改变量v的值为b,也需要一个多数集q2的认可,而q1和q2必然至少有一个共同的成员p,节点p已经记录了v=a。因此只需要通过增加一些约束,让p能够告诉节点n2这个事实:v=a,使得n2放弃自己的提议,或者让节点p拒绝节点n2想要赋予v的值为b这个行为,都可以确保变量v的一致性不被破坏。这个思想对于这个四个协议来说都是一样的,4个协议都使用一个唯一的整数作为标识符来标明leader的合法性,paxos叫做proposer-id,ZAB叫epoch,VR叫view,raft叫term。把leader看做是想要赋予变量v某个值的节点n1,n2,上面提到的情形中,如果n2是目前的合法leader,那么n2需要知道v=a这个事实,对于raft来说就是选择n2是已经记录了v=a的节点,对于multi-paxos来说,就是重新确认下v的值。如果n1是目前的合法leader,n2是老的leader,p就会根据leader的标识符拒绝掉n2的提案,n2的提案会由于得不到一个多数派的接受而失效。最直接的从理论上阐明这个原理的是经典的paxos算法,关于这个原理更具体的阐述可以看看我在如何浅显易懂地解说 Paxos 的算法?下的回答。所以确实在一定程度上可以视raft,ZAB,VR都是paxos算法的一种改进,一种简化,一种优化,一种具象化。Lamport老人家还是叼叼叼。。。。。。。不过值得一提的是,ZAB和raft作者确实是受了paxos很多启发,VR是几乎同时独立于paxos提出的。
Raft容易实现在于它的描述是非常规范的,包括了所有的实现细节。如上面的人说的有如伪代码。而paxos的描述侧重于理论,工程实现按照谷歌chubby论文中的说话,大家从paxos出现,写着写着,处理了n多实际中的细节之后,已经变成另外一个算法了,这时候正确性已经无法得到理论的保证。所以它的实现非常难,因为一致性协议实非常精妙的。小细节上没考虑好,整个协议的一致性就崩溃了,而发现并更正细节上的错误在没有详尽的现成的参考的情况下是困难的,这需要对协议很深的理解。而且在Raft协议的博士论文CONSENSUS: BRIDGING THEORY AND PRACTICE,两位作者手把手事无巨细的教你如何用raft协议构建一个复制状态机。我表示智商正常的大学生,都能看懂。我相信在未来一致性现在被提起来,肯定不是现在这样,大部分人觉得好难啊,实现更难。。。。应该会成为一种常用技术。
raft利用日志连续性对Paxos做了大量很好的简化,但是其中有一点有很强的误导性,就是新任leader对于旧日志的处理,他的原文描述是“Raft uses a simpler approach where it guarantees that all the committed entries from previous terms are present on each new leader from the moment of its election, without the need to transfer those entries to the leader.”
raft利用日志连续确认的特性,只要选举termID最大的做leader,就可以保证leader当选以后,不需要对本地旧日志重新投票,而是请follower过来直接抓就行了。即使对哪些尚未形成多数派的entry,也是一样的处理方式,并未进行重新投票,在他的协议里这个做法是没有问题的。
但是multi-paxos原本的意思是,即使形成了多数派,仍然需要使用新的proposalID走一遍prepare-accept过程,工程上做了优化之后,对于明确标记了已形成多数派的entry可以不重新投票,但是未确认是否形成多数派的entry,则要求新任leader使用新的proposalID重新投票。
因为multi-paxos并不假设日志间有连续确认的关系,每条日志之间相互独立,没有关系,1号日志尚未确认时,2号日志就可以确认形成多数派备份。附一张之前分享ppt里的图,大家自己琢磨吧。