一致性协议

此文知识来自于:《从Paxos到Zookeeper分布式一致性原理与实践》第二章
分布式入门基础知识,由于博主对其理解不深,只能生搬硬套,建议先直接看大牛的视频30分钟讲解,在本文末。


1. 2PC和3PC

在分布式系统总,每一个机器节点虽然都能够明确地知道自己在进行事务操作过程中的结果是成功或失败,但却无法直接获取到其他分布式节点的操作结果。
因此,当一个事务操作需要跨越多个分布式节点的时候,为了保持事务处理的ACID特性(某个节点为单位),就需要引入一个称为“协调者(Coordinator)”
的组件来统一调度所有分布式节点的执行逻辑,这些被调度的分布式节点被称为“参与者(Participant)”。

Coordinator负责调度Participant的行为,并最终决定这些Participant是否要把事务真正的提交。基于这个思想,衍生除了二阶段提交和三阶段提交两种协议,
在本节中,我们将重点对这两种分布式事务中涉及的一致性协议进行讲解。

1.1 2PC

2PC是Two-Phase Commit的缩写,即二阶段提交,是计算机网络尤其是在数据库领域内,为了使基于分布式系统架构下的所有节点在进行事务处理过程中能保持
原子性和一致性而设计的算法。

1.1.1 阶段一:提交事务请求

  1. 事务询问:协调者向所有的参与者发送事务内容,询问是否可以执行事务提交曹组,并开始等待各参与者的响应。
  2. 执行事务:各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中。
  3. 各参与者向协调者反馈事务询问的响应:如果参与者成功执行了事务操作,那么反馈给协调者Yes响应,表示事务可以执行;如果参与者没有成功执行事务,
    那么就反馈给协调者No响应,表示事务不可以执行。

由于上面讲述的内容在形式上近似是协调者组织各参与者对一次事务操作的投票表态过程,因此二阶段提交协议的阶段一页被称为“投票阶段”,即各参与者投票
表明是否要继续执行接下去的事务提交操作。

1.1.2 阶段二:执行事务提交

正常情况,包含以下两种可能:

.1 可能一:执行事务提交:假如协调者从所有的参与者的反馈都是Yes响应,那么就会执行事务提交。

.1.1 发送提交请求:协调者向所有参与者发出Commit请求。
.1.2 事务提交:参与者接收到Commit请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行期间占用的事务资源。
.1.3 反馈事务提交结果:参与者在完成事务提交之后,向协调者发送Ack消息。
.1.4 完成事务:协调者接收到所有参与者反馈的Ack消息后,完成事务。

.2 可能二:执行事务中断:假如任何一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。

.2.1 发送回滚请求:协调者向所有参与者节点发出Rollback请求。
.2.2 事务回滚:参与者接收到Rollback请求后,会利用其在阶段一记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源。
.2.3 反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送Ack消息。
.2.4 中断事务:协调者接收到所有参与者反馈的Ack消息后,完成事务中断。

以上就是二阶段提交过程中,前后两个阶段分别进行的处理逻辑。简单地讲,二阶段提交将一个事务的处理分成了投票和执行两个阶段,其核心是对每个事务
都采用了先尝试后提交的处理方式,因此也可以将二阶段提交看作一个强一致性的算法。

1.1.3 优缺点

原理简单,实现方便;但是同步阻塞、单点问题、数据不一致、太过保守。

.1 同步阻塞:在事务的执行过程中,所有参与该事务操作的逻辑都处于阻塞状态,也就是说,各个参与者在登台其他参与者响应的过程中,

将无法进行其他任何操作。

.2 单点问题:一旦协调者出现问题,整个二阶段提交流程将无法运转,更为严重的是,如果协调者是在阶段二中出现问题的话,

那么其他参与者将会一直处于锁定事务资源的状态中,而无法继续完成事务操作。

.3 数据不一致:当协调者向所有参与者发送Commit请求之后,发生了协调者在尚未发送完Commit请求之前自身发生了崩溃,

导致最终只有部分参与者收到了Commit请求。于是,其他没有收到Commit请求的参与者没有进行事务提交,而收到Commit请求的参与者会进行事务提交,最终数据不一致。

.4 太过保守:任何一个节点的失败都会导致整个事务的失败。

1.2 3PC

研究者在二阶段提交协议的基础上进行了改进,提出了三阶段提交协议。
3PC是Three-Phase Commit的缩写,将二阶段提交协议的“提交事务请求”过程分为两个,形成了CanCommit、PreCommit和DoCommit。

1.2.1 阶段一:CanCommit

.1 事务询问:协调者向所有的参与者发送一个包含事务内容的CanCommit请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应。

.2 各参与者向协调者反馈响应:如果自身可以顺序执行事务,反馈Yes响应,并进入预备状态,否则反馈No响应。

1.2.2 阶段二:PreCommit

.1 执行事务预提交:假如协调者从所有参与者获得的反馈都是Yes响应。

.1.1 发送预提交请求:协调者向所有参与者节点发出PreCommit的请求,并进入Prepared阶段。
.1.2 事务预提交:参与者接收到PreCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中。
.1.3 各参与者向协调者反馈事务执行的响应:如果参与者成功执行了事务操作,那么就会反馈给协调者Ack响应,同时等待最终的指令:

提交(commit)或中止(abort)。

.2 中断事务:假如任何一个参与者向协调者反馈了No响应,或等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。

.2.1 发送中断请求:协调者向所有参与者节点发出abort请求。

.2.2 中断事务:无论是收到来自协调者的abort请求,或者是在等待协调者请求过程中出现超时时,参与者都会中断事务。

1.2.3 阶段三:DoCommit

.1 可能一:执行提交

.1.1 发送提交请求:协调者从“预提交”状态转换到“提交”状态,并向所有的参与者发送DoCommit请求。
.1.2 事务提交:参与者接收到DoCommit请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源。
.1.3 反馈事务提交结果:参与者在完成事务提交之后,向协调者发送Ack消息。
.1.4 完成事务:协调者接收到所有参与者反馈的Ack消息后,完成事务。

.2 可能二:中断事务

.2.1 发送中断请求:协调者向所有参与者节点发送abort请求。
.2.2 事务回滚:参与者接收到abort请求后,利用Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源。
.2.3 反馈事务回滚结果:参与者在完成事务回滚之后,向协调者发送Ack消息。
.2.4 中断事务:协调者接收到所有参与者反馈的Ack消息后,中断事务。

需要注意的是,一旦进入阶段三,可能会存在以下两种故障:

  • 协调者出现问题。
  • 协调者和参与者之间的网络出现故障。
    无论出现哪种情况,参与者都会在等待超时之后,继续进行事务提交。即,默认为允许提交。

1.2.3 优缺点

降低参与者的阻塞范围,出现单点故障后继续达成一致;但是在参与者接收到PreCommit消息后,如果协调者所在的节点和参与者无法正常通信,
该参与者仍然会进行事务的提交,这必然出现数据不一致性。

2. Paxos算法

我们将重点讲解另一种非常重要的分布式一致性协议:Paxos。Paxos算法是一种基于消息传递且具有高度容错特性的一致性算法,是目前公认的解决分布式
一致性问题最有效的算法之一。

我们现在已经知道,在常见的分布式系统中,总会发生诸如机器宕机或网络异常等情况。Paxos算法需要解决的问题就是如何在一个可能发生上述异常的分布式系统中,
快速且正确地在集群内部对某个数据的值达成一致,并且保证不论发生以上任何异常都不会破坏整个系统的一致性。

2.1 追本溯源

1982年,Lamport与另两人提出了一种计算容错理论。在理论描述过程中,为了将要所描述的问题形象的表达出来,Lamport设想出了下面这样一个场景:

拜占庭帝国有许多支军队,不同军队的将军之间必须制定一个统一的行动计划,从而做出进攻或者撤退的决定,同时,各个将军在地理上都是被
分割开来的,只能依靠军队的通讯员来进行通讯。然而,在所有的通讯员中可能会存在叛徒,这些叛徒可以任意篡改消息,从而达到欺骗将军的目的。

这就是著名的“拜占庭将军问题”。从理论上来说,在分布式计算领域,试图在异步系统和不可靠的通道上来达到一致性状态是不可能的,因此在堆一致性的研究
过程中,都往往假设信道是可靠地。而事实上,大多数系统都是部署在同一个局域网中的,因此消息被篡改的情况非常罕见,另一方面,由于硬件和网络原因而
造成的消息不完整问题,只需一套简单的校验算法即可避免——因此,在实际工程实践中,可以假设不存在拜占庭问题,也即假设所有消息都是完整的,没有被
篡改的。那么,在这种情况下需要什么样的算法来保证一致性呢?

Lamport在1990年提出了一个理论上的一致性解决方案,同时给出了严格的数学证明。鉴于之前采用故事类比的方式成功的阐述了“拜占庭将军问题”,因此这次Lamport
同样用新娘库地设想除了一个场景来描述这种一致性算法需要解决的问题,及其具体的解决过程:

在古希腊有一个叫Paxos的小岛,岛上采用议会的形式来通过法令,议会中的议员通过信使进行消息的传递。值得注意的是,议员和信使都是兼职的,
他们随时有可能会离开议会厅,并且信使可能会重复的传递消息,也可能一去不复返。因此,议会协议要保证在这种情况下法令仍然能够正确的产生,
并且不会出现冲突。

这就是兼职议会,而Paxos算法名称的由来也是取自提到的Paxos小岛。

2.2 Paxos算法详解

Paxos作为一种提高分布式系统容错性的一致性算法,一直以来总是被很多人抱怨其算法理论太难理解。

2.2.1 问题描述:

假设有一组可以提出提案的进程集合,那么对于一个一致性算法来说需要保证以下几点:

  • 在这些被提出的提案中,只有一个会被选中。
  • 如果没有提案被提出,那么就不会有被选定的提案。
  • 当一个提案被选定后,进程应该可以获取被选定的提案信息。

对于一致性来说,安全性需求如下:

  • 只有被提出的提案才能被选定。
  • 只能由一个值被选定。
  • 如果某个进程认为某个提案被选定了,那么这个提案必须是真的被选定的那个。

在对Paxos算法的讲解过程中,我们不去精确地定义其活性需求,从整体上来说,Paxos算法的目标就是要保证最终有一个提案会被选定,当提案被选定后,
进程最终也能获取到被选定的提案。

在该一致性算法中,有三种参与角色,我们用Proposer、Acceptor、Learner来表示,在具体的实现中,一个进程可能充当不止一种角色,在这里我们并
不关心进程如何映射到各种角色。假设不同参与者之间可以通过收发消息来进行通信,那么:

  • 每个参与者以任意的速度执行,可能会因为出错而停止,也可能会重启。同时,即使一个提案被选定后,所有的参与者也都有可能失败或重启,因此除非
    哪些失败或重启的参与者可以记录某些信息,否则将无法确定最终的值。
  • 消息在传输过程中可能会出现不可预知的延迟,也可能会重复或丢失,但是消息不会被损坏。

2.2.2 提案的选定

要选定一个唯一提案的最简单方式莫过于只允许一个Accpetor存在,这样的话,Proposer只能发送提案给该Accpetor,Acceptor会选择它接收到的第一个
提案作为被选定的提案。这种解决方式尽管实现起来非常简单,但是却很难让人满意,因为一旦这个Accpetor出现问题,那么整个系统就无法工作了。

因此,应该寻找一种更好的解决方式,例如可以使用多个Acceptor来避免Accpetor的单点问题。现在我们就来看看,在存在多个Acceptor的情况下,如何
进行提案的选取:Proposer向一个Acceptor集合发送提案,同样,集合中的每个Acceptor都可能会批准该提案,当有足够多的Acceptor批准这个提案的时候,
我们就可以认为该提案被选定了。那么,什么是足够多呢?我们假定足够多的Acceptor是整个Acceptor集合的一个子集,并且让这个集合大得可以包含Acceptor
集合中的大多数成员,因为任意炼哥包含大多数Acceptor的子集至少有一个公共成员。另外我们再规定,每一个Acceptor最多只能批准一个提案,那么就能
保证只有一个提案被选定了。

2.2.3 推导过程

在没有失败和消息丢失的情况下,如果我们希望即使在只有一个提案被提出的情况下,仍然可以选出一个提案,这就暗示了如下的需求。

P1:一个Acceptor必须批准它收到的第一个提案。

上面这个需求就引出了另外一个问题:如果有多个提案被不同的Proposer同时提出,这可能会导致虽然每个Acceptor都批准了它收到的第一个提案,但是没有一个
提案是由多数人批准的。可能会现以下两种情况


Acceptor接收的提案数量相同,此时无法选定最终的提案了。
因此,在P1的基础上,需要再加上一个提案被选定需要由半数以上的Acceptor批准的需求暗示着一个Acceptor必须能够批准不止一个提案。在这里,我们使用一个全局的编号
(这种全局唯一编号的生成并不是Paxos算法需要关注的地方,就算法本身而言,其假设当前已经具备这样的外部组件能够生成一个全局唯一的编号)来标识每一个
被Acceptor批准的提案,当一个具有某Value值的提案被半数以上的Acceptor批准后,我们就认为该Value被选定了,此时我们也认为该提案被选定了。需要注意的是,
此处讲到的提案和Value不是同一个概念了,提案变成了由编号和Value组成的组合体,因此我们以“[编号,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还未收到任何提案时就被选定了。


如上图,在Acceptor1没有接收到任何提案的情况下,其他4个Acceptor已经批准了来自Proposer2的提案[M0,V1],而此时,Proposer1产生了一个具有其他Value值的、
编号更高的提案[M1,V2],并发送给了Acceptor1。根据P1,就需要Acceptor1批准该提案,但是这与P2a矛盾,因此如果要同时满足P1和P2a,需要对P2a进行如下强化:

P2b:如果一个提案[M0,V0]被选定后,那么之后任何Proposer产生的编号的提案,其Value值都为V0。

因为一个提案必须在被Proposer提出后才能被Acceptor批准,因此P2b包含了P2a,进而包含了P2。于是,接下去的重点就是论证P2b成立即可:

假设某个提案[M0,V0]已经被选定了,证明任何编号Mn > M0的提案,其Value值都是V0。

2.2.4 数学归纳法证明

略过。

2.2.5 Proposer生成提案

对于一个Proposer来说,获取哪些已经被通过的提案远比预测未来可能会被通过的提案来得简单。因此,Proposer在产生一个编号为Mn的提案时,
必须要知道当前某一个将要或已经被半数以上Acceptor批准的、编号小于Mn但为最大编号的提案。并且,Proposer会要求所有的Acceptor都不要
再批准任何编号小于Mn的提案——这就引出了如下的提案生成算法。

.1 Proposer选择一个新的提案编号Mn,然后向某个Acceptor集合的成员发送请求,要求该集合中的Acceptor做出如下回应。

  • 向Proposer承诺,保证不再批准任何编号小于Mn的提案。
  • 如果Acceptor已经批准过任何提案,那么其就向Proposer反馈当前该Acceptor已经批准的编号小于Mn但为最大编号的那个提案的值。

我们将该请求称为编号为Mn的提案的Prepare请求。

.2 如果Proposer收到了来自半数以上的Acceptor的响应结果,那么它就可以产生编号为Mn、Value值的Vn的提案,这里的Vn是所有响应中编号最大的提案的Value值。

当然还存在另一种情况,就是半数以上的Acceptor都没有批准过任何提案,即响应不包含任何的提案,那么此时Vn值就可以
由Proposer任意选择。

在确定提案之后,Proposer就会将该提案再次发送给某个Acceptor集合,并期望获得它们的批准,我们称此请求为Accept请求。需要注意的一点是,
此时接受Accept请求的Acceptor集合不一定是之前响应Prepare请求的Acceptor集合——这点相信读者也能够明白,任意两个半数以上的Acceptor集合,必定
包含至少一个公共Acceptor。

2.2.6 Acceptor批准提案

在上文中,我们已经讲解了Paxos算法中Proposer的处理逻辑,下面我们来看看Acceptor是如何批准提案的。

根据上面的内容,一个Acceptor可能会收到来自Proposer的两种请求,分别是Prepare请求和Accept请求,对这两类请求做出相应的条件分别如下。

  • Prepare请求:Acceptor可以在任何时候响应一个Prepare请求。
  • Accept请求:在不违背Accept现有承诺的前提下,可以任意响应Accept请求。因此,对Acceptor逻辑处理的约束条件,大体可以定义如下。

P1a:一个Acceptor只要尚未响应过任何编号大于Mn的prepare请求,那么它就可以接受这个编号为Mn的提案。

从上面这个约束条件中,我们可以看出,P1a包含了P1。同时,值得一提的是,Paxos算法允许Acceptor忽略任何请求而不用担心破坏其算法的安全性。

2.2.7 算法优化

在上面的内容中,我们分别从Proposer和Acceptor对提案的生成和批准两方面来讲解了Paxos算法在提案选定过程中的算法细节,同时也在提案的编号全局唯一
的前提下,获得了一个满足安全性需求的提案选定算法,接下来我们再对这个初步算法做一个小优化。尽可能地忽略Prepare请求:

假设一个Acceptor收到了一个编号为Mn的prepare请求,但此时该Acceptor已经对编号大于Mn的prepare请求做出了响应,因此它肯定不会再批准
任何新的编号为Mn的提案,那么狠显然,Acceptor就没有必要对这个Prepare请求做出响应,于是Acceptor可以炫册忽略这样的Prepare请求。同时
Acceptor也可以忽略掉那些它已经批准过的提案的Prepare请求。

通过这个优化,每个Acceptor只需要记住它已经批准的提案的最大编号以及它已经做出Prepare请求响应的提案的最大编号,以便在出现故障或节点重启的情况下,
也能保证P2c的不变性。而对于Proposer来说,只要它可以保证不会产生具有相同编号的提案,那么就可以丢弃任意的提案以及它所有的运行时状态信息。

2.2.8 算法陈述

.1 阶段一:

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

举个例子来说,假定一个Acceptor已经响应过的所有Prepare请求对应的提案编号分别为1、2、...、5和7,那么该Acceptor在接收到一个编号为8的
Prepare请求后,就会将编号为7的提案作为响应反馈给Proposer。

.2 阶段二:

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

当然,在实际运行过程中,每一个Proposer都有可能会产生多个提案,但只要每个Proposer都遵循如上所述的算法运行,就一定能够保证算法执行的正确性。
值得一提的是,每个Proposer都可以在任意时刻丢弃一个提案,哪怕针对该提案的请求和响应在提案被丢弃后会到达,但根据Paxos算法的一系列规约,依然可以保证
其在提案选定上的正确性,事实上,如果某个Proposer已经在试图 生成编号更大的提案,那么丢弃一些旧的提案未尝不是一个好的选择。
因此,如果一个Acceptor因为已经收到过更大编号的Prepare请求而忽略某个编号更小的Prepare或者Accept请求,那么它也应当通知其对应的Proposer,
以便该Proposer也能够将该提案进行丢弃——这和上面“算法优化”部分中提到的提案丢弃是一致的。

2.2.9 提案的获取

在上文中,我们已经介绍了如何来选定一个提案,下面我们再来看看如何让Learner获取提案,大体可以有以下几种方案。

.1 方案一:

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

.2 方案二:

另一种可行的方案是,我们可以让所有的Acceptor将它们对提案的批准情况,统一发送给一个特定的Learner(下文中我们将这样的Learner称为“主Learner”),
在不考虑拜占庭奖金问题的前提下,我们假定Learner之间可以通过消息通信来互相感知提案的选定情况。基于这样的前提,当主learner被通知一个提案
已经被选定时,它会负责通知其它的Learner。

在这种方案中,Acceptor首先会将得到批准的提案发送给主Learner,再由其同步给其他Learner,因此较方案一而言,方案二虽然需要多一个步骤才能将
提案通知到所有的Learner,但其通信次数却大大减少了,通常只是Acceptor和Learner的个数总和。但同时,该方案引入了一个新的不稳定因素:主Learner随时可能出现故障。

.3 方案三:

在讲解方案二的时候,我们提到,方案二最大的问题在于主Learner存在单点问题,即主Learner随时可能出现故障。因此,对方案二进行改进,可以将主Learner的范围扩大,
即Acceptor可以将批准的提案发送给一个特定的Learner集合,该集合中的每个Learner都可以在一个提案被选定后通知所有其他的Learner。
这个Learner集合中的Learner个数越多,可靠性就越好,但同时网络通信的复杂度也就越高。

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

根据前面的内容坚决,我们已经基本了解Paxos算法的核心逻辑,下面我们再来看看Paxos算法在实际运作过程中的一些细节。假设存在这样一种极端情况,
有两个Proposer依次提出了一系列编号递增的议案,但是最终都无法被选定,具体流程如下:

Proposer P1提出了一个编号为M1的提案,并完成了上述阶段一的流程。但与此同时,另外一个Propoesr P2提出了一个编号为M2的提案,同样也完成了
阶段一的流程,于是Acceptor已经承诺不再批准编号小于M2的提案了。因此,当P1进入阶段二的时候,其发出的Accept请求将被Acceptor忽略,
于是P1再次进入阶段一并提出了一个编号为M3的提案,而这又导致P2在第二阶段的Accept请求被忽略,以此类推,提案的选定过程将陷入死循环。

为了保证Paxos算法流程的可持续性,以避免陷入上述提到的“死循环”,就必须选择一个主Proposer,并规定只有主Proposer才能提出议案。这样一来,
只要主Proposer和过半的Acceptor能够正常进行网络通信,那么但凡主Proposer提出一个编号更高的提案,该提案终将会被批准。当然,如果Proposer发现当前
算法流程中已经有一个编号更大的提案被提出或正在接受批准,那么它会丢弃当前这个编号较小的提案,并最终能够选出一个编号足够大的提案。因此,
如果系统中有足够多的组件(包括Propsoer、Acceptor和其他网络通信组件)能够正常工作,那么通过选择一个主Proposer,整套Paxos算法流程就能够保持活性。

3 小结

2PC和3PC:1.牧师分别问新郎和新娘:你是否愿意……不管生老病死……(投票阶段)。
2.当新郎和新娘都回答愿意后(锁定一生的资源,只要有一个没有反应,这场结婚就失败)。(投票阶段)
3.牧师就会说:我宣布你们……(执行阶段)。
存在的问题:1.阻塞问题:如果新郎回答原意,新娘没反应,则整个结婚就阻塞。(投票阶段之后增加眼神交流阶段(3PC的额外阶段),之后才真正承诺一生一世不分离即锁定资源)。
2.单点问题:如果牧师没反应,整个结婚就失败。(3PC的超时机制,给牧师5秒反应时间)

主要从协议设计和原理实现角度详细讲解了二阶段提交协议、三阶段提交协议和Paxos这三种典型的一致性算法。其中二阶段提交协议解决了分布式事务的原子性问题,
保证了分布式事务的多个参与者要么都执行成功,要么都执行失败。但是,在二阶段解决部分分布式事务问题的同时,依然存在一些难以解决的诸如同步阻塞、
无限期等待问题。三阶段提交协议则是在二阶段提交协议的基础上,添加了PreCommit过程,从而避免了二阶段提交协议中的无限期等待问题。而Paxos算法支持
分布式节点角色之间的轮换,这极大地避免了分布式单点的出现,因此Paxos算法既解决了无限期等待问题,是目前来说最优秀的分布式一致性协议之一。
传送门,视频30分钟讲解paxos的演变

推荐阅读更多精彩内容

  • 一致性的问题定义 多个副本如何如何保存一致的数据换句话说就是多个进程就一个值达成一致,达成一致之后,这个值是可以被...
    在秋名山跑步阅读 243评论 0 0
  • Paxos是什么 Paxos算法是基于消息传递且具有高度容错特性的一致性算法,是目前公认的解决分布式一致性问题最有...
    jiangmo阅读 638评论 0 6
  • gossip协议 我们的办公室八卦一般都是从一次交谈开始,只要一个人八卦一下,在有限的时间内办公室的的人都会知道该...
    jsondream阅读 1,246评论 0 5
  • Paxos算法在分布式领域具有非常重要的地位。但是Paxos算法有两个比较明显的缺点:1.难以理解 2.工程实现更...
    Jeffbond阅读 11,782评论 21 86
  • 《三国演义》里有很多牛人,我最欣赏的牛人还是曹公。曹公何许人也?当然是曹操了。按驼庵居士顾随的说法,在文学史上,被...
    皖水畔人阅读 345评论 0 0