——转自:{老码农的专栏}算法
Paxos算法的难理解与算法的知名度同样使人敬仰,从我我的的经历而言,难理解的缘由并非该算法高深到你们智商不够,而在于Lamport在表达该算法时过于晦涩且缺少一个完整的应用场景。若是大师能换种思路表达该算法,你们可能会更容易接受:数据库
Lamport首先提出算法的起源,在没有任何辅助场景下,已经让不少人陷于泥潭,在满脑子疑问的前提下,根本没法继续接触算法的具体内容,更无从体会算法的精华。本文将换种表达方法对Paxos算法进行从新描述。promise
咱们全部的描述都假设读者已经熟读了Lamport的paxos-simple一文,所以对各类概念再也不解释。网络
除了Lamport的几篇论文,对Paxos算法描述比较简洁的中文文章是:http://zh.wikipedia.org/zh-cn/Paxos%E7%AE%97%E6%B3%95,该文翻译的比较到位,但在关键细节上仍是存在一些歧义和一些对原文不正确的理解,可能会致使读者对Paxos算法更迷茫,但阅读该文能够快速地对Paxos算法有个大概的了解。less
1.应用场景异步
(1)分布式中的一致性分布式
Paxos算法主要是解决一致性问题,关于“一致性”,在不一样的场景有不一样的解释:spa
(2)MQ.net
假如全部系统的Log信息都写入一个MQ Server,而后经过MQ把每条Log指令发异步送到多个Log Server写入文件(写入多个Log Server的缘由是对Log文件作备份以防数据丢失),则全部Log Server上的数据确定是一致的(Log内容及顺序彻底相同),由于MQ自己就有排序功能,只要进了Q数据也就有了序,至关于编了全局惟一的号,不管把这些数据写入多少个文件,只要按编号,各文件的内容一定是一致的,但一个MQ Server显然是一个单点,若是宕机,会影响整个系统的可用性。线程
(3)多MQ
要解决MQ单点问题,首选方案是采用多个MQ Server,即便用一个MQ Cluster,客户端能够访问任意MQ Server,不一样的客户端可能访问不一样MQ Server,不一样MQ Server上的数据内容、顺序可能不一致,若是不解决这个问题,每一个MQ Server写入Log Server的内容就不一致,这显然不是咱们指望的结果。
(4)NoSQL中的数据更新
通常的NoSQL都会经过数据复制的形式保证其可用性,但客户端对多数据进行操做时,可能会有不少对同一数据的操做发送的某一台或几台Server,有可能执行:Insert、Update A、Update B....Update N,就一次Insert连续屡次Update,最终复制Server上也必须执行这一的更新操做,若是由于线程池、网络、Server资源等缘由致使各复制Server接收到的更新顺序不一致,这样的复制数据就失去了意义,若是在金融领域甚至会形成严重的后果。
上面这些不一致问题性正是Paxos算法要解决的,固然这些问题也不是只有Paxos能解决,在没有Paxos以前这些问题也获得了解决,好比经过使用双Master模式的MQ解决MQ单点问题;经过使用Master Server解决NoSQL的复制问题,但这些解决方法都存在一些缺陷,要么难水平扩展,要么影响可用性。固然除了Paxos算法还有其余一些算法也试图解决这类问题,好比:Viewstamped Replication算法。
上面描述的这些场景的共性是但愿多Server之间状态一致,也就是一致性,再看中文Wiki开篇提到的:
在一个分布式数据库系统中,若是各节点的初始状态一致,每一个节点都执行相同的操做序列,那么他们最后能获得一个一致的状态。为保证每一个节点执行相同的命令序列,须要在每一条指令上执行一个“一致性算法”以保证每一个节点看到的指令一致
你们或许会对该描述有更深的理解。
2.Paxos如何解决这类问题
Paxos对这类问题的解决就是试图对各Server上的状态进行全局编号,若是能编号成功,那么全部操做都按照编号顺序执行,一致性就不言而喻。当Cluster中的Server都接收了一些数据,如何进行编号?就是表决,让全部的Server进行表决,看哪一个Server上的哪一个数据应该排第一,哪一个排第二...,只要多数Server赞成某个数据该排第几,那就排第几。
很显然,为了给每一个数据惟一编号,每次表决只能产生一个数据,不然表决就没有任何意义。Paxos的算法的全部精力都放在如何在一次表决只产生一个数据。再进一步,咱们称表决的数据叫Value,Paxos算法的核心和精华就是确保每次表决只产生一个Value。
3.Paxos算法
咱们对原文的概念加以补充:
也就是说,Acceptor对proposer有两个动做:promise和accept
下面的解释也主要围绕着”Only a single value is chosen,“,再看下条件P1,
P1:An acceptor must accept the first proposal that it receives.
乍一看,这个条件是显然的,由于以前没有任何value,acceptor理所固然地应该accept第一个proposal,但仔细想一想,感受P1这个条件很不严格,究竟是一个对问题的简单描述仍是一个数学上严格的必要条件?这些疑问归结为2个问题:
(1)这个条件本质上在保证什么?
(2)第二个proposal怎么办?
在后续的算法中看到一个Acceptor是否批准一个Value与它是不是第一个没有任何关系,而与这个Proposal的编号有关。那岂不说明P1没有获得保证?开始我也百思不得其解,后来通过跟朋友交流发现,P1中的"accept"实际上是指acceptor对proposer的"promise",就是语言描述跟算法的步骤描述之间存在歧义,所以我认为对算法问题仍是应该采用数学语法而非文字语言。
因此,P1是强调了第一个proposal要被promise,但第二个还未提到,这也是疑问之一。
也很显然的是,单靠P1是没法保证Paxos算法的,因可能没法造成多数派,那接下来的讨论应该是考虑如何弥补P1的缺点,使其能够保证Paxos算法,就是咱们但愿将来的条件应该说明:
因而约束P2出现了:
P2:If a proposal with value v is chosen, then every higher-numbered proposal that is chosen has value v.
P2的出现让人大跌眼镜,P2并没沿着P1的路向下走,也没有解决P1的上述2个不完备,而是从另外一个侧面讨论如何保证只能选出一个Value。P1讨论的是该如何选择,P2讨论的是一旦被选出来,以后的选择应该不变,就是P1还在讨论选的问题,P2已经选出来了,中间有个断层,怎么选的没有讨论。
其实从后面Lamport不断对P2加强能够看出,P2里面蕴含着P1(经过proposal编号,第一次以前没有编号,因此选择),P2才真正给出了怎么选择的具体过程,从过后分析看,P1给出了第一个该怎么选,P2给出了全部的该怎么选,条件有点重复。因此,把P1和P2看做是两个独立条件的作法是不许确的,于是中文wiki中提到“若是 P1 和 P2 都可以保证,那么约束2就可以保证”,对细微理解有必定的影响。
也不是说P1就没有用,反过来看,P2是个未知问题,而P1是这个未知问题的已知部分,从契约的角度来看,P1就是个不变式,任何对P2的加强都不能过了头以致于没法知足P1这个不变式,也就是说,P1是P2加强的底线。
那还有没有其余的不变式须要遵照?是否在对P2加强的过程当中已破坏了这些未知的不变式?这些高难度的问题牵扯到Paxos算法正确性,要看MIT的严格的数学证实,已超出了本文。
另外,中文Wiki对P2的描述是:“P2:一旦一个 value 被批准(chosen),那么以后批准(chosen)的 value 必须和这个 value 同样。”,原文采用higher-numbered更能描述将来对proposal进行编号这个事实,而中文采用“以后”,已经彻底失去这个意义。
咱们暂时按下P1不表,近距离观察一下P2,为了保证每次选出一个value,P2规定在一个Value已经被选出的状况下,若是还有其余的proposer提交value,那以后批准的value应该跟前一个一致,就是在事实上已经选定一个value时,以后的proposer不能提交不一样的value把以前的结果打乱。这是一个泛泛的描述,但若是这个描述能获得实现,paxos算法就能获得保证,所以P2也称"safety property"。
接下来的讨论都时基于“If a proposal with value v is chosen”,如何保证“then every higher-numbered proposal that is chosen has value v”,具体怎么作到“a proposal with value v is chosen"暂且不谈。
P2更可能是从思想层面上提出来该如何解决这个问题,但具体的落实工做须要不少细化的步骤,Lamport是经过逐步加强条件的方式进行落实P2,主要从下面几个方面进行:
Lamport为何能把过程划分的如此清楚已经不得而知,但从Lamport发表的文章来看,他对分布式有很深的造诣,也持续了很长的时间,能有如此的结果,与他对分布式的基础与背后的巨大努力有很大关系。但对咱们而言,不知过程只知个结果,总感受知其然不知其因此然。
咱们沿着上面的思路继续:
P2a:If a proposal with value v is chosen, then every higher-numbered proposal accepted by any acceptor has value v.
这个条件是在限制acceptor,很显然,若是P2a获得了知足,知足P2是确定的,但P2a的加强破坏了P1不变式的底线,具体参考原文,因此P2a自己没啥意义,转而从proposer端进行加强。
P2b:If a proposal with value v is chosen, then every higher-numbered proposal issued by any proposer has value v.
这个条件是在限制proposer,若是能限制住proposer,对acceptor的限制固然能被知足的。同时,由于限制proposer必须提交value v,也就顺便保证了P1(第一个确定是value v)
但P2b是难以实现的,难实现的缘由是多个proposer能够提交任意value的proposal,没法限制proposer不能提交某个value,所以须要寻找P2b的等价条件:
P2c:For any v and n, if a proposal with value v and number n is issued, then there is a set S consisting of a majority of acceptors such that either (a) no acceptor in S has accepted any proposal numbered less than n, or (b) v is the value of the highest-numbered proposal among all proposals numbered less than n accepted by the acceptors in S.
根据原文,P2c里面蕴含了P2b,但由P2c推导P2b是最难理解的部分。
首先要清楚P2c要作什么,由于P2b很难直接实现,P2c要作的就是解决P2b的问题,就是解决“若是value v被选择了,更高编号的提案已经具备value v”,也就是说:
就是要证实若是C成立,那么结果R成立,而原文的表达是“若是R成立,那么存在一个条件R”,容易让人搞混因果关系,再次感叹若是使用数学符号表达这样的歧义确定会减小不少。
P2c解决问题的思路是:不是直接尝试去知足P2b,而是寻找能知足P2b的一个充分条件,若是能知足这个充分条件,那P2b的知足是显然的。还要强调一点的是proposer能够提交任意的value,你怎么能限制我提交的必须是value v呢?其实原文中的“For any v and n, if a proposal with value v and number n is issued”是指“若是一个编号为n的proposal提交value v,而且value v能被acceptor所接受”,要想被接受就不能随便提交一个value,就必须是一个受限制的value,这里讨论的前提是value v是要被接受的。而后咱们再看下,是否知足了条件C,结果R就成立。
(a) no acceptor in S has accepted any proposal numbered less than n
若是这个条件成立,那么n是S中第一个proposal,根据P1,必须接受,因此结果R成立
(b) v is the value of the highest-numbered proposal among all proposals numbered less than n accepted by the acceptors in S
这个证实先假设编号为n的proposal具备value X被选择,确定存在一个集合C,其中的每一个acceptor都接受了value X,而集合S中的每一个Acceptor都接受了value v,由于S、C都是多数派,因此存在一个公共成员u,既接受了X,又接受了v,为了保证选择的惟一性,必须X=v.
你们可能会发觉该证实有点不太严格,“小于n的最大编号”与n之间还有不少proposal,那些proposal也有一些value,那些value会不会不是v?
这个就会用到原文中的数学概括法,就是任意的编号m的proposal具备了value v,那么n=m+1是,根据上面也是具备value v的,那么向后递推,任意的n >m都具备value v。中文wiki中的那个概括证实不须要对m...n-1正推,而对n反证,经过数学概括正推彻底能够得出最终结果。
也就是说,P2c是P2b的一个增强,知足P2c就能知足P2b。
咱们再近距离观察下P2c,发现只要在proposer提交提案前,咨询一下acceptor,看他们的最高编号是啥,他们是否选择了某个value v,再根据acceptor的回答进行选择新的编号、value提交,就能够知足P2c。经过编号,能够把(a)和(b)两个条件统一在一块儿。
其实P2c要表达的思想很是简单:若是前面有value v选出了,那之后就提交这个value v;不然proposer决定提交哪一个value,具体作法就是事前咨询,事中决定,过后提交,也就是说能够经过消息传递模型实现。Lamport经过条件、集合、概括证实等形式表达该问题,而没提这样作的目的,会致使理解很困难。你们可能会比较疑惑,难道自始至终只能选出一个value?其实这里的选出,是指一次选举,而不是整个选举周期,能够屡次运行paxos,每次都只选出一个value。
知足P2c从侧面也反映出要想提交一个正确的value v,要对proposer、acceptor同时进行限制,仅限制一方问题是没法解决的。
再回顾下条件之间的递推关系P2c=>P2b=>P2a=>P2,就是说P2c最终保证了P2,也就是解决了如何作到一个value v被选择以后,被选择的编号更大的proposal都具备value v,P2c不只保证P2的结果,更提出了“如何选”的问题,就是上面分阶段进行,这就填补了P1与P2之间缺乏如何选的断层,还有P1的2个不完备问题从直观上感受会获得解决,具体的要看算法过程章节。
P1的不完备问题:
P2c也顺便解决了P1的不完备问题,由于proposer提交的value是受acceptor限制的,就不会在一次选举中提交两个不一样的value,即便能提交也会由于proposal编号问题有一个会被拒绝,从而能保证能造成多数派。
另外一个关于第二个该怎么选的不完备问题,也是显然的了。
再次证实了,P2里面蕴含了P1,P1只是未知问题P2的不变式。