分布式一致性算法-paxos介绍

概述

Paxos算法是Lamport创造基于消息传递的一致性算法,包括Google的Chubby在内很多系统都应用了Paxos算法,Google Chubby[1]有下面的描述:

all working protocols for asynchronous consensus we have so far encountered have Paxos at their core.

足见该算法在分布式系统中的地位。

 

背景说明

Paxos解决的问题是,在分布式系统中系统间如何就一个不可变变量达成一致,仅此而已!但是在该算法的基础之上我们可以做非常有意义的事情,比如Google Chubby就是对Multi-Paxos算法的工程实现,Multi-Paxos

算法的基础就是Paxos,通俗来说,就是多个轮次的Paxos算法的执行,确定一系列不可能变量的值,如果各个节点初始状态一致,再执行相同的操作序列(即确定的一系列不可变变量的值),那么最终结果必然也是一致的。这是可以应用于系统容错和系统一致性上的。

 

条件约束

在分布式系统一致性领域有一个号称定理的结果 FLP Impossibility[2][3],  在异步通信环境下,没有一个算法可以保证数据一致性。我们能够做的就是尽可能的在Liveness和Safety之前找到一个平衡,这和CAP很相像。

Paxos做了如下保证:

Liveness:

只要存在多数派,并且多数派之间网络是联通的,则:

  1. 肯定会有提议被接受
  2. 被接受的提议肯定可以被其他进程学习到

 

Safety:

    Do not be eval ! 保证不做错的事情

  1. 只有一个值被确定或者批准,不能出现第二个值把第一个值覆盖的情况

也是两个约束条件是Paxos的算法根基。

 

推导过程

举个栗子: 假设,三个进程 Pi、Pj和Pk 想就变量V达成一致。

最直接的想法:

P1:  “进程集合中的任一进程向其他所有进程提议V的值,当提议被进程接收后,进程会拒绝再次对V的提议”  


这个想法是满足Safety要求的。但是问题也很明显,首先这个想法不允许任何进程故障,这和Liveness要求不符合!其次如果是多个进程并发对V进行赋值,无法支持。

 

优化刚刚的想法P1, 首先我们提出一个概念:法定集合(即进程集合中的多数派),将原来的想法P1升级为

P2: “进程集合中的任一进程向其他进程提议V的值,当提议被法定集合采纳,即可认为提议已经确定;当提议被进程接收后,进程会拒绝再次对V的提议”

 

其次,对于多个进程并发对V进程赋值的场景P2 依然无法满足,需要将 “当提议被进程接收后,进程会拒绝再次对V的提议” 这个限制去掉

 

P3: “进程集合中的任一进程向其他进程提议V的值,当提议被法定集合采纳,即可认为提议已经确定;当提议被进程接收后,进程仍然可以接收对V的提议”

如果只是允许进程可以接收多次对V的提议,系统一致性同样也无法满足,比如Pi.v被赋值为a, 而且Pj.v 也采纳了a,但是同时Pk.v 被赋值为b,其实正常来说v=a 已经

形成了多数派,按照P3的约定,这个提议可以被接受了,是不能再修改的,但是由于“当提议被进程接收后,进程仍然可以接收对V的提议”,这个限制导致Safety无法满足,

说明P3存在局限性!

 

正常来说上面的例子中v=a已经被法定集合确认,它本身已经是确定性取值,Pk已经没有任何选择了,它只能提议v=a。

因此,我们调整一下策略, Pk在提议前先去向其他进程询问一下其他人的提议值,如果所有进程v的值都为null, 那就提议自己的v=b, 如果不为空就提议某个值出现次数最多的

那个值,回到刚刚的例子, pk向pi和pj询问到了v=a,此时pk也提议v=a,此时所有进程对v=a达成了一致。但是很不幸,恰巧每个值出现的次数相等怎么办?举例来说就是,

Pi提议v=a, Pj提议v=b, 此时Pk向Pi和Pj询问的结果是[v=a,1] [v=b,1] ,此时Pk懵逼了,它无法判断该如何提议了。

看来,单纯依靠v的值作为询问依据是不行的, 那不妨给每个v都加一个基友epoch吧, 我们姑且认为epoch是v的一个身份,他是一个单调递增的数字,我们将v和epoch统称为提案,

 

至此其实已经初步具备了Paxos的算法模型,即Paxos算法在正式提案前会有一个询问的过程,其实也是一个两阶段的算法。同时我们也将进程内的角色细化一下,每个进程分为两种角色:

Proposer 和 Acceptor。 Processor 负责询问Acceptor选择一个合理的提案,并将该提案提交给Acceptor; Acceptor负责接收和处理Processor的询问和提案。

 

还是刚刚的例子:如果Processor 询问的结果为null,则Processor 将自主的给v 赋值, 如果不为null,则Processor需要选择一个v作为提案提交。 试想如果两个Processor同时询问且结果都为null,则都会

向各自的法定集合提交提案,两个法定集合必然存在至少一个common Acceptor,此时common Acceptor如何选择这两个Processor 的提案呢?

可以应用提案的epoch属性来解决这个问题,我们可以设定一个规则:common Acceptor 如果同时接收到多个提案,只会接收epoch最大的提案,拒绝掉其他提案。 我们假设acceptor使用a.epoch 来表示

接收的epoch值,如果一个提案PA的epoch大于a.epoch,Acceptor则接收这个提案PA并更新自己的a.epoch=PA.epoch ,反之拒绝掉这次提案.

 

假定Pk接收到了Pi和Pj的两个提案,且Pi.epoch > Pj.epoch, 此时依然存在两种情形需要分别考虑:

情形一: 如果Pk先收到了Pi的提案, 当接收到Pj的提案时,直接拒绝

情形二: 如果Pk先收到了Pj的提案,此时Pk.epoch = Pj.epoch,当接收到Pi的提案时,发现Pi.epoch > pk.epoch,仍然要接收,此时显然和Safety冲突,因为当Pk接收到了Pj的提案时,多数派已经形成,即不可变

变量已经确定,此时不应该再接收Pi的提案!

其实我们期望的是,无论是Pk先接收到谁的提案, 结果是一致的,即:接收到epoch最大的提案,拒绝掉其他提案。

那么我们能不能在询问阶段Pi顺便告诉Pk,自己在提交阶段的Epoch呢?

在询问阶段,各个Processor向Acceptor告知提交的epoch, Acceptor只接收最大的Epoch并记录下来,这样在提交阶段,如果Processor的epoch小于自己的epoch则拒绝这次提案

回过头继续看刚刚的情形二, 虽然Pk先收到了Pj的提案,但是由于Pk此时的a.epoch = Pi.epoch,因为在询问阶段Pk就已经将最大的epoch记录,此时由于Pk的a.epoch > Pj.epoch,仍然会拒绝掉Pj


继续看刚刚导致Pk懵逼的那个例子,Pk在提案前向Pi和Pj询问v的取值,Pi反馈是v=a, Pj反馈是v=b, 因为当时没有epoch的概念,导致Pk无法选择,此时我们已经有了epoch的支持,Pk如何选择v的取值呢?

先说结论:选择epoch最大的v的取值

如何证明epoch大的v就是目标值呢?[5][6]

 

综合以上我们总结一下Paxos执行过程:

询问阶段(Propose阶段)

 

Proposer 发送 Propose

       Proposer 生成全局唯一且递增的Proposal ID,向集群的所有机器发送 Propose,这里无

       需携带提案内容,只携带Proposal ID即可 

Acceptor 应答 Propose
       Acceptor 收到Propose后,做出“两个承诺,一个应答

       两个承诺
          第一,不再应答 Proposal ID 小于等于(注意:这里是 <= )当前请求的 Propose 第二,不再应答 Proposal ID 小于(注意:这里是 )当前请求的 Accept请求

        一个应答
          返回已经 Accept 过的提案中 Proposal ID 最大的那个提案的Value和accepted

          Proposal ID,没有则返回空值 

    

接收阶段(Accept 阶段)

Proposer 发送 Accept
   “提案生成规则”:Proposer 收集到多数派的Propose应答后,从应答中选择存在提案

   Value的并且同时也是Proposal ID最大的提案的Value,作为本次要发起 Accept 的提案。 如果所有应答的提案Value均为空值,则可以自己随意决定提案Value。然后携带上当前 Proposal ID,向集群的所有机器发送 Accept请      求

应答 Accept
    Acceptor 收到 Accpet 请求后,检查不违背自己之前作出的“两个承诺”情况下,持久

   化当前 Proposal ID 和提案Value。最后 Proposer 收集到多数派的Accept应答后,形成 决议 

 

范例学习

case1:

服务器S1, 收到将v命名为X的请求, v=X,被法定集合接受。 决议已经形成,决议值为X。然后P5 学习到v=X, 并接受它。

 

case2:

v=x 只被S3 接收了,它被P4 学习到了,并被法定集合接收。

 

case3:

P 3没有被多数派Accept(只有S1 Accept),同时也没有被P 4学习到。由于P 4 Propose的所有应答,均未返回Value,则P 4.5可以Accept自己的Value(Y)

后续P 3Accept(X)会失败,进行下一轮提案,最终S1,S2的Acceptor也学习到了Value(Y) 

参考

[1] The Chubby lock service for loosely-coupled distributed systems (PDF)

[2] https://en.wikipedia.org/wiki/Consensus_(computer_science)

[3] http://blog.csdn.net/chen77716/article/details/27963079

[4]https://www.zhihu.com/question/19787937/answer/82340987

[5] http://research.microsoft.com/en-us/um/people/lamport/pubs/paxos-simple.pdf

[6] https://www.zhihu.com/question/19787937 



你可能感兴趣的:(分布式)