分布式算法,不得不提paxos。它是目前公认的解决分布式共识问题最有效的算法之一,甚至可以说过去几十年里一切分布式一致性算法都来源于它。
那么要学习paxos,我们首先得认识它。一般描述它,都会包含两个词:分布式容错、分布式共识算法。那么它们是指什么呢?paxos又解决了什么样的问题呢?
理解共识可能要难一点,那么我举个例子。假如有A、B两个客户端,他们都对同一个X进行赋值。A需要设置X=1,B需要设置X=2。那么让A和B都认同值X=1或者X=2的过程,就是达成共识。
带着这个问题,来聊聊如何达成共识呢?
paxos为了帮我们理解,抽象出三个角色和两个阶段
在描述这些角色之前,我们需要先了解什么提案。在paxos算法中提案是指需要达成共识的某一个值,或者某一个操作。paxos对其封装成一个提案,并为其生成唯一的提案编号。本文中使用M, V表示一个提案,其M表示提案编号,V表示需要达成共识的值。
proposer的工作在于接收客户端请求,将其封装成提案(proposal)。并将提案(proposal)发给所有的接受者(acceptor)。根据接受者(acceptor)返回情况,控制是否需要提交该提案(proposal)即保存该提案(proposal)。
acceptor的工作在于参与对提案(proposal)的投票,接收和处理paxos的两个阶段的请求。
learner不参与提案和投票,只被动接收提案结果。他的存在可用于扩展读性能,或者跨区域之间读操作。
因为他们不投票,所以他们不是paxos集群的重要组成部分。因此,它们可以失败,或者与集群断开连接,而不会损害paxos服务的可用性。对用户的好处是learner相比acceptor来说更能通过不太可靠的网络链接进行连接。实际上,learner可用于与另一个数据中心的paxos服务器通信,并且写入消耗最小的网络流量,因为在没有投票协议的情况下所需的消息数量较少。
prepare阶段,由proposer向acceptor发送prepare请求,acceptor根据约定决定是否需要响应该请求。如果acceptor通过提案M, 的准备请求,则向proposer保证以下承诺
其中prepare阶段还得注意,在prepare请求中,proposer只会发提案编号,也就是M, 。
accept阶段,在proposer在prepare阶段收到大多数响应后,由proposer向acceptor发送accept请求。例如此时进行决策的提案是M, V,根据acceptor在prepare阶段对proposer的承诺。
proposer会统计收到的accept请求的响应,如果响应中的编号等于自己发出的编号,则认为该acceptor批准过了该提案。如果存在大多数acceptor批准了该提案,则记作该提案已达成共识,或者记作提案已被批准。如果没有大多数acceptor批准该提案,则重新回到prepare阶段进行协商。
其中accept阶段也有注意的地方,在prepare请求中,proposer只会发提案M, 。而accept请求,proposer会发送提案编号和提案值,也就是M, V。这里要注意的是V的值,如果在prepare请求的响应中,部分acceptor已经批准过的提案值,则V为prepare请求的响应中编号最大的提案值,否则可以由proposer任意指定。
learn阶段,在某一个提案通过paxos达成共识之后,由acceptor通知learner学习提案结果。
该小节分为两部描述:提案选定、提案获取。
先来看一张非常流行的图,它用伪代码描述了paxos的过程。我预先描述下几个变量。
当然,实际运行过程中,没有以上陈述的那么理想。真实情况下,每一个proposer都有可能产生多个提案,但只要每个proposal遵循如上算法运行,就一定能保证执行正确性。文章后续我们会对多提案提出的情况进行模拟,并详细讲解。
在一个提案达成共识后,如何让learner获取该提案也是一个值得细究的问题。一般有以下几种方案。
为了更好的熟悉paxos,我们举例描述paxos中提案选定过程。假设存在3节点的paxos集群,这里需要注意每一个节点可以同时扮演proposer和acceptor。情况如下
proposerA收到请求将X设置成3,proposerB收到请求将X设置成5。proposerA和proposerB分别为此生成提案,其proposerA的提案编号为1,proposerB提案编号为2。在prepare阶段它们交互结果如下
至此,proposerA获得2个prepare响应,proposerB获得三个prepare
响应。即他们都获得了大多数节点的prepare响应,于是各自开始accept阶段提交。
以上过程的主要描述了accept对proposer的两个承诺,即如果acceptor通过提案M, 的准备请求
那么还有一个承诺是
为了描述该承诺,我们想象出这样一个场景。proposeB完成prepare请求后,发起accept请求,且提案为3, 6。在此过程中,proposeA发起prepare请求,提案编号为4, ,并且acceptor先收到proposeA发起prepare请求,也就是说acceptor会拒绝proposeB的accept请求。情况如下
原始的paxos算法(Basic Paxos),只能完成一个值的共识。Lamport宗师提到可以通过执行多次basic-paxos实现一系列值的共识。但是由于多次协商会增加通信以及影响协商的活性(指协商进入死循环)。
宗师则提出multi-paxos的解决方案,但是由于宗师并没有把multi-paxos讲清楚,只是介绍了大概的思想,缺少算法过程必要细节。所以这给了我们很大的想象空间,因此每个人实现的multi-paxos都有所差异。
总体来说multi-Paxos基于basic-paxos做了两点改进:
首先multi-Paxos是要选举一个leader的,宗师提到:可以通过basic-paxos进行leader的选举。
multi-paxos通过改变prepare阶段的作用范围至后面leader提交的所有实例,从而使得leader的连续提交只需要执行一次prepare阶段,后续只需要执行accept阶段,将两阶段变为一阶段,提高了效率。为了区分连续提交的多个实例,每个实例使用一个instance ID标识,instance ID由leader本地递增生成即可。
multi-paxos允许有多个自认为是leader的节点并发提交proposal而不影响其安全性,这样的场景即退化为basic-paxos。
怎么解决两个提案提出后陷入死循环?
---------------------------------------------------------
三个节点的集群A、B、C。
A和B通过提案[4, 6],C没有通过任何提案。
此时C收到客户端请求,将值设置为5,C生成的编号为5,通过basic-paxos后。
最后集群中通过的提案应该是多少呢?
---------------------------------------------------------
假如有三个提案者A, B, C,五个接受者SERV1, SERV2, SERV3, SERV4, SERV5
A提案编号为1,值为3
B提案编号为2,值为5
C提案编号为3,值为8
①A第一阶段发给SERV1, SERV2, SERV3获得回应,赢得半数选票
②B开始第一阶段,获得SERV3, SERV4, SERV5的选票
③A开始第二阶段,发给SERV1值为3的提案后,SERV1允许提交该值,此时A宕机
④B开始第二阶段,发给SERV5值为5的提案后,SERV5允许提交该值,此时B宕机
⑤C开始第一阶段,获得SERV1, SERV2, SERV5的选票,根据规定,它将收到SERV1接受的值为3,SERV5接受的值为5。
那C的第二阶段提案应该是[3, 3]还是[3, 5]呢?
----------------------------------------------------------
multi-paxos提出leader的角色,提案只能由leader提出,那么当写性能达到leader的瓶颈,怎么解决呢?
应该裂变分区,拆成多个不相干的分区,由多个paxos-group来完成之前一个leader的工作
参考文章:https://zhuanlan.zhihu.com/p/31780743。文中提到paxos推导过程,也可以了解下。
> 技术创作101训练营
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。