分布式之一致性协议Paxos

 

Paxos

比较难理解,https://www.zhihu.com/question/19787937这篇文章从头推导出了协议,推荐阅读。在这里我简单的拾一下牙慧。

首先分两种角色,提议者和接受者(可以为同一个节点),这和2pc、3pc一样。Paxos中变量v达成一致的条件: N个进程中大多数(超过一半) 进程都认为v是同一个值

第二、一致性要求的就是某一时刻各个节点达成一致,有两个要求:接受者们对某个值达成一致后,不能再对另一个值达成一致,成为安全性;最后总会决定为某一个值,不会无限期等待,称为活性

第三、大多数进程组成的集合为法定集合,基于的数学原理为两个法定集合一定有交集。

现在开始推导:

有一个v要被多个进程修改

1.有三个进程p1、p2、p3,p1要修改v为a,p2要修改v为b,他们修改完了自己的v,再发送消息修改p3的值,由安全性可知p3需要决定拒绝掉一个修改请求,可以遵循先来后到,让p3接受先到的修改请求;由此引出一个问题,假如p1、p2、p3都要修改v的值,由于三个进程都要接受先到的修改请求(自己给自己发消息),那么永远无法做出一致性决定,违反了活性。由此可以推出:进程能多次修改v的值,并且每个进程都没有理由拒绝自己收到的第一个请求

 

分布式之一致性协议Paxos_第1张图片

2.先来后到不能满足我们的需求,所以现在需要换一个策略来决定拒绝哪一个消息,这里引入ID来描述消息的大小,让每个进程接受ID大的消息。来看上图左边的情况,假设p1.id=2,p2.id=1,p3接受消息的状况有两种:p1的消息比p2的先到达p3。此时,p3.v=a且p3记录下接受到的id为2,然后p2的消息到来,p3发现p2.id比记录下的id小,所以拒绝掉p2,保持了一致性p2的消息比p1先到达p3。此时,p3.v=b且p3记录下接受到的id为1,然后p1的消息到来,p3发现p1.id比记录的id大,无法拒绝p1,此时无法拒绝了,一致性不能保证

分布式之一致性协议Paxos_第2张图片

3.定义步骤2中的消息id为提案id(proposal_id),v为提案值。不符合的部分已经缩小了(橙色部分)。针对橙色部分有三种解决办法:

1)p3拒绝p2,要达到这个目的,需要p1在p1和p2发送之前先发送一个自己的提案id给p3,更新p3的最大提案id

2)p3拒绝p1,在当前条件下无法做到。

3)限制p1的值与p2的值一致,需要p1主动获取其他提交者的提案值,更新自己的v与p2一致

分析第一个解决方法

p3拒绝p2引入了新流程,p1广播发送预提案,预提案值(pre.proposal_id)为接下来提案环节的提案id,接受者更新自己的最大提案id(max.proposal.id)。两种情况:

a. p1预提案先到,p3更新max.proposal.id为p1的提案id,p2后到发现比max.proposal.id小,拒绝p2,满足一致性。

b. p2预提案先到,p3更新max.proposal.id为p2的提案id,p1后到发现比max.proposal.id大,无法拒绝p1的预提案,跟之前的2步骤环节类似。

分布式之一致性协议Paxos_第3张图片

可以看出预提案与提案环节情况非常类似,但是进一步缩小了不符合一致性的范围,针对b情况只能通过 3)来限制v的值来实现,令p1.v=p2.v

扩展出来:

n个进程,Pi进程要修改v为a,Pj进程要修改v为b,只有超过半数的接受者同意了Pi的提议才算Pi的提议通过,这个超过半数的接受者我们之前提到了叫做法定集合-记作Qi。同理,Pj的法定集合记作Qj。两个超过半数的集合必定有交集,假设交集只有一个,记为K,那么只要这个交集K做出的决定不变,那么两个法定集合通过的提议就是一致的,这个K就是前文的p3。

so 整理目前的推理如下

预提案:

提议者:发送预提案广播,广播内包含自己接下来要发送的提案id(proposal_id)

接受者:接收广播后更新自己的最大提案id(max.proposal.id)

提案:

提议者:广播自己的提议,提案id(proposal_id)为之前预提案时发送的id

接受者:收到提议后,如果proposal.id >=max.proposal.id,则接受这个提议,并更新自己的max.proposal.id,否则拒绝。

分布式之一致性协议Paxos_第4张图片

 

目前我们先在提案阶段用提案id比大小的策略进行了处理,将拒绝不了的情况(id大的后到)前移到预提案阶段来解决,遇到了和提案阶段一样的情况(id大的后到),不能无限的往前移了,现在是时候让p1.v=p2.v了(Pi.v=Pj.v)。

重申一下现在剩下的问题:Pj预提案先到,K更新max.proposal.id为Pj的提案id,Pi后到发现比max.proposal.id大,无法拒绝Pi的预提案

解决的方法:限制Pi的值,令Pi.v=Pj.v

带来的新问题:怎么获得Pj.v

在提案阶段只要接受者记录下自己同意的v值,在预提案阶段收到预提案广播后将自己同意的v值返回回去。在这里我们重新捋一遍推导过程,看看Pi能否获得Pj.v

提案阶段:   

Pi.id=2  Pi.v=a  Pj.id=1  Pj.v=b。Pi先到达Pk,Pk根据id大小可以拒绝Pj-可以一致。

Pj先到达Pk,Pk根据id大小不能拒绝Pi-不能保持一致。----------->针对这一情况,增加预提案

预提案:

Pi.pre.proposal_id=2  Pj.pre.proposal_id=1。Pi先到达Pk,Pk根据id大小可以拒绝Pj-可以一致。

Pj先到达Pk,Pk根据id大小不能拒绝Pi-不能保持一致。----------->针对这一情况,增加限制Pi.v=Pj.v。

所以在这里,Pj.v已经记录在了Pk,Pi的预提案可以获得Pj.v(Pi不一定能连接到Pk,但是Pk一定是法定集合Qj的一员,Pi的法定集合Qi和Qj一定有交集,所以Pi可以获得Pj.v

但是在此时,其余的接受者可能接受了其他的提案值v,那么哪一个v才是Pj.v?

假设提议者Pm.v被某一个接受者接受,且Pm.v != Pj.v。Pm要想提出提案,首先通过了预提案:

Pm.pre.proposal_id(Pm的预提案)>Pj.proposal_id时:

1)当Pj的提案先于Pm的预提案时,由于接受者已经更新自己的max.proposal.id为Pj.proposal_id,当Pm的预提案到达时,由于Pm.pre.proposal_id>max.proposal.id(Pj.proposal_id,Pm需要将自己的v设置成和Pj.v一致,不符合Pm.v != Pj.v

2)当Pm提案先于Pj的提案,由于接受者已经更新自己的max.proposal.id为Pm.proposal_id,当Pj的提案到达时,由于Pj.proposal_idPm.pre.proposal_id),Pj被拒绝,这与Pj.v被接受不符

当Pm.pre.proposal_id(Pm的预提案)

1)当Pj的提案先于Pm的预提案时,由于接受者已经更新自己的max.proposal.id为Pj.proposal_id,当Pm的预提案到达时,由于Pm.pre.proposal_idPm提出提案不符

2)当Pm的预提案不但先于Pj的提案,而且也先于Pj的预提案时,才有可能有某一个接受者接受了Pm.v,且Pm.v != Pj.v。如下图

分布式之一致性协议Paxos_第5张图片

综上,只有在Pm.proposal_id < Pj.proposal_id时,才有可能Pm.v  != Pj.v。当Pm.proposal_id > Pj.proposal_id时,Pm.v都等于Pj.v。那么只要获取法定集合返回的提案集合中proposal_id最大的提案,该提案的v就是Pj.v。为了节省网络开销,只要每一个接受者返回自己接受的proposal_id最大的提案,提议者就能选出Pj.v。

总结paxos流程

阶段一(预提案):

提议者:广播自己的预提案,预提案id(pre.proposal_id)和接下来提案id(proposal.id)相同。

接受者:收到预提案消息后,如果pre.proposal_id>自己的max.proposal.id,更新自身的max.proposal.id=pre.proposal_id,并返回自己记录的proposal_id最大的提案。

阶段二(提案):

提议者:收到超过半数的预提案回复后,选出proposal.id最大的提案,将该提案的v设置成自己的v,如果预提案返回的提案集合为空,v可以设置任意值,然后广播自己的提案(提案id和之前的预提案id一致)。

接受者:收到提案广播后,如果proposal.id>=自己的max.proposal.id,接受该提案并更新自身的max.proposal.id=proposal_id,记录该提案(包括提案id和提案值v)。

以上推理都是基于https://www.zhihu.com/question/19787937这篇文章,感谢大神。

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