分布式中的一致性算法:Paxos协议

Paxos是什么?

Paxos算法是一种基于消息传递且具有高度容错特性的一致性算法。Chubby的设计开发者Burrows曾说过,所有一致性协议本质上要么是Paxos要么是其变体。

Paxos用来解决什么?

在常见的分布式系统中,总是会发生诸如机器宕机或者是网络异常这样的情况,就会导致消息发送失败,消失的消息重复。Paxos算法主要是解决这些问题,就是在分布式环境中,如何快速的在集群内部的某个值的某个数据的是达成一致,保证整个系统的一致性。

Paxos的提出

Lamport在1990年提出了一个理论上的一致性解决方案,同时给出了严谨的数学证明。为描述该算法,他虚拟了一个叫做Paxos的希腊小岛,岛上采用一会的形式来通过法令,议会中的议员通过信使进行消息的传递,而议员和信史都是兼职的,他们随时有可能离开议会厅,并且信史可能会重复的传递消息也可能一去不复返,此处假设没有拜占庭将军问题,即消息不会被篡改,在这样的环境下,通过议会协议仍然能保证有法令会被通过,并被所有议员所接受。

Paxos的原理及内容

什么是通过Paxos达到一致性?通过一个大学寝室中的例子来理解一下。
寝室里的四个室友想在下午进行一个集体活动,所以他们需要对做什么达成一致。
室友A首先提出来:“咱们去看电影吧!”
室友B觉得不错,附和道:“嗯,可以,去看电影吧”
这时,正在打游戏的室友C没听到2人的对话,突然说:“咱们去吃饭吧。”
室友D表示很赞同:“对,咱吃饭去吧”
为了达成一致,有的人就得改变自己的想法,于是室友B先动摇了,室友A一看,得,少数服从多数,咱就吃饭去吧。这样,四个室友达成了一致。
在这里插入图片描述
在这里我们可以看到以下几点,

  • 一致性就是得到一个唯一的结果。
  • 一致性遵从少数服从多数的原理。
  • 最终每个人都知道达成一致的是什么。
  • 大家可能会对任意一个结果达成一致,而不一定是他们的提议。
  • 交流之间可能会出现错误,即消息可能会丢失。

为什么系统需要一致性?
假设你是在家里工作,你搭建了一个服务器,并把它做成开放的,别人会来访问它,看起来很不错。但随着越来越多的人使用,你的服务器越来越慢,你的资源不够用了,所以你升级了RAM和CPU,但相同的事情还是发生了,因为对于一个单独的机器是没有无穷的资源的。
所以你得想点别的办法
在这里插入图片描述
方法一是主副本模式,其中一个节点是主节点,其余的则是副本。一旦有人想写一个值,他会告诉主节点,主节点会把不同的提议进行序列化,并把它们传递给副本,这样数据就会写入每一个节点。
或者你可以考虑点对点的模式,这里所有的节点都一样。当它们想要提出一个新的值,它们就把提议传递给其他节点,去对下一项议题达成一致,然后每个节点都会对此作出响应。
对于第一种模式,如果主节点出了问题,就需要在副本中再选一个主节点,否则整个系统就会崩溃。第二种模式下,所有节点必须连续不断地达成一致,以保证所有的操作在任何地方都是一致的。

下面我们来看一下Paxos的基本原理
首先了解一下Paxos中的基本概念
ID:相互不冲突的提案编号
Value:提案的值
Proposer:提案者,提案的内容为:ID+value,对同一轮提案,最多提议一个value 。Acceptor:倡议者,有 N 个。Proposer 提出的 x=value 提案必须获得超过半数(N/2+1)的 Acceptor接受后才能被接受。
Learner:提案学习者。一个提案超过半数Accpetor通过即可被接受,其他未确定的Acceptor可以通过learner来同步结果。

具体流程

预提案阶段
提案者视角:Proposer选择一个提案编号n,向所有的Acceptor广播Prepare n请求。
倡议者视角:Acceptor收到Prepare n请求,若提案编号n比之前接收的请求都要大,则承诺将不会接收提案编号比n小的提议,并且附上之前Accept的提议中编号小于n的最大的提案的value。如果n比之前接受的提案编号小,则不予理会。

提案阶段
提案者视角:Proposer收到Acceptor的承诺:
如果未超过半数的Accpetor回复承诺,本次提案失败;
如果超过半数的Acceptor回复承诺,又分为不同情况:
如果(回复承诺的)所有Acceptor都未接收过value,那么向所有的Acceptor发送自己的value和提案编号n。
如果有部分Acceptor接收过value,那么从接受过的value中选择提案编号最大的value作为本次提案的value,提议编号仍然为n
倡议者视角:Acceptor接收到提议后,如果该提案编号不等于自身当前承诺的编号),不接受该请求,相等则将提案的value写入本地。

现在我们来讨论一下具体的细节,首先是多数承诺。对应2f+1个Acceptor需要其中f个的承诺即可。
假设我们有2个Proposer和3个Acceptor。
在这里插入图片描述
Proposer1向3个Acceptor发送了PREPARE 5,并且得到了Acceptor1的承诺。同时,Proposer2向3个Acceptor发送了消息PERPARE 4。此时Proposer1得到了一个PROMISE 5,即得到了大多数的承诺,这样,PREPARE 4就将被Acceptor忽视,即便PREPARE 4被其中一个Acceptor所接受(出于时间延迟),Proposer1仍将继续收到REQUEST 5。

的确,你可能会发现这里可能会出现竞争。
在这里插入图片描述
例如Proposer1可能会发送一个PREPARE 5给大多数的Acceptor,它们可能会对此作出承诺。Proposer2同时发送一个PREPARE 6的消息,也得到了多数Acceptor的承诺,这样一来PREPARE 5就会被忽视。Proposer1可能会尝试一个更高的ID,比如PREPARE 7,得到大多数Acceptor的承诺,于是ID 6的提案就会被忽视。Proposer2可能也会再尝试一个更高的ID。几组这样的提案会引起产生争论的热点。
一个可能的解决方案是设定一个指数回退,这将给出足够的时间使得任意一个提案被完整的进行,可能Proposer1就会等待Proposer2把ID 8的ACCEPT REQUEST处理完,这样达成一致,Paxos进程结束。

多数接受
在这里插入图片描述
Proposer1发送一个PREPARE 5,值为cat的消息给大多数的Acceptor,它们会对此作出承诺。Proposer2同时发送一个PREPARE 6的消息,也得到了多数Acceptor的承诺,其值仍将是cat。

发布了28 篇原创文章 · 获赞 2 · 访问量 3259

猜你喜欢

转载自blog.csdn.net/Maestro_T/article/details/94336416