Paxos算法

算法介绍

Paxos算法是Leslie Lamport最先提出的一种基于消息传递的一致性算法,用于解决分布式系统中的一致性问题。在目前所有的一致性算法中,该算法最常用且被认为是最有效的。
所谓的分布式系统的一致性问题,就是如何保证系统中初始状态相同的各个结点在执行相同的操作序列时,看到的指令序列是完全一致的,并且最终得到完全一致的结果

三类节点

在Paxos算法中,节点被分成了三种类型:proposersacceptorslearners。其中:
proposers提出决议(value,实际上就是告诉系统接下来该执行哪个指令)
acceptors批准决议
learners获取并使用已经通过的决议
一个结点可以兼有多重类型。
在Paxos算法中,主要操作的对象是proposers和acceptors,learners可以暂时忽略不管。
接下来是Paxos算法最关键的几个部分,我把它们分为三个条件、两个约束、两个阶段。

三个条件

1.决议只有在被proposers提出后才能批准
2.每次只批准一个决议
3.只有决议确定被批准后learners才能获取这个决议
为了满足上述三个条件,主要是第二个条件,即如何保证每次只批准一个决议,必须对系统有一些约束条件。通过约束条件的不断加强,最后得到一个可以实际运用到算法中的完整约束条件。

两个约束

在决议的过程中,proposers将决议发送给acceptors,acceptor对决议进行批准,批准后的决议才能称为正式的决议。
即决议的传递过程是这样的:Proposers —> Acceptors —> Learners
那么问题来了,批准的标准是什么?
决议的批准采用非常原始的方式,即少数服从多数原则,也就是说大多数acceptors接收的决议将成为最终的正式决议。
从集合论的观点来看,两组“多数派”(Majority)至少有一个公共的acceptor,这句话怎么理解,就是说多数派最起码是有超过一半的acceptor才能叫多数派,那么两组多数派都有超过一半的acceptor,必定有重复的且数量至少为一个的acceptor。
如果每个acceptor只能接受一个决议,则“每次只批准一个决议”的条件就可以的得到保证,因此,第一个约束出现啦!我们称它为p1:
p1:每个acceptor只接受它得到的第一个决议。
p1表明一个acceptor可以收到多个决议,为了区分,对每个决议进行编号,后到的决议编号大于先到的决议编号。
注:决议的编号除了能表明决议到来的顺序之外再无别的用处,真正有意义的是决议的内容,即value。

但其实我们只要一仔细想就能发现约束p1是不完备的,仅靠约束p1是根本无法得到一个“多数派”,从而无法得到一个正式的决议。比如说:

在这里插入图片描述

Acceptor1已经批准了决议1,但是Acceptor2批准的是决议2,这样的话决议1并没有被大多数acceptors批准,那么决议1也就无法成为正式的决议,那么如何让Acceptor2批准决议1呢?
进一步加强约束得到:
p2:一旦某个决议得到通过,之后通过的决议必须和该决议保持一致。
这样的话决议1在得到通过之后,Acceptor2需要批准的决议也是决议1,这样的话决议1就会被大多数acceptors批准,进而成为正式决议。
说是两个约束,其实第二个约束包含了三条内容。
我们对p2稍作加强得到:
p2a:一旦某个决议v得到通过,之后任何acceptor再批准的决议必须是v。
虽然表面上看上去,p1和p2a组合起来已经很完备了,但实际上这两者是存在冲突的情况的,比如说:
在系统得到决议v的过程中一个proposer和一个acceptor因为出现问题并没有参与到决议的表决中。在得到决议v之后出现问题proposer和acceptor恢复过来,此时这个proposer提出一个表决w(w不等于v)给这个acceptor。如果按照p1,这个acceptor应该接受这个决议w,但是按照p2a,它应该接受v,而不应该接受w,所以还需进一步加强约束条件:
p2b:一旦某个决议v得到通过,之后任何proposer再提出的决议必须是v。
这样的话即使没有参与到决议的表决中去的proposer恢复过来了,它提出的是已经通过的决议v。
p2b虽然和p1并不冲突,但是在技术上去实现是很困难的,因此提出一个蕴含p2b的约束p2c:
p2c:如果一个编号为n的提案具有值v,那么存在一个“多数派”,要么它们中没有谁批准过编号小于n的任何提案,要么它们进行的最近一次批准具有值v。
p2c可以保证决议v得到通过,进而p2b可以保证proposer提出的一定是决议v,p1可以保证acceptor得到的第一个决议是v,这样的话被“多数派”批准的决议v一定可以得到通过,也就是说决议v具有唯一性。
为了保证决议v的唯一性,acceptor也要满足一个约束条件:
当且仅当acceptor没有收到编号大于n的请求时,acceptor才批准编号为n的提案。
也就是说acceptor收到编号小于以往批准的提案的编号时,要么不予理睬,要么返回error。

两个阶段

在proposer和acceptor得到约束之后,具体的批准提案的过程又是怎样的呢?

一个决议分为两个阶段:

一、准备阶段

S1a:Proposer发送Prepare请求
proposer选择一个提案并将它的编号设为n,编号是全局唯一且可递增的,将它发送给全部acceptor或其中的一个“多数派”。
S1b:Acceptor应答Prepare请求
根据上述对acceptor的约束可以把应答情况分为两类:
(1)如果acceptor收到的提案的编号>它已经回复的所有提案的编号,则
根据acceptor的约束,假设收到的提案编号是n,这个n是目前acceptor收到的所有的编号中最大的那一个,所以它可以批准该提案。
acceptor将该提案的编号记录下来,作为当前接收到的最大的提案编号;然后回复请求,并将自己上次批准的编号和value(如果有)回复给proposers,并不再批准小于n的提案(如果这个提案是acceptor接收到的第一个提案,那么返回null(编号)和null(值)即可)
其实这种情况就是acceptor的约束的表现形式
(2)如果acceptor收到的提案的编号<已经回复的所有提案的编号,则acceptor不再回复或回复error(也就意味着该acceptor对proposer的该提案不批准,有可能造成该提案得不到“多数派的支持”)

二、批准阶段/选举阶段

S2a:Proposer发送Accept请求
Proposer能发送Accept请求的前提是:之前发送的Prepare请求已经得到了“多数派”的支持,具体要看Acceptor对Prepare请求的应答情况,即S1b。
经过一段时间后,Proposer收集到一些Prepare回复,有下列几种情况:
(1)回复数量>一半的Acceptor数量,且所有的回复的value都为空,则Proposer发送accept请求,并带上自己指定的value。
(2)回复数量>一半的Acceptor数量,且有的回复value不为空,则Proposer发出accept请求,并带上回复中编号最大的value(作为自己的提案内容)。
其实第二种情况很有意思,它的意思就是虽然你proposer发送的编号够大,但是在此之前我们acceptor已经批准了一部分提案,那么你就要尝试发送这些提案,做到提案的唯一性,我个人认为Paxos算法的目的是为了选出一个提案,而不是选出一个指定的提案。
(3)回复数量<=一半的Acceptor数量,则尝试更新生成更大的编号,再转回S1a执行(就是说,该提案没批准,编号太小了,你换个大的!)
在此过程中,其实有个问题需要解决一下,假如说Proposer提出的提案编号是1,但是编号为3的提案已经得到了批准,那么你这个proposer再提出编号为2的提案是没有意义的,所以可以看出,这个编号首先是递增的,而且递增也是有规则的,以下为如何生成一个更大的且合适的编号:
在这里插入图片描述
也就是说更新的编号是跟proposer的数量有关的。

S2b:Acceptor应答请求
这个其实跟Acceptor应答Prepare请求是一样的:
(1)收到的编号N>=接收到的最大编号值,则回复提交成功(ok),并持久化编号N和value(也就是说通过accept请求之后,记录下来的编号n和value就不再变动了,即使后来的编号更大,应答的依旧是(ok,#n,value))
(2)收到的编号<接收到的最大编号值,则不回复或者回复error。

S2c:Proposer统计投票
经过一段时间后,Proposer收集到一些Accept回复提交成功,有几种情况:
(1)回复数量>一半的Acceptor数量,则表示提交value成功。此时,可以发一个广播给所有Proposer,通知它们已commit的value。(其实到这一步,最终提案已经选出来了,Paxos算法也就结束了)
(2)回复数量<=一半的Acceptor数量,则尝试更新生成更大的编号,再转S1a执行。

这两个阶段五个步骤算是一轮,每个Proposer的Paxos协议是一轮一轮分别进行的,根据Proposer向Acceptor提交决议的顺序,每一个Proposer都要经过一轮Paxos算法。最终可能需要多轮才能确定唯一的决议。

举例

题目要求:在server1、server2和server3中选一个Master。
已知:
Proposer1的编号是2;
Proposer2的编号是1;
Proposer3的编号是3;
Proposer向Acceptor提交决议的顺序是:Proposer1、Proposer3、Proposer2。
Proposer向Acceptor提交决议的情况是:
在这里插入图片描述
试分析:
最终选取出的Master是哪台服务器?写出分析过程。

分析过程如下:
初始状态:
在这里插入图片描述
第一轮:
按照Proposer向Acceptor发送提案的顺序:
Proposer 1:
在这里插入图片描述
Acceptor1、2由于之前没有批准任何提案,所以记录下当前编号,并回复成功批准Prepare请求(ok,null,null)
Proposer 3:
在这里插入图片描述

注意!上次只是批准了Prepare #2请求,并不是批准了Proposer提出了提案,批准提案的前提是已经批准了Accept请求,显然目前没有达到这个阶段,所以回复的内容依旧是(ok,null,null)

Proposer 2:
在这里插入图片描述

Proposer 2给的编号太小了,Acceptor 3可不搭理它,所以啊Proposer再试试更新更大点的编号吧!
至此,第一轮结束,Proposer们轮了个遍,该统计一下Acceptor对Prepare请求的应答情况了:
Proposer 1: 2应答>一半Acceptor数量,编号就不用更新了,下次带着自己的值来吧
Proposer 3: 2应答>一半Acceptor数量,也一样,编号就不用更新了,下次带着自己的值来吧
Proposer 2: 0应答<一半Acceptor数量,小可爱,更新编号吧,嘤嘤嘤

第二轮:
Proposer 1:
在这里插入图片描述

情况就是Acceptor1批准了Proposer 1的Accept请求,把值记录了下来并予以回复,但是Acceptor1拒绝了Proposer 1的Accept请求。

Proposer 3:
在这里插入图片描述
Acceptor 2和3都批准了Proposer 3的Accept请求,把值记录了下来,并予以应答,不得不说这Proposer 3的面子还真是大!

Proposer 1:
在这里插入图片描述
我们可怜的Proposer 1的Prepare请求终于得到答复了,可惜Acceptor 2和3的值和编号已经持久化了,即使你给的编号再大,也只能回你个ok了。
至此,第二轮结束,Proposer们轮了个遍,该统计一下Acceptor对Accept请求的应答情况了:
Proposer 1: 1应答<一半Acceptor数量,只能更新编号了,下次带着应答中最大编号的值来。
Proposer 3: 2应答>一半Acceptor数量,好的,你的提案已经被组委会批准了!
Proposer 2: 2应答>一半Acceptor数量,编号是不用更新了,但是值还得用acceptor给你的。

第三轮:
Proposer 1:
在这里插入图片描述
这就不用我多说了吧!

Proposer 3:
Proposer 3的Accept请求已经被多数派批准,至此,Master已经选出来了,就是Proposer的值:Serv3!

The end.

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

so.far_away

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值