Paxos一致性算法详解

目录

 

Paxos一致性算法详解

1.问题描述

2.推导过程

3.算法流程

4.Multi-Paxos算法

5.参考文献


Paxos一致性算法详解

1.问题描述

假设有一组可以提出提案的进程集合,那么对于一个一致性算法来说需要保证以下几点:

  • 在这些被提出的提案中,只有一个会被选定。
  • 如果没有提案被提出,那么就不会有被选定的提案。
  • 当一个提案被选定后,进程应该可以获取被选定的提案信息。

而且为了满足安全一致性,有如下要求:

  • 要求只有被提出的提案才能被选定。
  • 只能有一个提案被选定。
  • 如果某个进程认为某个提案被选定了,那么这个提案必须是真的被选定的那个。

在Paxos一致性算法中,有Proposer、Acceptor和Learner三种参与角色,假设不同参与者之间可以通过收发消息来进行通信。其中Proposer提出提案,Acceptor决定哪个提案作为被选定的提案,Learner作为提案的接受者。

2.推导过程

本部分描述性文字比较多,如果对此不感兴趣,可以直接看第3部分的算法流程。

首先,我们希望即使只有一个提案被提出,仍然可以有提案被选定。这暗示我们:

P1:一个Acceptor必须批准它收到的第一个提案。

但根据P1,就可能导致多个提案分别被数量一致或相近的Acceptor批准,比如A、B分别被1、2批准,这样仍然无法确定唯一一个提案。怎么解决呢?

P2:一个提案被选定必须得到半数以上的Acceptor的批准。

这一需求的引入,表明Paxos算法要求奇数个节点组成集群,它的容错能力为2F+1,即最多允许F个节点同时出现故障。

至此我们已经保证了,只要有提案被提出,就一定会有一个提案被选定。并且结合P1和P2,暗示一个Acceptor必须能够批准多个提案。我们在这里引入一个全局有序的编号,ProposalID来唯一标识每一个被Acceptor批准的提案。

当一个提案——我们用value指代它的内容,被半数以上Acceptor批准后,我们就认为该value被选定了,该提案也被选定了,此时提案变成了一个由编号和Value组成的结合体:[ProposalID,Value]。

但此时又出现了一个问题,Acceptor可以批准对个提案,这就导致可能会有多个不同提案被选定,这违背了第1节中只有一个提案被选定的要求。所以:

P3:如果提案[ProposalID0,Value0]被选定了,那么后续所有被选定的提案的Value都必须为Value0。

只要能满足P3,即使有多个提案被选定,我们也可以保证所有被选定的提案具有相同的value值。

那怎么满足P3的要求呢?我们可以进一步得到

P3-1:如果提案[ProposalID0,Value0]被选定了,那么后续所有Acceptor批准的提案的Value都必须为Value0。

只要满足P3-1,P3肯定满足,但问题来了。如果此时提案[ProposalID0,Value0]被选定了,但是AcceptorN正好没有批准这个提案因为通信是异步的,即它不知道该提案已经被选定,它就没法对它后续批准的提案进行设置,这仍然会使得P3不满足。所以我们可以进一步对Proposer要求:

P3-2:如果提案[ProposalID0,Value0]被选定了,那么之后任何Proposer提出的提案Value值都必须为Value0。

 综上,我们可以得到Propser的执行流程:

1.Proposer向半数以上Acceptors发送一个只含有ProposalID的消息,该请求称为准备请求。

2.如果Proposer收到超过一半Accepotor的回复,就使用回复中最大的ProposalID的value,加上准备消息的ProposalID作为提案,如果回复中value为空,则可以提出任意值。

3.向回复的Acceptors发送Accept请求,请求它们批准提出的Proposal。

Acceptor的执行流程如下:

1.当收到准备请求时,如果ProposalID大于之前收到的准备消息,则回复其批准过的最大编号的提案。

2.当收到Accept请求时,如果Acceptor没有回复过一个更大ProposalID的准备消息,接受该Proposal并回复。

3.算法流程

我们可以得到Paxos的算法流程:

1.Proposer选择一个提案编号Mn(为保证唯一,可以使用时间戳+ServerID),然后向半数以上的Acceptor发送编号为Mn的准备请求。

2.如果一个Acceptor,A收到一个编号为Mn的准备请求,且Mn>A之前响应过的准备消息的最大编号(maxM_pre),则A将其批准过的最大编号的提案(既包含编号又包含value,假定为[Ma_max,va])作为响应反馈给Proposer,并且令maxM_pre=Mn(即不再回复编号小于等于Mn的准备消息)。

3.如果Proposer收到来自半数以上的Acceptor对于准备消息Mn返回的反馈,那么他就会发送一个提案[Mn,Vn]给Acceptors,其中Vn为收到的反馈中编号最大的提案的值(即可能为va),如果响应中不包含任何提案,则可以是任意值。这一步被称为Accept请求。

4.如果Acceptor收到这个针对[Mn,Vn]提案的Accept的请求,只要maxM_pre<=Mn,即没响应过更大编号的准备请求,则通过这个提案。同时,令Ma_max=Mn,va=Vn。

伪代码如下:

通过伪代码我们可以发现一个问题,假设有两个ProposerA和B,A发送一个准备得到了半数以上的反馈,正在进行accept请求时,B也发送了一个准备,此时A检测到有更新的提案,会重新回到步骤1,重复上面过程,将会形成一个活锁(Livelock)的情形。

可以考虑选择一个Proposer作为主Proposer,规定只有它能提出提案,这样就能保证算法流程的活性。

4.Multi-Paxos算法

原始的Paxos算法(Basic Paxos)只能对一个值形成决议,决议的形成至少需要两次网络来回,在高并发情况下可能需要更多的网络来回,极端情况下甚至可能形成活锁。如果想连续确定多个值,原始Paxos算法就无法解决。

实际应用中几乎都需要连续确定多个值,而且希望能有更高的效率。Multi-Paxos正是为解决此问题而提出。Multi-Paxos基于原始Paxos做了两点改进:

  1. 针对每一个要确定的值,运行一次Paxos算法实例(Instance),形成决议。每一个Paxos实例使用唯一的Instance ID标识。
  2. 在所有Proposers中选举一个Leader,由Leader唯一地提交Proposal给Acceptors进行表决。这样没有Proposer竞争,解决活锁问题。在系统中仅有一个Leader进行Value提交的情况下,Prepare阶段就可以跳过,从而将两阶段变为一阶段,提高效率。

Multi-Paxos流程:

Multi-Paxos首先需要选举Leader,Leader的确定也是一次决议的形成,所以可执行一次Basic Paxos实例来选举出一个Leader。选出Leader之后只能由Leader提交Proposal,在Leader宕机之后服务临时不可用,需要重新选举Leader继续服务。在系统中仅有一个Leader进行Proposal提交的情况下,Prepare阶段可以跳过。

Multi-Paxos通过改变Prepare阶段的作用范围至后面Leader提交的所有实例,从而使得Leader的连续提交只需要执行一次Prepare阶段,后续只需要执行Accept阶段,将两阶段变为一阶段,提高了效率。为了区分连续提交的多个实例,每个实例使用一个Instance ID标识,Instance ID由Leader本地递增生成即可。

Multi-Paxos允许有多个自认为是Leader的节点并发提交Proposal而不影响其安全性,这样的场景即退化为Basic Paxos。

Chubby和Boxwood均使用Multi-Paxos。ZooKeeper使用的Zab也是Multi-Paxos的变形。

5.参考文献

[1]https://zhuanlan.zhihu.com/p/31780743

[2]《从Paxos到Zookeeper分布式一致性原理与实践》

猜你喜欢

转载自blog.csdn.net/qq_35590459/article/details/110100933