转载:https://www.cnblogs.com/linbingdong/p/6253479.htmlhtml
Paxos算法在分布式领域具备很是重要的地位。可是Paxos算法有两个比较明显的缺点:1.难以理解 2.工程实现更难。算法
网上有不少讲解Paxos算法的文章,可是质量良莠不齐。看了不少关于Paxos的资料后发现,学习Paxos最好的资料是论文《Paxos Made Simple》,其次是中、英文版维基百科对Paxos的介绍。本文试图带你们一步步揭开Paxos神秘的面纱。安全
Paxos算法是基于消息传递且具备高度容错特性的一致性算法,是目前公认的解决分布式一致性问题最有效的算法之一。网络
Google Chubby的做者Mike Burrows说过这个世界上只有一种一致性算法,那就是Paxos,其它的算法都是残次品。分布式
虽然Mike Burrows说得有点夸张,可是至少说明了Paxos算法的地位。然而,Paxos算法也由于晦涩难懂而臭名昭著。本文的目的就是带领你们深刻浅出理解Paxos算法,不只理解它的执行流程,还要理解算法的推导过程,做者是怎么一步步想到最终的方案的。只有理解了推导过程,才能深入掌握该算法的精髓。并且理解推导过程对于咱们的思惟也是很是有帮助的,可能会给咱们带来一些解决问题的思路,对咱们有所启发。学习
在常见的分布式系统中,总会发生诸如机器宕机或网络异常(包括消息的延迟、丢失、重复、乱序,还有网络分区)等状况。Paxos算法须要解决的问题就是如何在一个可能发生上述异常的分布式系统中,快速且正确地在集群内部对某个数据的值达成一致,而且保证不论发生以上任何异常,都不会破坏整个系统的一致性。优化
注:这里某个数据的值并不仅是狭义上的某个数,它能够是一条日志,也能够是一条命令(command)。。。根据应用场景不一样,某个数据的值有不一样的含义。设计
在Paxos算法中,有三种角色:3d
在具体的实现中,一个进程可能同时充当多种角色。好比一个进程可能既是Proposer又是Acceptor又是Learner。日志
还有一个很重要的概念叫提案(Proposal)。最终要达成一致的value就在提案里。
注:
Proposer能够提出(propose)提案;Acceptor能够接受(accept)提案;若是某个提案被选定(chosen),那么该提案里的value就被选定了。
回到刚刚说的『对某个数据的值达成一致』,指的是Proposer、Acceptor、Learner都认为同一个value被选定(chosen)。那么,Proposer、Acceptor、Learner分别在什么状况下才能认为某个value被选定呢?
假设有一组能够提出(propose)value(value在提案Proposal里)的进程集合。一个一致性算法须要保证提出的这么多value中,只有一个value被选定(chosen)。若是没有value被提出,就不该该有value被选定。若是一个value被选定,那么全部进程都应该能学习(learn)到这个被选定的value。对于一致性算法,安全性(safaty)要求以下:
咱们不去精确地定义其活性(liveness)要求。咱们的目标是保证最终有一个提出的value被选定。当一个value被选定后,进程最终也能学习到这个value。
Paxos的目标:保证最终有一个value会被选定,当value被选定后,进程最终也能获取到被选定的value。
假设不一样角色之间能够经过发送消息来进行通讯,那么:
假设只有一个Acceptor(能够有多个Proposer),只要Acceptor接受它收到的第一个提案,则该提案被选定,该提案里的value就是被选定的value。这样就保证只有一个value会被选定。
可是,若是这个惟一的Acceptor宕机了,那么整个系统就没法工做了!
所以,必需要有多个Acceptor!
多个Acceptor的状况以下图。那么,如何保证在多个Proposer和多个Acceptor的状况下选定一个value呢?
下面开始寻找解决方案。
若是咱们但愿即便只有一个Proposer提出了一个value,该value也最终被选定。
那么,就获得下面的约束:
P1:一个Acceptor必须接受它收到的第一个提案。
可是,这又会引出另外一个问题:若是每一个Proposer分别提出不一样的value,发给不一样的Acceptor。根据P1,Acceptor分别接受本身收到的value,就致使不一样的value被选定。出现了不一致。以下图:
刚刚是由于『一个提案只要被一个Acceptor接受,则该提案的value就被选定了』才致使了出现上面不一致的问题。所以,咱们须要加一个规定:
规定:一个提案被选定须要被半数以上的Acceptor接受
这个规定又暗示了:『一个Acceptor必须可以接受不止一个提案!』否则可能致使最终没有value被选定。好比上图的状况。v一、v二、v3都没有被选定,由于它们都只被一个Acceptor的接受。
最开始讲的『提案=value』已经不能知足需求了,因而从新设计提案,给每一个提案加上一个提案编号,表示提案被提出的顺序。令『提案=提案编号+value』。
虽然容许多个提案被选定,但必须保证全部被选定的提案都具备相同的value值。不然又会出现不一致。
因而有了下面的约束:
P2:若是某个value为v的提案被选定了,那么每一个编号更高的被选定提案的value必须也是v。
一个提案只有被Acceptor接受才可能被选定,所以咱们能够把P2约束改写成对Acceptor接受的提案的约束P2a。
P2a:若是某个value为v的提案被选定了,那么每一个编号更高的被Acceptor接受的提案的value必须也是v。
只要知足了P2a,就能知足P2。
可是,考虑以下的状况:假设总的有5个Acceptor。Proposer2提出[M1,V1]的提案,Acceptor2~5(半数以上)均接受了该提案,因而对于Acceptor2~5和Proposer2来说,它们都认为V1被选定。Acceptor1刚刚从宕机状态恢复过来(以前Acceptor1没有收到过任何提案),此时Proposer1向Acceptor1发送了[M2,V2]的提案(V2≠V1且M2>M1),对于Acceptor1来说,这是它收到的第一个提案。根据P1(一个Acceptor必须接受它收到的第一个提案。),Acceptor1必须接受该提案!同时Acceptor1认为V2被选定。这就出现了两个问题:
因此咱们要对P2a约束进行强化!
P2a是对Acceptor接受的提案约束,但其实提案是Proposer提出来的,全部咱们能够对Proposer提出的提案进行约束。获得P2b:
P2b:若是某个value为v的提案被选定了,那么以后任何Proposer提出的编号更高的提案的value必须也是v。
由P2b能够推出P2a进而推出P2。
那么,如何确保在某个value为v的提案被选定后,Proposer提出的编号更高的提案的value都是v呢?
只要知足P2c便可:
P2c:对于任意的N和V,若是提案[N, V]被提出,那么存在一个半数以上的Acceptor组成的集合S,知足如下两个条件中的任意一个:
为了知足P2b,这里有个比较重要的思想:Proposer生成提案以前,应该先去『学习』已经被选定或者可能被选定的value,而后以该value做为本身提出的提案的value。若是没有value被选定,Proposer才能够本身决定value的值。这样才能达成一致。这个学习的阶段是经过一个『Prepare请求』实现的。
因而咱们获得了以下的提案生成算法:
(b) 若是Acceptor已经接受过提案,那么就向Proposer响应已经接受过的编号小于N的最大编号的提案。
咱们将该请求称为编号为N的Prepare请求。
若是Proposer收到了半数以上的Acceptor的响应,那么它就能够生成编号为N,Value为V的提案[N,V]。这里的V是全部的响应中编号最大的提案的Value。若是全部的响应中都没有提案,那 么此时V就能够由Proposer本身选择。
生成提案后,Proposer将该提案发送给半数以上的Acceptor集合,并指望这些Acceptor能接受该提案。咱们称该请求为Accept请求。(注意:此时接受Accept请求的Acceptor集合不必定是以前响应Prepare请求的Acceptor集合)
Acceptor能够忽略任何请求(包括Prepare请求和Accept请求)而不用担忧破坏算法的安全性。所以,咱们这里要讨论的是何时Acceptor能够响应一个请求。
咱们对Acceptor接受提案给出以下约束:
P1a:一个Acceptor只要尚未响应过任何编号大于N的Prepare请求,那么他就能够接受这个编号为N的提案。
若是Acceptor收到一个编号为N的Prepare请求,在此以前它已经响应过编号大于N的Prepare请求。根据P1a,该Acceptor不可能接受编号为N的提案。所以,该Acceptor能够忽略编号为N的Prepare请求。固然,也能够回复一个error,让Proposer尽早知道本身的提案不会被接受。
所以,一个Acceptor只需记住:1. 已接受的编号最大的提案 2. 已响应的请求的最大编号。
通过上面的推导,咱们总结下Paxos算法的流程。
Paxos算法分为两个阶段。具体以下:
阶段一:
(a) Proposer选择一个提案编号N,而后向半数以上的Acceptor发送编号为N的Prepare请求。
(b) 若是一个Acceptor收到一个编号为N的Prepare请求,且N大于该Acceptor已经响应过的全部Prepare请求的编号,那么它就会将它已经接受过的编号最大的提案(若是有的话)做为响应反馈给Proposer,同时该Acceptor承诺再也不接受任何编号小于N的提案。
阶段二:
(a) 若是Proposer收到半数以上Acceptor对其发出的编号为N的Prepare请求的响应,那么它就会发送一个针对[N,V]提案的Accept请求给半数以上的Acceptor。注意:V就是收到的响应中编号最大的提案的value,若是响应中不包含任何提案,那么V就由Proposer本身决定。
(b) 若是Acceptor收到一个针对编号为N的提案的Accept请求,只要该Acceptor没有对编号大于N的Prepare请求作出过响应,它就接受该提案。
Learner学习(获取)被选定的value有以下三种方案:
经过选取主Proposer,就能够保证Paxos算法的活性。至此,咱们获得一个既能保证安全性,又能保证活性的分布式一致性算法——Paxos算法。