分布式一致性协议-Paxos

Paxos算法是分布式一致性的重要协议,由Leslie Lamport提出,旨在解决异步系统中的一致性问题。它涉及Proposer、Acceptor、Learner三个角色,确保在多种情况下能正确选定唯一的提案。算法分为准备阶段和接受阶段,通过多数派原则避免冲突。通过选取主Proposer可以保证算法的活性,防止死循环。
摘要由CSDN通过智能技术生成

分布式系统的一致性问题

面向大型高可用可扩展的分布系统,对数据的处理我们摒弃了传统事务的强一致性模型,而是在系统的可用性和数据一致性之间进行一定的权衡,于是就产生了一系列的一致性协议。今天我将分享一致性协议中一个重要的算法Paxos算法。

Paxos算法由来

Paxos算法是一个非常重要的分布式一致性协议。Paxos算法是莱斯利·兰伯特(Leslie Lamport)于1990年提出的一种基于消息传递且具有高度容错特性的一致性算法,被公认为解决分布式一致性问题最有效的算法之一。
1982年,Lamport与另外两人共同发表了论文 The Generals Problem,提出了一种计算机容错理论,他设想这么一个场景: 拜占庭帝国有许多支军队,不同军队之间必须制定一个统一的行动计划,从而做出进攻或者撤退的决定,同时,各个将军在地理上都是被分隔开来的,只能依靠军队的通讯员来进行通讯。然而,在所有的通讯员中可能会存在叛徒,这些叛徒可以任意篡改消息,从而达到欺骗将军的目的。这就是著名的“拜占庭将军问题”。从理论上来说,在分布式计算领域,试图在异步系统和不可靠的通道上来达到一致性状态时不可能的,因此在对一致性研究的过程中,我们往往假设信道是可靠的。而事实上,大多数系统都是部署在同一个局域网中的,因此消息被篡改的情况非常罕见;另一方面,由于硬件和网络原因而造成的信息不完整问题,只需一套简单的校验算法即可避免。因此,在实际工程实践中,可以假设不存在拜占庭问题,即假设所有消息都是完整的,没有被篡改的。
Lamport在1990年提出了一个理论上的一致性解决方案,同时给出了严格的数学证明。具体的决策过程: 在古希腊有一个叫做Paxos的小岛,岛上采用议会的形式来通过法令,议会中的议员通过信使进行消息传递。值得注意的是,议员和信使都是兼职的,他们随时有可能离开议会室,并且信使可能会重复的传递消息,也可能一去不复返。因此,议会协议要保证在这种情况先法令仍然能够正确的产生,并且不会出现冲突。 这就是论文The Part-Time Parliament中提到的兼职议会,而Paxos算法名称的由来也是取自论文中提到的Paxos小岛。

Paxos算法详解

问题描述

假设有一组可以提出提案的进程集合,那么对于一个一致性算法来说需要保证以下几点:
·在这些被提出提案中,只有一个会被选定。
·如果没有提案被提出,那么就不会有被选定的提案。
·当一个提案被选定后,进程应该可以获取被选定的提案信息。
对于一致性来说,安全性还需要考虑:
·只有被提出的提案才能被选定。
·只能有一个值被选定。
·如果某个进程认为某个提案被选定了,那么这个天必须是真的被选定的那个。
也就是说Paxos算法的目标是要保证最终有一个提案会被选定,当提案被选定后,进程最终也能获取到被选定的提案。
在该一致性算法中,有三种参与角色,我们通常用Proposer、Acceptor、Learner来表示。在具体的实现中,一个进程可能充当不止一个角色。假设不同参与者之间可以通过收发消息来进行通信,那么:
·每个参与者以任意速度执行,可能会因为出错而停止,也可能会重启。同时,即使一个提案被选定后所有参与者也都有可能失败或者重启,因此除非那些失败或重启的参与者可以记录某些信息,否则将无法确定最终的值。
·消息在传输过程中可能出现不可预知的延迟,也可能会重复或者丢失,但消息不会被破坏,即消息内容不会被篡改(不存在拜占庭式的问题)

提案的选定

要选定一个唯一提案的最简单的方法就是只允许一个Acceptor存在,这样Proposer只能发送提案给该Acceptor,Acceptor只需要将它第一个收到的提案作为被选定的提案。这种解决方式实现起来非常简单,但实际情况却难让人满意,因为一旦这个Acceptor出现问题,那么整个系统就无法工作了。
因此,需要寻找一种更好的方式,来解决Acceptor的单点问题。可以使用多个Acceptor来解决这个问题,那么多Acceptor是如何进行提案的选取呢?
Proposer向一个Acceptor集合发送提案,同样,集合中的每个Acceptor都可能会批准该提案,当有足够多的Acceptor批准这个提案的时候,我们就可以认为该提案被选定了。那么,什么是足够多呢?我们假定足够多的Acceptor是整个Acceptor集合的一个子集,并且让这个集合大得可以包含Acceptor集合中的大多数成员,因为任意两个包含大多数Acceptor的子集至少有一个公共成员。另外我们再规定,每个Acceptor最多只能批准一个提案,那么就能保证只有一个提案被选定了。

推导过程

在实际的算法设计中,我们还需要添加一些限制条件来保证算法的有效与合理性。首先,在没有失败和消息丢失的情况下,如果我们希望即使只有一个提案被提出的情况下,仍然可以选出一个提案,这就如下需求。
P1:一个Acceptor必须批准它收到的第一个提案。
上面的这个需求引出了另外一个问题:如果有多个提案被不同的Proposr同时提出,这可能会导致虽然每个Acceptor都批准了它收到的第一个提案,但是没有一个提案是由多数人批准的。
在这里插入图片另外,即使只有两个提案被提出,如果每个提案都被差不多一半的Acceptor批准了,也可能导致无法确定该选定哪个提案。
在这里插入图片描述因此,在P1的基础上,需要再加上一个提案被选定需要半数以上的Accpetor批准的需求,这就暗示着一个Acceptor必须能够批准不止一个提案。这里,可一个使用一个全局的编号来唯一标识每一个被Acceptor批准的提案,当一个具有某个Value值的提案被半数以上的Acceptor批准后,我们就认为该Value被选定了,此时我们也就认为该提案被选定了。此时的提案由一个编号和Value组成,即[编号,Value].
根据上面的内容,我们虽然允许多个提案被选定,但同时必须保证所有被选定的提案都具有相同的Value值。这里我们需要做如下约定:
P2:如果编号为M0、Value值为V0的提案[M0,V0]被选定了,那么所有比编号M0高的,且被选定的提案,其Value值必须也是V0.
因为提案的编号是全序的,条件P2就保证了只有一个Value值被选定这一关键安全性属性。同时,一个提案要被选定,其首先必须被至少一个Acceptor批准,因此我们可以通过满足如下条件来满足P2。
P2a:如果编号为M0、Value值为V0的提案[M0,V0]被选定了,那么所有比编号M0更高的,且被Acceptor批准的提案,其Value值必须也是V0。
至此,我们仍然需要P1来保证提案会被选定,但是因为通信是异步的,一个提案可能会在某个Acceptor还未收到任何提案时就被选定了。
在这里插入图片描述如图所示,在Accpetor1没有收到任何提案的情况下,其他4个Acceptor已经批准了来着Proposer2的提案[M0,V1],而此时,Proposer1产生了一个具有其他Value值的、编号更高的提案[M1,V2],并发送给了Accpetor1。那么根据P1,就需要Acceptor1批准该提案,但是这与P2a矛盾,因此如果要同时满足P1和P2a,需要对P2a进行如下强化:
P2b:如果一个提案[M0,V0]被选定后,那么之后任何Proposer产生的编号更高的提案,其Value值都是V0。
因为一个提案必须在被Proposer提出后才能被Acceptor批准,因此P2b包含了P2a,进而包含了P2a。于是我们只要证明P2b成立则整个理论为成功的,即: 假设某个提案[M0,V0]已经被选定,那么任何编号Mn>M0的提案,其Value值都是V0.

数学归纳法证明

我们可以用第二数学归纳法来进行证明,也就是:假设在编号M0到Mn-1之间的提案,其Value值都是V0,证明Mn提案对应的Value值也为V0.
因为编号为M0的提案是已经被选定了,这就意味着在肯定存在一个由半数以上的Accpetor组成的集合C,C中的每个Acceptor都批准了该提案。再结合归纳假设,“编号为M0的提案被选定“意味着:C中的每个Accpetor都批准了一个编号在M0到Mn-1范围内的提案,并且每个编号在M0到Mn-1范围内被Accpetor批准的提案,其Value值都为V0.
因为任何包含半数以上Acceptor的集合S都至少包含C中的一个成员,因此我们可以认为如果保持了下面的P2c的不变性,那么编号Mn的提案的Value值也是V0.
P2c:对于任意的Mn和Vn,如果提案[Mn,Vn]被提出,那么肯定存在一个半数以上的Acceptor组成的集合S,满足以下两个条件中的任意一个。
·S中不存在任何批准过编号小于Mn的提案Acceptor。
·选取S中所有Acceptor批准的编号小于Mn的提案,其中编号最大的那个提案其Value值是Vn.
至此,只需要通过保持P2c,我们就能够满足P2b了。
从上面的推导中,我们可以发现,从P1到P2c的过程是一系列条件的逐步加强,如果需要证明这些条件可以保证一致性,那么就需要进行反向推导:P2c=>P2b=>P2a=>P2,然后通过P2和P1来保证一致性。
现在我们证明在满足P2c条件下,结合归纳假设推导出结论的正确性。
1.当Mn = M0+1时,如果有这样一个编号为Mn的提案[Mn,Vn],首先我们知道[M0,V0]已经被选定了,那么就一定存在一个Accpetor的子集S,且S中的Acceptor已经批准了一个小于Mn的提案,于是,Vn只能是由多数集S中编号小于Mn但为最大的那个提案的Value值。因为此时Mn=M0+1,因此编号小于Mn且最大的提案为[M0,V0],同时由于S和通过[M0,V0]的Acceptor集合都是多数集,也就是说二者肯定有交集-这样的Acceptor在确定Vn取值时,选择了V0.
2.根据假设,编号在M0+1到Mn-1区间内所有的提案的Value值为V0,那么根据1.同理可以证明编号为Mn的提案如果要被批准,则其Value值为V0.

Proposer生成提案

对应一个Proposer来说,要提出一个编号为Mn的提案,必须知道当前某个将要或者已经被半数以上Acceptor批准的编号小于Mn但为最大编号的提案。并且,要求Acceptor不再批准任何编号小于Mn的提案,故有如下提案生成算法:
1.Proposer选择一个新的提案编号Mn,然后向某个Acceptor集合的成员发送请求,要求该集合的Acceptor做出如下回应。
·向Proposer承诺,保证不再批准任何编号小于Mn的提案。
·如果Acceptor已经批准过任何提案,那么其就向Proposer反馈当期该Acceptor已经批准的编号小于Mn但为最大编号的那个填的值。
2.如果Proposer收到来自半数以上的Accpetor的响应结果,那么它就可以产生编号为Mn、Value值为Vn的提案,这里的Vn是所有响应中编号最大的提案的Value值。当然如果半数以上的Acceptor都没有批准过任何提案,那么此时Vn值由Proposer任意选择。
在确定提案后,Proposer就会将该提案再次发送给某个Acceptot集合并期望获得批准。我们称为Accept

Accpetor批准提案

根据上面的内容,一个Acceptor可能会收到来自Proposer的两种请求,分别是Prepare请求和Accept请求,对应这两类请求做出响应的条件分别是:
·Prepare请求:Acceptor可以在任何时候响应一个Prepare请求
·Accept请求:在不违背Acceptor现有承诺的前提下,可以任意响应Accept请求。

算法陈述

综合前面的内容,我们对Paxos算法的提案选择过程进行一个陈述。

阶段一

1.Proposer选择一个提案编号Mn,然后向Acceptor的某个超过半数的子集成员发送Mn的Prepare请求。
2.如果一个Acceptor收到一个编号为Mn的Proposer请求,且编号Mn大于该Acceptor已经响应的所有Prepare请求的编号,那么它就会将它已经批准过的最大编号的提案作为响应反馈给Proposer,同时该Acceptor会承诺不会再批准任何编号小于Mn的提案。

阶段二

1.如果Proposer收到来自半数以上的Acceptor对于其发出编号为Mn的Prepare请求的响应,那么它就会发送一个针对[Mn,Vn]提案的Accept请求给Acceptor。注意,Vn值是收到响应中编号最大的提案的值,如果响应中不包含任何提案,那么它就是任意值。
2.如果Acceptor收到这个针对[Mn,Vn]提案的Accept请求,只要该Acceptor尚未对编号大于Mn的Prepare请求作出响应,那么它就可以通过这个提案。

提案的获取

上面我们介绍了如何选定一个提案,那么如何将选定的提案让Learner获取呢,大概有以下几种方案:

方法一

一旦Acceptor批准了一个提案就将该提案发送给所以的Learner。显然,这种做法虽然可以尽快让Learner获取被选定的提案,但却需要让每个Acceptor与所有的Learner逐个进行通信,通信次数至少是二者个数的乘积。

方法二

我们可以让所有Acceptor将它们对提案的批准情况,统一发送给一个特定的Learner(主Learner)由主Learner通知其它Learner,这种方法虽然会减少通信次数,但引入单点问题。

方法三

我们对方法二进行改进,可以将主Learner的范围扩大,即Acceptor可以将批准的提案发送给一个特定的Learner集合,该集合中的每个Learner都可以在一个提案批准后通知其它Learner.

通过选取主Proposer保证算法的活性

现在,我们基本知道了Paxos算法的核心逻辑,下面我们来看Paxos算法在实际运作过程中的一些细节。假设存在这样一种极端情况,两个Proposer一次提出了一系列编号递增的提案,但是最终都无法被选定,具体流程:
Proposer P1提出了一个编号为M1的提案,并完成了上述阶段一的流程,但与此同时,另一个Proposer P2提出了一个编号为M2(M2>M1)的提案,同样完成了阶段一的流程,于是Acceptor已经不能再批准编号小于M2的提案了。因此,当P1进入阶段二的时候,其发出的Accept请求将被Acceptor忽略,于是P1再次进入阶段一并提出了一个编号为M3(M3>M2)的提案,这又导致P2在第二阶段的Accept请求被忽略,以此类推,提案的选定过程将陷入死循环。

在这里插入图片描述为了保证Paxos算法流程的可持续性,就必须选择主Proposer,并规定只有主Proposer才能提出提案。这样一来,只要主Proposer和过半的Acceptor能够正常通信,就能保证整套Paxos算法流程的活性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值