您的位置:首页 > 其它

paxos-分布式系统数据一致性算法学习

2017-11-15 23:26 148 查看


Paxos算法是莱斯利·兰伯特(Leslie Lamport,此人现在在微软研究院)于1990年提出的一种基于消息传递的一致性算法。这个算法被认为是类似算法中最有效的。文章中很多东西借鉴大神的博客,在最下面有链接。该文章只是自己加深印象,做个总结。

一:基本语义

在Paxos算法中有一下几种角色:

Proposer:议案的提议者

Acceptor:议案的决议者

Client:发出议案者

Learner:最终议案的学习者

以上四种角色,议案的提议者和决策者最重要,Proposer通俗一点就是Client的使者,Client提出一个议案,然后交于Proposer,Proposer再拿着这个议案去向Acceptor申请。还有一个名词就是最终决议,稍后再介绍。

还有一下几种关键字:

Proposal:议案,由proposer提出,最终被acceptor批准或者否决

Value:决议,他是议案的内容,一个议案就是一个{编号,决议}对

Accept:批准,表示议案被Acceptor批准

Choose:选择,表示议案被选择,也就是被多数Acceptor批准

算法一致性的基本语义:

1):决议(value)只有在被Proposer提出之后才能被批准成为议案(议案是Proposal)

2):再一次Poaxs算法执行示例中,有且仅有一个value被choosen

3):Learner只能学习最终被批准的value

以上三个语义可以转化为四个约束条件:

P1:一个Acceptor必须接受(accpet)第一次收到的议案

        但是该需求可能会导致一个问题,如果多个Proposer分别提出几个不同的提议,从而导致每个Acceptor 分 别批注了几个不同的提议,但是没有一个提议被多数派接受。即使只有两个决议,但是有可能有这种情 况,就是在单个Acceptor失效的情况下,每个议案都被半数的Acceptor接受,那么还是没有办法提出最终的议案。

        一个决议要经过多数派的批准猜中最终被choose,这个需求和P1暗示了我们Acceptor必须能够批准多个议案,因此我们为每个不同的议案分配不同的编号(如果只有三个Proposer,我们可以给他们编号0,1,2,那么他们的议案编号就可以是3*i+j,其中i可以用来跟踪提出议案的次数,j是他们开始的编号,这样就可以做到唯一递增,同理,多个也是),因此每一个议案由编号和决议构成,也就是【议案={编号,决议}】,如果一个议案被多数派Accept,那么他就被choose,我们允许选择多个议案,但是必须保证所有选择的议案都包括相同的决议,归纳如下:

P2. 如果一个议案{n, v}被选择,那么所有被选择的议案(编号更高)包含的决议都是v。 

因为编号是全序的,P2保证了“有且仅有一个决议被选择”这一关键属性,议案必须被至少一个Acceptor批准才能被选择,因此只要满足一下条件,就能满足P2:

P2A:一旦一个编号n,value v({n,v})的议案被choose, 那么之后任何Acceptor再次接受的议案(编号更高)必须具有value v

依然根据P1来确认选择了某些议案。因为通信是异步的,所以可能在有些情况下,某些Acceptor可能没有接受过一些议案,他们可能会错误的批准一个议案,试想一下,加入某个Proposer以内某些原因down了,但是后续自己又启动了,他提出了一个编号更高的议案,那么根据P21,Acceptor应该去批准这个议案,但是又违背了P2A,所以我们要去加强P2A:

P2B:一旦一个编号n,value v({n,v})的议案被choose,那么之后任何Proposer提出的议案(编号更高)必须具有value v

因为一个议案只有在被Proposer提出之后才有可能被批准,因此P2B包含了P2A,进而包含了P2

但是如何才能证明P2B,假设某个议案{m,v}被选择,然后我们可以去证明任何n>m的议案的决议都是v,对n可以简化证明:根据条件,每个提出的议案(编号m到n-1)他的决议都是v,我们可以证明编号为n的决议是v,对于选择的议案m,必然存在一个集合c(Acceptor的多数派)中的多数Acceptor已经批准了该议案,综合假设归纳,m被选择这一前提意味着:

C中的每个Acceptor都批准了一个一个编号m到n-1范围内的议案,并且议案的决议都是v

因为任何多数派组成的集合都包括至少C中的一员,那么我们可以得出结论,如果下面的不变性成立,那么议案n的决议就是v

P2C:对于任意的v和m,如果议案{n,v}被提出,那么存在一个有多数派构成的结合s(或者a,s中没有没有acceptor批准过小于编号n的议案,或者b)在s中的acceptor批准的所以议案(编号小于n)中,v编号小于n的提案的最大决议

通过保持P2C,我们就能满足P2B,为了保持P2C,准备提出提案(编号为n)的Proposer必须知道所有编号小于n的议案中编号最大的那个,如果存在的话,他可能已经或者将要被Acceptor多数派批准,获取已经批准的议案是简单的,但是预测将要批准的议案确实困难的,Proposer并不预测,它假设不会有这种情况,因此他会硬性规定Acceptor不许批准任何编号小于n的议案,这就引起了下面的基本算法也就是我们说的两阶段提交。

1):Proposer选择了一个新的编号n,准备向Acceptor集合中所有成员发送请求(Prepare阶段,n是Prepare请求的编号,也是下面accept回应议案的编号),并且要求回应:

    a、一个永不批准编号小于n的议案的承诺

    b、在他已经批准的编号小于n的议案中,编号最大的议案,如果存在的话,我们把这样的请求叫做prepare请求n

2):如果Proposer收到多数Acceptor的回应,那么他就可以提出议案{n,v}其中v是所有回应中编号最高的议案的决议,或者是Proposer选择的任意值,前提是Acceptor从未批准过任何议案

一个Proposer发出一个Acceptor集合发送已经被批准的议案,我们称之为Accept请求

换言之:就是

P1A:Acceptor可以批准一个编号为n的议案,当且仅当他没有回应过一个编号大于n的Prepare请求

P1A包含了P1,现在我们得到了一个一致性算法,假设满足安全性,假设议案的编号唯一,我们就可以通过简单的优化,得到下面一致性算法:

假设一个Acceptor接收到一个编号为n的Prepare请求,但是他已经回应了一个编号大于n的Prepare请求,于是Acceptor就没有必要回应这个Prepare请求了,因为他不会这个批准这个编号为n的议案,他还可以忽略已经批准过的议案的Prepare请求。

有了这些优化,Acceptor只需要保存它已经批准过的最高编号的议案(议案的编号和决议),以及他已经回应过得所有Prepare请求的最高编号,因为任何情况下,都需要保证P2C,Acceptor都需要保存这些信息,包括重启之后,注意,Proposer可以随意抛弃一个提案,但是他不会用相同的编号来提出另一个议案。


二:基本算法

其中通信模型我们采用异步的非拜占庭模型【拜占庭模型下,消息会丢失,重复,也有可能会损坏,换言之:我们认为消息可能会丢失,也可能会重复,但是消息不会出现损坏的现象】

下面简单介绍下Paxos算法的一些行为,基本分为两段提交过程:

1)Prepare阶段:

        (1):当Proposer希望提出一个议案V1,首先发送Prepare请求到大多数的Acceptor,Prepare请求的序列号为<SN1>;

         (2):当Acceptor接收到编号为<SN1>的Prepare请求的时候,他首先回去检查自身上次回复过得Prepare请求<SN2>

                a):如果SN2>SN1,忽略该请求,并且告知提出<SN1>议案的Proposer,他已经回复过一个编号大于SN1的Prepare请求,

                b):否则去检查上次批准的Prepare请求<SNx,Vx>,并且回复<SNx,Vx>;如果该Acceptor之前没有回复过,那么直接回复<OK>

2)Accept阶段 :

         (1):   因为通讯是异步的,所以我们可以在经过一段时间后,Proposer收到Acceptor的请求,回复可以分为几种:

            a):回复的数量满足多数派,而且回复的都是<OK>,那么Proposer发起Accept请求,内容就是<SN1,V1>

            b):回复的数量满足多数派,但是有的回复<SN2,V2>,<SN3,V3>.....则,Proposer找到所有回复中超过半数的那个,假如是<SNx,Vx>,则发出Accept请求,内容为<SN1,Vx>,此时他的编号不变,但是议案的决议变成Acceptor回复中超过半数议案的决议Vx

            c):回复的数量不满足多数派,那么Proposer会尝试编号+1再次发出Prepare请求,

         (2):在不违背自己向其他Proposer的承诺前提下,acceptor收到accpet请求后既接受并回复这个请求。


三:算法优化

但是在应用场景下,很容易出现活锁的情况,例如当三个或三个以上的Proposer在发送Prepare请求,很难有一个Proposer收到超过半数的回复而不停的执行第一阶段协议,这就陷入了一个怪圈,因此为了避免竞争,加快收敛速度,在算法中引入了一个Leader的角色,在正常情况下有且仅有一个参与者扮演leader角色,除非leader down掉或者是他丢掉了半数之上的Acceptor,才会重新选举leader。而且他角色扮演Acceptor,同时又扮演Learner角色。

在这种优化算法中,只有Leader才能提出议案,从而避免了竞争使得算法快速收敛而趋于一致,此时的Paxos算法在本质上退变为两阶段提交协议。但是在异常情况下,可能出现了多个leader,此时算法有变成了原始的Paxos算法。

Leader的选举也是用到了两阶段提交协议。

此时Leader的工作流程主要分为以下三部分:

1、学习阶段 向其他参与者学习自己不知道的数据(决议)

当一个参与者经过选举后成为了leader,他应该知道绝大多数的Paxos的实例,因此他会马上启动一个主动学习的过程。假设当前的新Leader早就知道1-134,138和139的Paxos实例,那么他就会执行137-137以及大于139的paxos。如果只检测到135和140的Poxas实例有确定的值,那他最后就会知道1-135,138-140的paxos的实例。

2、同步阶段 让绝大多数的参与者保持数据(决议)的一致性

此时的Leader已经知道1-134和138-140的poxas实例,那么他会重新执行1-135的实例,以保证绝大多数参与者在1-135的paxos实例上保持一致。对于138-140的实例,他不会马上去执行,而是等到服务阶段填充了136,137的paxos实例后在执行。这里之所以要填充这些实例,是为了防止以后Leader总是去学习这些间隔的实例,但是这些实例有没有确定的值,就会造成一定程度的资源浪费。

3、服务阶段 为客户端服务,提议案

 Leader将用户的请求转化为Paxos实例,当然,他可以去执行多个Paxos实例,但是如果当前leader出现异常,就有可能出现Paxos实例间断的问题,或者在异常情况下,出现了多个leader,此时Paxos算法就有可能出现活锁的现象。

链接:http://blog.csdn.net/sparkliang/article/details/5740882

链接:http://blog.csdn.net/xhh198781/article/details/10949697

链接:http://shuofenglxy.iteye.com/blog/1188422
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: