糖 球

学 街 舞 的 书 呆 子

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

  转载请注明出处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票,最多),但在对外公布1号议案之前又有两名信使刚把两名议员的消息带到议长那里,而他们选的都是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的prepare请求时,才可以批准一个编号为n的议案。

  即议员如果回应了一个编号为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}。则说明最终法律已经产生。

总结

  如果想详细理解Paxos算法,我建议还是静下心来结合我说的例子看看Lamport的原论文,我的博文内容是源于论文[2]的思想加入自己的个人理解。至于它能干什么?比如几台备份节点中产生主结点;几个更新日志中选择一个最适合的等等。只要问题可以转化为分布式选举的模型,都可以用这个算法。

  太耗脑细胞,写完全文我又饿了。出去宵夜喽(^∇^)

参考文献

  [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.

 

by 糖球

 

posted on 2014-12-25 22:10  糖球  阅读(606)  评论(0编辑  收藏  举报