zoukankan      html  css  js  c++  java
  • Paxos学习笔记

    最近Distributed System学习了Paxos算法,表示并没有听懂…… 自己恶补下…… 这里是Paxos Made Simple的笔记。这篇文章里面Lamport详细的描述了为何Paxos算法会是这个逻辑。Lamport还是很幽默的,第一个版本的Paxos里 面的幽默不能被大家接受,于是Paxos Made Simple里面的第一句话就是:The Paxos algorithm, when presentd in plain English, is very simple. 我只想说:"干,我怎么没觉得。一定是我英语不好,读不懂"。

    假设

    任何系统或者算法的运行都需要满足一些条件,这里Paxos算法满足以下几个条件:

    • 假设没有拜占庭将军问题,即不会出现叛徒。这个假设可以假设是合理的,毕竟现在各种校验算法,出错了再发一遍呗
    • 所有的机器可以随时挂掉【Cool】
    • 所有的消息可以被重复传送,而且到达时间不定【Also Cool】

    角色

    每 台机器可以扮演三个角色:Proposer,Acceptor和Learner。这里是民主社会。Proposer就是题各种议案,比如,我觉得机器X牛 掰,应该当Leader这种。Acceptor来决定是不是接受这个提案。Learner就是当某个提案被通过之后,就要学会这个是神马提案。大概就这种分工。

    思路

    那么首先Proposer会提出某个提议,然后当大多数Acceptor接受之后,这个提案就通过了。我们都是严谨的人,这么说就模糊了:神马叫做大多数?这里给出的定义就是说,如果每个Acceptor同一时刻,最多只能接受一个提案的话,那么对于任意两个"大多数"集合,其中必须至少有一个Accetpor是相交的。举个例子,如果是5个Acceptor,我们就可以定义3为大多数(当然4也是),因为任意选三个出来的所有集合必定有交集。好就这样,你看不懂就怪自己中文不好吧。

    然后,我们假设,P1:每一个Acceptor都会接受收到的第一个提案。手上有个提案总比没有的好嘛。然后呢,问题来了,有很多Proposer,很多Acceptor,那么很可能不会出现一个大多数都接受的提议。这样就必须要求Acceptor可以接受多个提案。这也很合理,之前的提案坑了,总不能强迫人一直在坑里面吧,就像人不要在一棵树上吊死。为了跟踪提议,我们给提议加上编号(id),每个提议有一个谁是leader的信息(v)。不同的提议编号必须不一样,但是谁是leader可以一样(Proposer心有灵犀)。如果一个(id_k,v_k)被大多数Acceptor接受了,那么我们说这个提议通过。

    好了这里是重点,P2:我们可以让多个提议被通过,但是我们必须保证通过的提议关于谁是leader的信息是一样的。比如(id_1,v_1)和(id_2,v_2)都被通过了,那么我们必须保证:v_1 = v_2。这也很合理,如果不一样,而且两个提议都通过了,你让Learner学个毛毛。

    现在我们考虑另外一个情况,P2a:如果一个(n,v)的提议被通过了,那么任意编号>n的而且被任意Acceptor接受的提议必须有同样的v。如果P2a满足了,那么我们可以发现P2也就满足了,即P2a=>P2。为什么呢?首先如果提议要通过,就必须被大多数Acceptor接受。那么如果被任意一个Acceptor接受的提议已经满足了有同样的v,那么就满足P2的条件了。这里还需要用数学归纳法证明任意编号>n这点。自己脑补下就行。

    乍一看没啥问题,但其实坑往往就在那里,不论你发现没发现。首先根据假设,我们可以有如下的情况:某个Proposer挂了(我们假设是P),然后满血满状态复活了,比如他信了春哥,然后呢某个Acceptor很孤独(我们假设是A),没有任何Proposer和他玩,比如他住在很远的匹兹堡,而Proposer都在二次元,根本联系不上。然后由于P信春哥,所以P可以和A联系上。根据P1,我们知道,A绝对会接受P的提议(可能A比较寂寞),即使这个提议只出的leader和别人不一样,这个情况就不满足P2a了。也就是说由于P1的存在会导致P2a不成立,从而导致P2不成立。所以这里我们需要把P1也考虑进来,P2b:如果一个提议(n,v)被通过了,那么被任何一个Proposer提出的编号>n的提议都必须有同样的v。好了这里我们直接强迫上面的P提出的提议和已经接受的提议的意见一致,从而解决了这个问题。

    如何满足P2b?

    思路就是这样,如果我们有某个机制,可以在假设的情况下,满足P2b,那么我们就可以满足P2。Lamport给出的思路是,如果我们想满足某个条件,我们可以看下怎么证明这个条件可以被满足。

    假定(m,v)已经被通过了,注意通过的意思是大多数都已经接受这个提议。然后有一个新提议(n,v_n),这里n>m,然后我们需要证明v_n = v。如果要证明这一点,我们需要哪些条件呢?如果我们用数学归纳法的话,那么我们可以假设提议m..(n-1)提出的leader都是v,然后想法子证明v_n = v。

    如果(m,v)被通过了,那么必定有个可以被称为大多数Acceptor的集合C接受了这个提议,然后根据假设的条件,我们有:对于C中的任意一个Acceptor,他们必定接受了一个提议(i,v),这里i再m和n-1之间,并且值为v。同时我们发现,对于一个任意的大多数集合S(参考大多数上面的解释),必定至少有一个Acceptor是在集合C中的

    现在提出P2c,P2c:对于任意的n和v,如果有提议(n,v)那么必定有一个大多数集合S,满足1)集合S中没有任何一个Acceptor接受任何id<n的提议,或者2)v就是这个S中的所有Acceptor已经接受的提议中的最大的那个值。如果P2c被满足了那么P2b也可以被满足。在这个之前我们有

    • 提议m..(n-1)提出的leader都是v
    • 对于任意一个大多数集合S,其中必定有一个Acceptor接受的leader是v

    这样,我们需要保证n提出的这个v是S中已经被接受的最大的v。那么当一个Proposer需要提出议案的时候,他必须知道在任意一个大多数集合中,已经被或者将要被接受的最大的那个v。知道一个已经被接受的最大的v是比较容易的,预测一个将要被接受的v是难的。与其预测,不如加上一些机制:假定所有的Acceptor不会接受任何id<n的提议(我就是feature)。

    关于Proposer

    Finally,我们有了第一阶段的算法【尼玛英语真难】:

    • Proposer选择一个新的提议n,然后把n发送给所有的Acceptor,要求Acceptor做出如下响应(Prepare请求)
      • 不会再接受任何<n的提议
      • 返回已经被接受的拥有最高id的提议,如果没有接受任何提议的话返回空
    • 如果有大多数Acceptor有响应,那么Proposer会提出提议(n,v)这里v是所有接收到的提议中最大的那个v。如果所有的响应都是返回了空,那么Proposer可以自己选择一个(比如自己呀)。

    至此,Proposer终于有自己的提案了,然后可以发送提案给Acceptor,请求其接受。可见当一个Proposer也是不容易的,谁让你是民主社会。此外Proposer总是可以放弃一个提议,然后开始一个新的提议

    关于Acceptor

    对于Acceptor有两种请求,1)Prepare请求;2)Accept请求。对于Prepare请求,Acceptor总是可以做出响应。同时,对于一个Accept请求,他总是可以接受,如果他没有保证不会接受的话(提议id)。或者说,P1a:一个Acceptor可以接受一个编号为n的提议,只要他没有响应过编号>n的请求。可见P1a是包含P1的。

    这里还有个小优化,不要紧张不是凸优化:既然Acceptor不会接受编号小于自己已经响应过的提议,那么再Prepare请求阶段就可以直接忽略这个请求,因为即使响应了也不会接受。

    这样Acceptor需要记住如下两个信息:1)已经响应过的最高的提议编号;2)已经接受的最高的提议编号里面提议信息。因为P2c必须保证这两条信息的正确性,所以必须保存持久存储器里面,这样即使机器挂了,重新启动,还可以接着用。

    Finally Paxos

    • 第一阶段
      • Proposer选择一个数字n,发送prepare请求给acceptor
      • 如果Acceptor接受到一个prepare请求,并且编号n大于所有之前响应过的编号,那么他会保证不会响应和接受任何编号<n的请求,同时会发送已经接受的最大编号的proposal回去。如果没有接受过任何proposal则发送空。
    • 第二阶段
      • 如果Proposer从大多数Acceptor接受到了prepare请求的响应,那么他会写好自己的提议:编号为n,内容为接受到的响应里面的最大的值。然后发送Accept请求。如果所有的响应的值都是空的(比如初始阶段),则自己选一个值(比如自己)发送。
      • 如果Acceptor接受到了一个Accept请求,而且他没有保证不接受次请求(看编号),那么就接受。

    如果你以为这就结束了,那么我只能说too young, too simple, sometimes naive.... 虽然存在感少了点,但是……你看或不看他都在那儿……

    Learner

    对于一个节点,他需要知道那个值被选为当大哥了,毕竟跟错大哥是一个很悲剧的事情。一个朴素的算法是,对每个acceptor,只要他接受了一个请求,那么他就发告诉所有的节点。不过这也太SB了,对吧。Lamport才不会这样……

    另一个方法是,所有的acceptor都会发送请求给一个杰出learner(比如IQ高),然后这个杰出learner会告诉结果给别的learner。恩,好吧听上去也有点SB…… 毕竟这个杰出Learner可能太过于刻苦学习了,然后挂了,然后各种低智商Learner就跪了……

    最后,没法子,我们可以培养多个杰出Learner,然后Acceptor可以告知这些人,大哥是谁…… 人多了,挂掉一两个,在短时间信春哥,救活了就行了(Restart)……

    其他

    可以发现,这里的paxos可能会无限循环…… 有一些变种算法可以避免这些,不过我不太会。就这样,作业会写了。

  • 相关阅读:
    【POJ 2778】DNA Sequence
    【POJ 2923】Relocation
    codeforces 475D
    hdu4742
    hdu4741
    hdu5016
    poj3929
    Codeforces Round #267 (Div. 2)
    codeforces 455E
    hdu4073 Lights
  • 原文地址:https://www.cnblogs.com/esing/p/4455104.html
Copyright © 2011-2022 走看看