转载请注明出处http://www.cnblogs.com/dvd0423/p/4185697.html
浮躁的人请绕道,这篇文章不适合你。
把Paxos作为分布式系统系列的第一篇,是因为我迫不及待的想告诉别人这个算法是多么牛逼。相比于Map/Reduce架构、一致性Hash、两阶段提交等等,我觉得Paxos是一个让人第一次看到会感觉废话连篇,再仔细品味会拍手称赞的算法。Paxos算法最初由lamport提出,他第一次提出时模拟的是一个叫Paxos的希腊城邦立法的问题。由于这个算法太抽象了,所以他在两篇论文中提出都没多少人在意,直到后来谷歌chubby用了此算法别人才明白这个算法的精髓。这里我们只介绍它的中心思想,具体细节有很多,可以详细看看参考文献[1,2]中lamport的论文。
Paxos算法是一个基于消息传递机制的一致性算法,不同于共享内存机制。共享内存机制相当于大家在一起开会产生最终决议,是集中式的;而消息传递方式中人与人之间不知道各自在干什么,只能通过繁琐的沟通才能一点点了解,是分布式的。有点像openMP与MPI的区别。我个人更喜欢把它看作选举算法来理解,而不是一致性算法。
问题模型
城邦立法问题(和原模型大同小异):小岛上有议长、议员、信使、法律提议者、民众这些人。法律的产生由议长主持,提议者提出方案交给各个议员,议员按多数原则投票决定最终法律条文,而议员投票不需要在一起开会,所以他们可以在任何地方投票,投完票后只能由信使传递给议长,议长根据最终决议产生法律,并告知群众。
大家看到这里可能会觉得这不是很简单的事情吗?如果上述问题中所有人都100%尽职,并步调一致,那么这确实是一件很简单都事。关键问题在于现实世界中议长可能突然死了,这时候要换议长;一部分议员度假1天回家后才投票;一些信使中途也死了,或者中途干活去了。这些导致了消息传递的不可靠,从而导致了选举结果的混乱。比如本来决定了1号提议者的方案(共有5票最多),但在对外公布之前又有两名信使刚把议员的消息带到议长那里,而他们选的都是2号提议者(本来是4票)的方案。这样最终2号提议者应该是多数,所以议长要改变最终议案。消息传递的不可靠和不同步导致了决议的不一致。
算法实现
而Paxos算法就是提出一些约束条件,使立法顺利进行,就算有意外发生也能产生最终法案的策略。它很好的解决了一致性问题。要保证一致性就要满足一些条件,总结起来就是三条看似废话的安全属性:
•只有提议者的方案能成为法律,
•最终只能选一个人的提议,并且
•只有议长公布后大家才知道最终法案的内容。
显然,只要满足上面的条件的算法就都能保证投票过程不出问题。好,现在Paxos算法正式开始:
如果仅有一个提议者提出了一个决议,我们也希望能选择一个决议。所以下面条件需要成立:
P1一个议员必须无条件同意他收到的第一个决议。
在满足P1的情况下如果每个议员接收到的第一个决议都不同,则没有决议被多数派批准,所以为了避免这种情况发生一个议员就必须可以批准多个议案。每个提议者可以多次提议,而每次提议形成不同但议案,每个议案可以由唯一的编号决定,在这里我们的议案设置为{编号,内容}结构,用数字表示为{n, v}。我们允许选择多个议案,但被选为最终法律的所有议案的内容v都相同。也就是:
P2 如果一个议案{n, v}被最终选取,那么所有后来被选择的编号>n的议案内容也都是v。
因为编号是顺序的,所以P2保证了第二条安全属性。议案至少被一个议员批准才可能被最终选取。因此P2等价于下面推论:
P2a 如果最终被选择的议案是{n, v},那么任何议员批准的编号>n的议案内容都是v。
首先我们通过P1保证了某些议案被选择。由于通信的异步性,在一个议员C没有收到过任何议案的情况下,可能已经产生了最终决议。而这时候如果一个新的提议者提出了内容不同的、有更高编号的全新议案{N, V},则根据P1,C议员必须接受{N, V}。但这样就违反了P2a。为了解决这个矛盾我们要把P2a的约束加强为:
P2b如果最终被选择的议案是{n, v},那么以后任何提议者提出的的编号>n的议案内容都是v。
这样通过P2b就能推出P2a,从而推出P2。而如果最终议案满足P2b条件,则选取议案{n, v}的多数派集合S要满足一下条件:
P2c对于任意的v和n,如果议案{n, v}被提出,那么存在一个由议员的多数派组成的集合S,或者a) S中没有议员批准过编号小于n的议案,或者b) 在S的任何议员批准的所有议案(编号小于n)中,v是编号最大的议案的决议内容。
通过保持P2c,就能保持P2b。为了保证P2c,准备提出议案{n, v}的提议者必须知道所有编号小于n的议案中已经或者将要被多数派议员批准的编号最大的那个(如果有的话)。提议者获知已经被批准的议案很简单,但是预知未来将要被批准的议案很难。所以这个提议者要求议员不能批准任何编号比n小的议案。这就引出了以下提议者算法,这就是一个两阶段提交算法:
1.提议者选择一个新编号n,向某个议员集合中的所有成员发送请求,并要求回应:
a)一个绝不批准编号小于n的议案的承诺,
b)编号小于n的议案中他已经批准的编号最大的议案(如果存在的话)。否则回应无。
这样的请求称为prepare请求,没有内容,所以不是议案!这是第一阶段。
2.如果提议者A收到议员的回应,那么就可以发送提交一个议案{n, vA}的请求,其中vA要么是1中b)的议案内容vb,(vb内容为NULL时)要么就是提议者自己的新的议案内容vA,。这个请求为accept请求,此为第二阶段。
上面描述了提议者的操作,那么议员要满足什么要求呢?首先议员可以随便忽略任何请求,这样议员可以弃权不投票,但是不能乱投票,下面说明议员回应请求的操作要求:
议员可以批准一个编号为n的议案,当且仅当它没有回应过一个编号大于n的prepare请求。
即议员如果回应了一个编号为n+1的请求,就不会再回应编号为n的请求。议员只需要保存它已经批准的最高编号的议案{n, v},以及它已经回应的所有prepare请求的最高编号N。
至此Paxos决策过程完成。现在让我们结合提议者和议员的行为将算法分为两个阶段
阶段1:
a) 提议者选择一个议案编号n,向议员的多数派发送编号为n的prepare请求。
b) 议员:如果接收到的prepare请求的编号n大于它已经回应的任何prepare请求,它就回应已经批准的编号最高的议案{m, k},并承诺不再回应任何编号小于n的议案.
阶段2:
a) 提议者:如果收到了多数议员对prepare请求(编号为n)的回应,它就向这些议员发送议案{n, v}的accept请求,其中v是所有回应中编号最高的议案的决议内容即阶段1的k,如果回应说还没有议案,则v是提议者自己的议案内容。
b) 议员:如果收到了议案{n, v}的accept请求,它就批准该议案,除非它已经回应了一个编号大于n的议案。
公布最终法律
至此就可以保证最终法律的产生。但是人民群众还不知道是不是已经产生了最终法律。群众必须找到一个被多数派议员批准的议案,才能知道最终法律产生了。
一个显而易见的算法就是,让每个议员在批准议案时通知所有的群众。于是群众可以尽快知道选择的决议,但是要求每个议员通知每个群众——需要的消息个数等于群众数和议员数的乘积,这样太浪费信使。所以可以只告诉议长,然后由议长公布。但是议长也可能突然死了,所以要有多个议长同时主持。
第二种方法如果群众需要知道是否已经选择了一个决议,他可以让提议者根据上面的算法提出一个议案或者自己提议,prepare请求有回应,并且回应一个议案{m, k}。则说明最终法律已经产生。
总结
太耗脑细胞,写完全文我又饿了。出去吃夜宵喽。
by 糖球
参考文献
[1] The part-time parliament, Lamport, ACM Transactions on Computer Systems 16(2):133-169, 1998
[2] Paxos made simple, Lamport, SIGACT News 32(4):18-25, 2001.
[3] paxos和分布式系统. 百度基础架构部