Paxos算法学习

持续更新中,先填一点交个作业,我一定会回来完善的。

Paxos解决的就是在分布式系统中就某一个值达成共识的问题。

1、安全性要求

保证共识结果正确的三个基本前提条件:安全性要求
在这里插入图片描述

2、角色

Paxos中的三个角色,与Raft不同的是,Paxos允许一个节点同时承担多个角色,这就变得比较复杂。
在这里插入图片描述

3、系统什么时候达到共识?

The value is chosen when a large enough set of acceptors have accepted it.
当多数派节点都接受了这个值就达成了共识。

4、算法过程

首先,选举出结果的前提是Acceptor要去批准提案,如果Acceptor故意不批准提案,就可能永远选不出结果,因此有了第一个约束条件P1
P1约束

P1: An acceptor must accept the first proposal that it receives.

Acceptor必须批准(accept)他收到的第一份提案(proposal)。

P1漏洞:加入每个acceptor各自接受了等量的提案,导致选不出大多数集合,僵持不下的局面。
解决方案:允许acceptor接受新的提案,来覆盖旧的提案。
从而引入了提案编号来定义提案的新旧程度。

有了提案编号之后,什么时候达成共识?

A value is chosen when a single proposal with that value has been accepted by a majority of the acceptors. 
In that case, we say that the proposal (as well as its value) has been chosen.

P2约束

P2: If a proposal with value v is chosen, then every higher-numbered proposal that is chosen has value v.

如果一份内容为v的提案成为决议,那么任何编号更高的提案如果也成为了决议,它的内容也必须为v。

P2a约束

P2a: If a proposal with value v is chosen, then every higher-numbered proposal accepted by any acceptor has value v.

如果一份内容为v的提案成为决议,那么任何编号更高的提案如果被任意Acceptor批准,它的内容也必须为v。

P2b约束

P2b: If a proposal with value v is chosen, then every higher-numbered proposal issued by any proposer has value v.

如果一份内容为v的提案成为决议,那么任何编号更高的提案如果被任意Proposer发起,它的内容也必须为v。

P2c约束

P2c: For any v and n, if a proposal with value v and number n is issued, then there is a set S consisting of a majority of acceptors such that either
(a) no acceptor in S has accepted any proposal, or
(b) v is the value of the highest-numbered proposal among all proposals accepted by the acceptors in S.

算法过程

少数服从多数

首先要认识到,这是一个分布式系统下的共识算法,要解决的问题,简化一点,就是一堆机器,每一台都可能会收到客户端的一条消息,那需要将自己收到的消息,告诉其他的机器,让所有分布式系统中的机器,达到最终的一致,这就是达到共识。

Paxos采取了一个我们非常熟悉的达成共识的方法:少数服从多数。只要有超过一半的机器认可某一个消息,那么最终就所有机器都接受这条消息并将它作为本次的结论。而竞选失败的少数派消息,就会被拒绝,并由第一个从客户端处接收到该消息的机器,向客户端发送失败结果,由客户端进行重试,去尝试在下一轮竞选中胜出。

少数服从多数,说来简单,如果是一群人的话,大家碰个头一起举手表决就好了。但是放到一个分布式系统中就变复杂了。机器之间怎么传递提议,怎么表决,怎么统计多数,网络传输需要时间,在表决过程中,其他机器收到了新的消息怎么办,都需要一整套机制来解决。

下面就来逐步讲解Paxos的过程,但在讲解过程之前,先说Paxos中最常见的两种角色:

Proposer:提案者。也就是在选举中提出提案的人,放到分布式系统里,就是接收到客户端写操作的人。一切行为都由Proposer提出提案开始,Paxos会将提案想要进行的操作,抽象为一个“value”,去在多台机器中传递,最后被所有机器接受。

Acceptor:批准者。Acceptor 从含义上来说就是除了当前Proposer以外的其他机器,他们之间完全平等和独立,Proposer需要争取超过半数(N/2+1)的 Acceptor 批准后,其提案才能通过,它倡导的“value”操作才能被所有机器所接受。

除了以上两种角色,实际上Paxos还会提到Learner,即学习者这个角色,该角色是在达成决议时,对结论的学习者,也即是从其他节点“学习”最终提案内容,比较简单。需要注意,这些角色只是在不同时间下,逻辑上的划分,实际上任何一台机器都可以充当这三个角色之一。

一个简单的提案

先描述最简单的情况,假设现在有四台机器,其中一台收到了来自客户端的写操作请求,需要同步给其他机器。

此时这台收到请求的机器,我们称它为Proposer,因为它将要开始将收到的请求,作为一个提案,提给其他的机器。这里为了方便,我们假设这个请求是要将一个地址设置为“深圳”,那么如下图所示:
在这里插入图片描述
此时,其他的Acceptor都闲着呢,也没其他人找,所以当它们收到Proposer的提案时,就直接投票了,说可以可以,我是空的,赞成提案(同意提议):

在这里插入图片描述

到这里,就还是一个简单的同步的故事,但需要注意的是,这里Proposer实际上是经历了两步的。

在这个简单的提案过程中,Proposer其实也经历了两个阶段:

  • Prepare阶段:Proposer告诉所有其他机器,我这里有一个提案(操作),想要你们投投票支持一下,想听听大家的意见。Acceptor看自己是NULL,也就是目前还没有接受过其他的提案,就说我肯定支持。

  • Accept阶段:Proposer收到其他机器的回复,说他们都是空的,也就是都可以支持接受Proposer的提案(操作),于是正式通知大家这个提案被集体通过了,可以生效了,操作就会被同步到所有机器正式生效。

两个提案并发进行

现在考虑一个更复杂的场景,因为我们处于一个分布式的场景,每台机器都可能会收到请求,那如果有两台机器同时收到了两个客户端的不同请求,该怎么处理呢?大家听谁的呢?最后的共识以谁的为准呢?如下图

在这种情况下,由于网络传输的时间问题,两个Proposer的提案到达各个机器,是会存在先后顺序的。假设Proposer 1 的提案先达到了 Acceptor 1 和 Acceptor 2,而Proposer 2 的提案先达到了 Acceptor 3,其达到 Acceptor 1 和 Acceptor 2 时,由于机器已经投票给Proposer 1 了,所以Proposer 2 的提案遭到拒绝,Proposer 1 达到 Acceptor 3 的时候同样被拒。

Acceptor们迷了,Proposer们也迷了,到底应该接受谁?此时,还是遵循自由民主的法则——少数服从多数。

Proposer 1 发现超过半数的Acceptor都接受了自己,所以放心大胆地发起要求,让所有Acceptor都按照自己的值来操作。而Proposer 2 发现只有不到半数的Acceptor支持自己,而有超过半数是支持Proposer 1 的值的,因此只能拒绝Client 2,并将自己也改为Proposer 1 的操作:

到此为止,看起来没有问题,但是,这是因为恰好Acceptor的数量是单数,可以选出“大多数”,但是因为同时成为Proposer的机器数量是不确定的,因此是无法保证Acceptor的数量一定是单数的,如下面这种情况就无法选出“大多数”了:

这时,两个Proposer有可能总是先抢到一个Acceptor的支持,然后在另一个Acceptor处折戟沉沙,算法就一直循环死锁下去了。为了解决这种情况,Paxos给提案加了一个编号。

给提案加上编号

之前我们Proposer的提案都是只有操作内容的,现在我们给他加一个编号,即:

Proposer 1 的提案为:[n1, v1]

Proposer 2 的提案为:[n2, v2]

假设Proposer 1 接到Clint 1 的消息稍微早一点,那么它的编号就是1,Proposer 2 的编号就是2,那么他们的提案实际就是:

Proposer 1 的提案为:[1, { Set Addr = “深圳”}]

Proposer 2 的提案为:[2, { Set Addr = “北京”}]

此时,Paxos加上一条规则:

Acceptor如果还没有正式通过提案(即还没有Accept使操作生效),就可以接受编号更大的Prepare请求。

所以,回到上面的困境

当Proposer 1 想要向Acceptor 2 寻求支持时,Acceptor 2 一看你的编号(1)比我已经支持的编号(2)要小,拒绝拒绝。此时Proposer 1 由于没有得到过半数的支持,会重新寻求支持。

而当Proposer 2 想要向Acceptor 1 寻求支持时,Acceptor 1 一看你的编号(2)比我已经支持的编号(1)要大,好的你是老大我听你的。此时Proposer 2 已经得到了超过半数的支持,可以进入正式生效的Accept阶段了。

这里需要补充一下,Proposer 1 这里支持提案失败,他是怎么让自己也接受Proposer 2 的提案的呢?

所以这里的后续会发生的事情是:

Proposer 2 发现得到了过半数的支持,开始向所有Acceptor发送Accept请求。

所有Acceptor接收到Accept请求后,按照之前Prepare时收到的信息与承诺,去生效Proposer 2 的提案内容(即Set Addr = “北京”的操作)。

Proposer 1 之前已经收到了所有Acceptor的回复,发现没有得到过半数的支持,直接回复Client 1 请求失败,并变成一个Acceptor(或者说Learner),接受Proposer 2 的Accept请求。

这里再想多一点,考虑另一种场景:假设Proposer 2 的Accept请求先达到了Acceptor 2,然后Proposer 1 向Acceptor 2 发送的Prepare请求才到达 Acceptor 2,会发生什么呢?

最直观的处理是, Acceptor 2 直接拒绝,然后Proposer 1 走上面的流程,但Paxos为了效率,又增加了另一条规则:

如果一个Prepare请求,到达Acceptor时,发现该Acceptor已经接受生效了另一个提案,那么它除了回复提案被拒绝外,还会带上Acceptor已经通过的编号最大的那个提案的内容回到Proposer。Proposer收到带内容的拒绝后,需要修改自己的提案为返回的内容。

此时会发生的事情就变成了:

此时Acceptor 2 除了会拒绝它的请求,还会告诉Proposer 1,说我已经通过并生效了另一个编号为2的提案,内容是Set Addr = “北京”。

然后Proposer 1 查看回复时,发现已经有Acceptor生效提案了,于是就修改自己的提案,也改为Set Addr = “北京”,并告知Client 1 你的请求失败了。

接着Proposer 1 开始充当Proposer 2 的小帮手,帮他一起传播 Proposer 2 的提案,加快达成共识的过程。

PS:这里需要注意,编号是需要保证全局唯一的,而且是全局递增的,否则在比较编号大小的时候就会出现问题,怎么保证编号唯一且递增有很多方法,比如都向一个统一的编号生成器请求新编号;又比如每个机器的编号用机器ID拼接一个数字,该数字按一个比总机器数更大的数字间隔递增。

一些异常情况

上面的规则是不是就能保证整个算法解决所有问题了呢?恐怕不是,这里再看看一些异常情况。

异常情况一:假设现在有三个Proposer同时收到客户端的请求,那么他们会生成全局唯一的不同编号,带着各自接收到的请求提案,去寻求Acceptor的支持。但假设他们都分别争取到了一个Acceptor的支持,此时由于Prepare阶段只会接受编号更大的提案,所以正常情况下只有Proposer 3 的提案会得到所有Acceptor的支持。但假设这时候Proposer 3 机器挂了,无法进行下一步的Accept了,怎么办呢?那么所有Acceptor就会陷入持续的等待,而其他的Proposer也会一直重试然后一直失败。

为了解决这个问题,Paxos决定,允许Proposer在提案遭到过半数的拒绝时,更新自己的提案编号,用新的更大的提案编号,去发起新的Prepare请求。

那么此时Proposer 1 和Proposer 2 就会更新自己的编号,从【1】与【2】,改为比如【4】和【5】,重新尝试提案。这时即使Proposer 3 机器挂了,没有完成Accept,Acceptor也会由于接收到了编号更大的提案,从而覆盖掉Proposer 3 的提案,进入新的投票支持阶段。

异常情况二:虽然更新编号是解决了上面的问题,但却又引入了活锁的问题。由于可以更新编号,那么有概率出现这种情况,即每个Proposer都在被拒绝时,增大自己的编号,然后每个Proposer在新的编号下又争取到了小于半数的Acceptor,都无法进入Accept,又重新加大编号发起提案,一直这样往复循环,就成了活锁(和死锁的区别是,他们的状态一直在变化,尝试解锁,但还是被锁住了)。

要解决活锁的问题,有几种常见的方法:

当Proposer接收到回复,发现支持它的Acceptor小于半数时,可以不立即更新编号重试,而是随机延迟一小段时间,来错开彼此的冲突。

可以设置一个Proposer的Leader,全部由它来进行提案,这即使共识算法的常见套路,选择一个Leader。这需要进行Leader的选举,以及解决存活性检查以及换届的问题。实际上就已经演变成Multi-Paxos了。

异常情况三:由于在提案时,Proposer都是根据是否得到超过半数的Acceptor的支持,来作为是否进入Accept阶段的依据,那如果在算法进行中新增或下线了机器呢?如果此时一些Proposer知道机器数变了,一些Proposer不知道,那么大家对半数的判断就会不一致,导致算法出错。

因此在实际运行中,机器节点数的变动,也需要作为一条要达成共识的请求提案,通过Paxos算法本身,传达到所有机器节点上。

为了使Paxos运行得更稳定,不需要时刻担心是否有节点数变化,可以固定一个周期,要求只有在达到固定周期时才允许变更节点数,比如只有在经过十次客户端请求的提案与接受后,才处理一次机器节点数变化的提案。

那如果这个间隔设置地相对过久,导致现在想要修改节点数时,一直要苦等提案数,怎么办呢?毕竟有时候机器坏了是等不了的。那么可以支持主动填充空的提案数,来让节点变更的提案尽早生效。

Paxos协议的两阶段

抽象和完善一下这个过程,就是:

Prepare准备阶段:在该阶段,Proposer会尝试告诉所有的其他机器,我现在有一个提案(操作),请告诉我你们是否支持(是否能接受)。其他机器会看看自己是否已经支持其他提案了(是否接受过其他操作请求),并回复给Proposer(如果曾经接受过其他值,就告诉Proposer接受过什么值/操作)。

Acceptor如果已经支持了编号N的提案,那么不会再支持编号小于N的提案,但可以支持编号更大的提案;

Acceptor如果生效了编号为N的提案,那么不会再接受编号小于N的提案,且会在回复时告知当前已生效的提案编号与内容。

Accept提交阶段:在该阶段,Proposer根据上一阶段接收到的回复,来决定行为:

如果上一阶段超过半数的机器回复说接受提案,那么Proposer就正式通知所有机器去生效这个操作;

如果上一阶段超过半数的机器回复说他们已经先接受了其他编号更大的提案,那么Proposer会更新一个更大的编号去重试(随机延时);

如果上一阶段的机器回复说他们已经生效了其他编号的提案,那么Proposer就也只能接受这个其他人的提案,并告知所有机器直接接受这个新的提案;

如果上一阶段都没收到半数的机器回复,那么提案取消。

PS:接受其他提案,以及提案取消的情况下,Proposer就要直接告诉客户端该次请求失败了,等待客户端重试即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Paxos算法是一种用于分布式系统中的一致性算法,它的主要目的是让多个节点在网络不可靠的情况下,能够就某个值达成一致意见。Paxos算法的基本原理是通过多轮投票的方式,选出一个值作为共识值。它的工作过程可以简单地分为三个阶段,分别是提议(Prepare)、承诺(Promise)和学习(Learn)。 在提议阶段,一个节点提出一个值,并向其他节点发送一个编号为n的提议请求,请求其他节点对该提议进行批准。在承诺阶段,其他节点会向提议节点返回自己对该提议的批准意见。只有当提议节点收到了超过半数节点的批准意见时,才能进入学习阶段。在学习阶段,提议节点将该值广播给其他节点,使得其他节点也能够接受该值。 Paxos算法在Chubby中的作用是实现分布式锁服务。Chubby是Google开发的分布式锁服务,它使用Paxos算法实现分布式锁的功能。Chubby将分布式锁抽象成一个状态机,并将其存储在Paxos协议中。当一个客户端需要获取锁时,它会向Chubby服务发送一个请求,请求锁的所有者节点。Chubby服务会将请求转发给Paxos协议,由Paxos协议选举出一个节点作为锁的所有者,并将该节点信息返回给客户端。当锁的所有者释放锁时,Paxos协议会重新选举一个新的锁的所有者。这样,就能够保证在分布式系统中,只有一个节点能够获取锁,从而实现了分布式锁的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值