Paxos分布式一致性协议
为什么80%的码农都做不了架构师?>>>
源码:https://bitbucket.org/sciascid/libpaxos/src
1 前言
分布式系统中的节点通信存在两种模型:共享内存(Shared memory)和消息传递(Messages passing)。
基于消息传递通信模型的分布式系统,不可避免的会发生以下错误:进程可能会慢、垮、重启,消息可能会延迟、丢失、重复。
在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点都执行相同的操作序列,那么他们最后能得到一个一致的状态。
为保证每个节点执行相同的命令序列,需要在每一条指令上执行一个“一致性算法”以保证每个节点看到的指令一致。一个通用的一致性算法可以应用在许多场景中,是分布式计算中的重要问题。
因此从20世纪80年代起对于一致性算法的研究就没有停止过。
2 分布式一致性协议的目的
分布式一致性协议的目的是确定一个不可变变量分布式存储的取值。此取值可以是任何二进制数据,这个取值一旦最终确定将不再改变,并且可以被获取到。
此取值在分布式系统中采用多个副本的方式存储。为了简化问题的分析,可先不考虑可能出现消息篡改即拜占庭错误的情况;解决问题是在可能发生重启、延迟、消息丢失或重复、进程不可用等异常的分布式系统中如何就某个值达成一致,保证不论发生以上任何异常,都不会破坏取值的一致性。
3 一致性算法的回顾
3.1 互斥锁算法
3.1.1 算法角色定义
一、 提案委员:是指获取决议,产生提案,提交提案的成员。
二、 审批委员:是指接受或拒绝提案,或批准提案,或回复决议的成员。
3.1.2 算法原则
审批委员应用原则: a、锁定后就不再给其它提案委员分配访问权原则,b、永久保存已经批准的提案原则。
提案委员会应用原则: a、提案委员向审批委员先申请互斥锁,再提交提案,然后再释放互斥锁原则;b、值采用二阶段提交原则;
3.1.3 算法的内容
提案委员向审批委员申请一个互斥锁,锁定审批委员,审批委员只能接受由发放锁的提案委员提交的提案。
然后审批委员批准这个提案形成决议。形成决议后提案委员向审批委员申请释放这个互斥锁。审批委员没有互斥锁后其它的提案委员才能对它进行申请互斥锁。
这样就确定了一个不可变变量的取值。
3.1.4 算法的不足
如果提案委员已经申请到一个互斥锁,但在释放这个互斥锁之前故障,则系统就处于死锁状态。
因此,此算法不允许已经申请到锁的提案委员故障。
3.2 抢占锁算法
3.2.1 算法角色及定义
一、 提案委员:是指获取决议,产生提案,提交提案的成员。
二、 审批委员:是指接受或拒绝提案,或批准提案,或回复决议的成员。
三、 抢占锁:是指待竞争的资源根据竞争者的优先权剥夺资源的所有权,无须释放锁 操作;也称之为抢占式访问权。
3.2.2 算法原则
审批委员应用原则: a、喜新厌旧分配访问权原则(即抢占锁),b、值采用后者认同前者原则,c、永久保存已经批准的提案原则;。
提案委员应用原则: a、提案编号采用全局递增偏序编号原则;b、值采用二阶段提交原则;
3.2.3 算法的内容 “提案委员”在提出一个提案前,首先要发送自己的提案编号n给“审批委员”;提案委员向审批委员申请一个编号为n的访问权,审批委员只对比自己收到的申请编号x大的编号发放访问权;如果n大于x则此提案委员获得此审批委员的访问权;然后此提案委员将提案内容value提请审批委员批准,如果审批委员没有接受大于n的提案编号则批准这个提案从而形成决议。这样就确定了一个不可变变量的取值。
3.2.3.1 准备提案阶段
这一阶段达到二个目的,一是抢占访问权,二是获取历史提案信息。 “提案委员”在提出一个提案前,首先要发送自己的提案编号n给“审批委员”,则要么收到“最大提案的编号m及空提案”,要么收到一个“提案的编号及提案内容v”;
那么存在以下二种情况:
1、 如果收到的回复的提案编号要大于自己发送的提案编号;则抢占失败。区分以下三 种情况:A、如果收到回复的提案内容为空;则将收到的最大提案编号递增一,将“n+1”重新发送给“审批委员”。B、如果收到回复的提案内容不为空,则说明此时已经形成一个确定的一致的内容(此种情况也称之为“已经形成决议案”)那么返回给“提案委员”此决议案。以上B称之为“获取历史提案信息”。
2、 如果收到的回复的提案编号等于自己发送的提案编号,则抢占成功;这时“提案 委员”就可以进入“提交审批”阶段。 “审批委员”收到提案编号后存在如下二种情况: 1、如果“审批委员”收到其提案编号n大于已经记录的提案编号m;则“审批委员”将自己上次批准的提案回复(如果从未批准任何提案则回复空的提案内容)给“提案委员”,并记录n(将n替换m);而且承诺不再接受或批准小于提案编号n的提案。此过程称之为“抢占访问权”,也称之为已经取得抢占锁。 2、如果“审批委员”收到其提案编号n比自己已经接受提案的编号小或相等,将自己上次批准的提案的编号及提案内容回复给“提案委员”,如果从未批准任何提案则回复最大提案的编号及空提案内容给“提案委员”。
3.2.3.2 审批提案阶段
“提案委员”将抢占的提案编号n 和自己的提案内容发送给“审批委员会”;
“审批委员”只批准比自己已经接受的提案编号大的提案(此种情况称之为“审批成功”);
并承诺不再接受小于 n 的提案;
“审批委员”收到提案后,如果提案的编号大于等于它已经接受的所有提案编号,则 “审批委员”将批准此提案内容,并将此批准过的提案回复给“提案委员”。
如果提交审批的提案编号小于它已经接受的提案编号,则审批失败(注意“接受提案编号”失败不能称之为“审批失败”。)
如果“提案委员”收到审批失败(此种情况也称为“提案失败”),则将提案编号递增一,重新进入“准备提案阶段”。
3.2.4 算法的不足 此算法虽然可以容忍已经申请到访问权的“提案委员”故障,但却不能容忍“审批委员”故障。
3.3 PAXOS 算法
为描述 Paxos 算法,LESLIE Lamport 虚拟了一个叫做 Paxos 的希腊城邦,这个岛按照议会民主制的政治模式制订法律,
但是没有人愿意将自己的全部时间和精力放在这种事情上。所以无论是议员,议长或者传递纸条的服务员都不能承诺别人需要时一定会出现,
也无法承诺批准决议或者传递消息的时间。但是这里假设没有拜占庭将军问题(Byzantine failure,即虽然有可能一个消息被传递了两次,但是绝对不会出现错误的消息);
只要等待足够的时间,消息就会被传到。另外,Paxos岛上的议员是不会反对其他议员提出的决议的。
对应于分布式系统,议员对应于各个节点,制定的法律对应于系统的状态。各个节点需要进入一个一致的状态,
例如在独立Cache的对称多处理器系统中,各个处理器读内存的某个字节时,必须读到同样的一个值,
否则系统就违背了一致性的要求。一致性要求对应于法律条文只能有一个版本。议员和服务员的不确定性对应于节点和消息传递通道的不可靠性。
Paxos逻辑是对一个单独问题的独立的决策过程,实际的系统需要一系列的Paxos过程(即Multi-Paxos算法)来推动系统的运作。
3.3.1 算法的提出与证明
划分角色定义问题
首先将议员的角色分为 [提出提案者],[接受提案者],和 [学习决议者](允许身兼数职)。
[提出提案者] 提出提案,提案信息包括提案编号和提议的value;
[接受提案者]收到提案后可以接受(accept)提案,若提案获得多数 [接受提案者] 的接受,则称该提案被批准(chosen);
[学习决议者] 只能“学习”被批准的提案。
划分角色后,就可以更精确的定义问题:
1、决议(VALUE)只有在被 [提出提案者] 提出后才能被批准(未经批准的决议称为“提案(PROPOSAL)”);
2、在一次 PAXOS 算法的执行实例中,只批准(CHOSEN)一个 VALUE;
3、[学习决议者] 只能获得被批准(CHOSEN)的 VALUE。 作者通过不断加强上述3个约束(主要是第二个)获得了 Paxos 算法。
批准 value 的过程中,首先 [提出提案者] 将 value 发送给 [接受提案者],之后 [接受提案者] 对 value 进行接受(accept)。
为了满足只批准一个 value 的约束,要求经“多数派(majority)”接受的 value 成为正式的决议(称为“批准”决议)。
这是因为无论是按照人数还是按照权重划分,两组“多数派”至少有一个公共的 acceptor,如果每个 acceptor 只能接受一个 value,约束2就能保证。
于是产生了一个显而易见的新约束:
P1:一个 [接受提案者]必须接受(accept)第一次收到的提案。
注意 P1 是不完备的。如果恰好一半 acceptor 接受的提案具有 value A,另一半接受的提案具有 value B,那么就无法形成多数派,无法批准任何一个 value。
约束2并不要求只批准一个提案, 暗示可能存在多个提案。只要提案的 value 是一样的,批准多个提案不违背约束2。于是可以产生约束 P2:
P2:一旦一个具有 value v 的提案被批准(chosen),那么之后批准(chosen)的提案必须具有 value v。
注:通过某种方法可以为每个提案分配一个编号,在提案之间建立一个全序关系,所谓“之后”是指比所有编号更大的提案。
如果 P1 和 P2 都能够保证,那么约束2就能够保证。批准一个value意味着多个acceptor接受(accept)了该value.
因此,可以对P2 进行加强:
P2a:一旦一个具有 value v 的提案被批准(chosen),那么之后任何 [接受提案者] 再次接受(accept)的提案必须具有 value v。
由于通信是异步的,P2a 和 P1 会发生冲突。如果一个 value 被批准后,一个 proposer 和一个 acceptor 从休眠中苏醒,前者提出一个具有新的 new value 的提案。
根据 P1,后者应当接受,根据 P2a,则不应当接受,这中场景下 P2a 和 P1 有矛盾。于是需要换个思路,转而对 proposer 的行为进行约束:
P2b:一旦一个具有 value v 的提案被批准(chosen),那么以后任何 [提出提案者] 提出的提案必须具有 value v。
由于[接受提案者] 能接受的提案都必须由 [提出提案者] 提出,所以P2b 蕴涵了 P2a,是一个更强的约束。
但是根据 P2b 难以提出实现手段。因此需要进一步加强 P2b。
假设一个编号为 m 的 value v 已经获得批准(chosen),来看看在什么情况下对任何编号为 n(n>m)的提案都含有 value v。因为 m 已经获得批准(chosen),显然存在一个 [接受提案者] 的多数派 C,他们都接受(accept)了 v。
考虑到任何多数派都和 C 具有至少一个公共成员,可以找到一个蕴涵 P2b 的约束 P2c:
P2c:如果一个编号为 n 的提案具有 value v,那么存在一个多数派,要么他们中所有人都没有接受(accept)编号小于 n 的任何提案,要么他们已经接受(accept)的所有编号小于 n 的提案中编号最大的那个提案具有 value v。
可以用数学归纳法证明 P2c 蕴涵 P2b: 假设具有 value v 的提案 m 获得批准,当 n=m+1 时,采用反证法,假如提案 n 不具有 value v,而是具有 value w,根据P2c,则存在一个多数派S1,
要么他们中没有人接受过编号小于 n 的任何提案,要么他们已经接受的所有编号小于 n 的提案中编号最大的那个提案是 value w。
由于S1和通过提案m时的多数派C之间至少有一个公共的acceptor,所以以上两个条件都不成立,导出矛盾从而推翻假设,证明了提案 n 必须具有 value v;
若 (m+1)…(N-1) 所有提案都具有 value v,采用反证法,假如新提案 N 不具有 value v,而是具有 value w', 根据P2c,则存在一个多数派S2,要么他们没有接受过 m...(N-1) 中的任何提案,要么他们已经接受的所有编号小于 N 的提案中编号最大的那个提案是 value w'。
由于S2和通过 m 的多数派 C 之间至少有一个公共的 acceptor,所以至少有一个 acceptor 曾经接受了 m,从而也可以推出 S2 中已接受的所有编号小于 n 的提案中编号最大的那个提案的编号范围在 m…(N-1) 之间,而根据初始假设,m…(N-1)之间的所有提案都具有 value v,所以 S2 中已接受的所有编号小于 n 的提案中编号最大的那个提案肯定具有 value v,导出矛盾从而推翻新提案 n 不具有 value v 的假设。根据数学归纳法,我们证明了若满足P2c,则P2b一定满足。
P2c 是可以通过消息传递模型实现的。另外,引入了 P2c 后,也解决了前文提到的 P1 不完备的问题。
3.3.2 算法简述
要满足 P2c 的约束,[提出提案者]提出一个提案前,首先要和足以形成多数派的 [接受提案者]进行通信,获得他们进行的最近一次接受(accept)的提案(prepare 过程),之后根据回收的信息决定这次提案的 value,形成提案开始投票。
[提出提案者] 提出一个提案X(编号,内容),提交 [接受提案者]开始投票。当获得多数 [接受提案者] 接受(accept)后,提案获得批准(chosen),由 [提出提案者] 将这个消息告知 [学习决议者]。这个简略的过程经过进一步细化后就形成了 Paxos 算法。
在一个paxos实例中,每个提案需要有不同的编号,且编号间要存在全序关系。可以用多种方法实现这一点,例如将序数和 [提出提案者] 的编号拼接起来。
如何做到这一点不在 Paxos 算法讨论的范围之内。
如果一个最近一次接受(accept)的提案编号为 m 的[接受提案者]在投票过程中接受了另一个 [提出提案者] 针对提案 n (n > m)的问题,
但是在开始对 n 进行投票前,又接受(accept)了编号小于 n 的另一个提案(例如 n-1),如果 n-1 和 m 具有不同的 value,这个投票就会违背 P2c。
因此在投票过程中,[接受提案者]应不会再接受(accept)编号小于 n 的提案。这是对 P1 的加强:
P1a:当且仅当 [接受提案者] 没有接受过编号大于 n 的 [提出提案者]请求时,[接受提案者]接受(accept)编号为 n 的提案。
现在已经可以提出完整的算法了。
3.3.3 PAXOS算法内容
3.3.3.1 算法目标
对提交的多个提案只批准一个提案内容作为决议,并且此内容对大家是一致的不可更改的决议案。
3.3.3.2 算法角色定义
一、 提案委员:是指获取决议,产生提案,提交提案的成员。
二、 审批委员:是指或接受提案,或批准提案,或回复决议的成员。
三、 提案委员会:是指由多个提案委员组成的群体。
四、 审批委员会:是指由多个审批委员组成的群体。
3.3.3.3 算法原则
审批委员会应用原则:
a、喜新厌旧分配访问权原则,b、值采用后者认同前者原则,c、永久保存已经批准的提案原则。
提案委员会应用原则: a、提案编号采用全局递增偏序编号原则;b、少数服从多数接受原则; c、值采用二阶段提交原则;
3.3.3.4 算法过程
如何生成唯一的议案号,如何保证各个议案号的有序性,即如何比较他们的大小?
方案一:每个提案编号(比如分配一个编号M = N*i+x;N为“提案委员会”的委员总数,X为“提案委员”在委员会的序号,i为“提案委员”的第几次提议),每次他们都使i递增1哪么M就是当次的提案编号。
方案二:设提案编号为二元组序列(y,p);y为“提案委员”的第几次提议,p为“提案委员”的序列编号。在“审批委员”比较提案编号时先比较两者y的值,取y大的提案,如果y相等则比较两者p的值,取p大的提案。
方案三:设提案编号高位为时间戳低位为IP。
建议选方案三,方案一在扩编“提案委员会”时,要重新设n的值从而要重置所有的历史提案编号;
而方案二和方案三可以任意扩编“提案委员会”不会影响已经存在的其它“提案委员”的历史提案编号。
提案委员序号:是指“提案委员会”为“提案委员”分配的序列编号。 由提案到通过一个决议分为二个阶段:
3.3.3.4.1 准备提案阶段
这一阶段达到二个目的,一是抢占访问权,二是获取历史提案信息。
“提案委员”在提出一个提案前,首先要发送自己的提案编号n给“审批委员会”,则要么收到“最大提案的编号m及空提案”,
要么收到一个“提案的编号及提案内容v”(注意不一定是决议,因为有时此提案未形成多数派);
那么存在以下二种情况:
1、 如果收到多数派的回复的提案编号要大于自己发送的提案编号;则抢占失败。
区分以下三种情况:
A、如果收到全部回复的多数派提案内容为空;则将收到的最大提案编号递增一,将“n+1”重新发送给“审批委员会”。
B、如果收到回复的提案内容其中多数派不为空且内容不相同,则就收到的提案内容不为空的几个提案中,取最大提案编号的提案x,并将提案x(即“m,f”)的提案编号递增一即m+1,将自己的提案内容置为提案x的提案内容(即f);然后将“m+1,f”重新发送给“审批委员会”。
C、如果收到回复的提案内容其中多数派不为空且内容一样,则说明此时多数派已经形成一个确定的一致内容(此种情况也称之为“已经形成决议案”)那么返回给“提案委员”此决议案。
以上B和C统一称之为“获取历史提案信息”。
2、 如果收到多数派的回复的提案编号等于自己发送的提案编号,则抢占成功;这时“提案委员”就可以进入“提交审批”阶段。
“审批委员”收到提案编号后存在如下二种情况:
1、如果“审批委员”收到其提案编号n大于已经记录的提案编号m;则“审批委员”将自己上次批准的提案回复(如果从未批准任何提案则回复空的提案内容)给“提案委员”,并记录n(将n替换m);而且承诺不再接受或批准小于提案编号n的提案。此过程称之为“抢占访问权”。
2、如果“审批委员”收到其提案编号n比自己已经接受提案的编号小或相等,将自己上次批准的提案的编号及提案内容回复给“提案委员”,如果从未批准任何提案则回复最大提案的编号及空提案给“提案委员”。
3.3.3.4.2 审批提案阶段
“提案委员”将抢占的提案编号n 和自己的提案(包括编号 n 和根据 P2c 决定的内容)发送给“审批委员会”;
“审批委员”只批准比自己已经接受提案的编号大或等于的提案(此种情况称之为“审批成功”);并承诺不再接受小于 n 的提案;
“审批委员”收到提案后,如果提案的编号大于等于它已经接受的所有提案编号,则 “审批委员”将批准此提案内容(如果根据 P2c 没有已经批准的内容,那么该内容就是“提案委员”提案中的内容;如果已经批准过的内容为value0,那么该提案内容就是value0)并将此批准过的提案回复给“提案委员”;
如果提交审批的提案编号小于它已经接受的提案编号,则审批失败(注意“接受提案编号”失败不能称之为“审批失败”)。回复所接受的提案编号。
如果“提案委员”收到多数派审批失败(此种情况也称为“提案失败”),则将提案编号递增一,重新进入“准备提案阶段”。 如果“提案委员”收到多数派提案内容相同,则此决议案已经形成。
多数派审批失败然后将提案编号递增一,重新进入“准备提案阶段”此处存在“活锁”的问题。
解决“活锁”的问题有以下几种方法:
1、选举一个提案委员会的提案主管,所有的提案由此提案主管提交给“审批委员会”。
2、审批失败后延时以一定秒数(在实际应用中调整这个值)以内的随机秒数再递增编号与“审批委员会”提交提案。
3.3.4 PAXOS算法流程图
3.3.5 算法的不足
此算法虽然可以容忍已经申请到访问权的“提案委员”故障,可以容忍少数派“审批委员”故障;但在出现竞争的情况下,其收敛速度很慢,甚至可能出现活锁的情况,
例如当有等于或多于审批委员会数量的“提案委员”在同时发送提案请求后,很难有一个“提案委员”收到半数以上的回复而不断地执行第一阶段的协议。
3.4 MULTI-PAXOS 算法
Paxos算法只能从多个不同提案 value中确定一个一致的value,但实际应用中需要确定的是无限多个value(提供不间断服务,如log无限增长),
因此,每个value的确定需要一个Paxos 实例。多个实例之间不相干,可以并行进行。
当然每个实例也需要一个唯一的实例编号,实例编号按序分配并顺序写入log。
把Paxos每个两阶段提交过程:准备->承诺->提交->批准;称作一个回合,每个Paxos实例内又可能经过多个回合才达成一致。
多个paxos实例运行确认一系列的确定性值。这就是Multi-Paxos算法。 Paxos算法只能从多个不同提案 value中确定一个一致的value,确定之后就不能更改。
但实际应用中需要对这个值进行增加,删除等无限多次操作序列。这样就需要改造value。将value定义为操作一个值的操作序列。
这样就需要将value定义为(f(value))其中f为操作序列编号,然后再在此编号f增加一层属性,一个是记录增加操作(f 为奇数递增)次数的序号,一个是记录删除操作(f为偶数递增)的次数据的序号。
从外界来看Value这个值是否存在取决于f;如果f为奇数则Value这个值存在。如果f为偶数则value为空,也就是删除一个值的操作,Value值不存在。
因此由f取决的Value的值表示不存在,哪么在下一个Paxos实例中新的NewValue可以取代f为偶数的Value的值。
可以进一步抽象,将f和决议案编号合二为一;这样在决议案编号增加一层属性,称操作序号。
所决议案编号为奇数则为“增加操作”,决议案编号为偶数则为“删除操作”(如第一次进行“增加操作”为1,哪么第二次进行“删除操作”就为2,1奇数标识“增加操作”,2偶数标识“删除操作”,1,2又各为决议案的全局性序列编号)。
有人会说这个算法违反了Paxos算法的P2c约束,事实上P2c约束只是作用域于每个Paxos实例内;也就是说1操作为一个Paxos实例,而2操作为另外一个Paxos实例。
各个Paxos实例过程中的提案编号可以重复,但各个Paxos实例形成了决议编号是不能重复的。将决议定义为对某值的操作,一但形成一个决议案,也就是说对某个值的操作已经协商一致,然后执行这个操作(如将value值设为q),也就是对这个决议案的执行,这个决议案也就无法改变。
4 代理提交抢占式多数派一致性算法(PPMC)
此算法(PROXY SUBMISSION PREEMPTIVE MAJORITY GROUP CONSISTENCY ALGORITHM)是在PAXOS算法基础上增加了“立案委员”角色,并对处于竞争时由抢得提交权的“立案委员”领导代理提交提案;从而使其快速收敛(PAXOS FAST CONVERGENCE)以解决PAXOS算法的活锁问题。
4.1 算法角色及定义
一、 草案:草案是没有提案编号的提案
二、 提案:给草案编个提案号称之为提案。
三、 草案委员:是指需要获取决议案,起草草案并向立案委员提交草案的成员。
四、 立案委员:是指需要获取决议,接收草案并给草案编提案号从而形成提案,然后向审批委员提交提案的成员。
五、 审批委员:是指或接受提案,或批准提案,或回复决议的成员。
六、 立案委员会:是指由多个立案委员组成的群体。
七、 审批委员会:是指由多个审批委员组成的群体。
八、 立案委员编号:为每个立案委员进行编号形成立案委员编号。
4.2 算法原则
审批委员会应用原则: a、喜新厌旧分配访问权原则,b、值采用后者认同前者原则,c、永久保存已经批准的提案原则;。d、如果有竞争就以喜新厌旧分配提交权原则
立案委员会应用原则: a、提案编号采用全局递增偏序编号原则;b、少数服从多数原则; c、值采用二阶段提交原则;d、如果有竞争就转发提案到有提交权的立案委员代理提交的原则;
4.3 算法过程
如何生成唯一的提案编号请参考上述的Paxos协议关于提案编号的几种生成方法。
访问权其中包含两个权限:一是提交权;一是修改权。只有先得到提交权才能享有修改权(即提交自己的提案内容供审批者的审批,审批通过后才能成为审批者的提案内容)。
这两项权限都是通过提案编号来抢占获得或失去。 由草案到通过一个决议分为二个阶段:
4.3.1 准备提案阶段
这一阶段达到三个目的,一是抢占访问权,二是获取历史提案信息。三是有竞争就转发至有提交权的立案委员代理提交(即代理提交)。
“草案委员”将自己的草案发送给立案委员。立案委员收到草案后在提交提案前,首先要给该草案编号形成提案(令n号),然后将立案委员自己的委员号(令w号)与提案号(即n,w)发送给“审批委员会”,则要么收到“最大提案的编号、有提交权的立案委员号及空提案”,要么收到一个“提案的编号、有提交权的立案委员号及提案内容v”(注意不一定是决议,因为有时此提案未形成多数派);
那么就存在以下二种情况:
1、 如果收到多数派回复的提案编号要大于自己发送的提案编号;则抢占失败。
区分以 下二种情况:
A、 如果收到回复的提案内容其中多数派不为空;又分为二种情况: A1、如果其中提案内容相同,则说明此时多数派已经形成一个确定的一致的内容(此种情况也称之为“已经形成决议案”)那么将此决议案返回给“草案委员”。 A2、如果其中提案内容不相同,则说明没有形成决议案且存在竞争,将提案转发至大于自身的最大提案编号的立案委员。
B、 如果收到全部回复的提案内容多数派为空;又分为以下二种情况: B1、如果其中提案编号相同,说明另一个立案委员抢占提交权成功,将此提案转发至抢占成功的立案委员。 B2、如果其中提案编号不相同;则将提案转发至大于自身的最大提案编号的立案委员。
如果提案转发失败:删除此发送失败的立案委员编号及其提案编号,再取最大提案编号(大于自己的提案编号)的立案委员号为w1,如果此立案委员故障(即不可用),再删除此发送失败的立案委员编号,再取最大提案编号的立案委员号w2作为此提案的转发目标者,如果此w2还是故障,哪么再删再取之,如果最终找不到(比自己大的提案编号);立案委员则将收到的最大提案编号(假设为m)递增一,将“m+1”重新发送给“审批委员会”(再次抢占提交提案权),等待回复处理。转发提案后,接收提案的立案委员将领导提交提案。
2、 如果收到多数派的回复的提案编号等于自己发送的提案编号,则抢占成功;这时 “提案委员”就可以进入“提交审批”阶段。
提案内容设置:如果收到的提案内容不为空的几个提案中,取最大提案编号的提案x(令为“m,Vf”),将提案x的提案编号递增一(即m+1),将自己的提案内容置为提案x的提案内容(即Vf);然后将“m+1,Vf”作为自己提案。这称之为“获取历史提案信息”。
“审批委员”收到提案编号后存在如下二种情况:
1、如果“审批委员”收到其提案编号n大于已经记录的提案编号m;则“审批委员”将自己上次批准的提案和相应的立案委员编号回复(如果从未批准任何提案则回复的提案内容为空。)给“立案委员”,并记录n(将m替换为n);而且承诺不再接受或批准小于提案编号n的提案。此过程称之为“抢占访问权”。
2、如果“审批委员”收到其提案编号n比自己已经接受提案的编号小或相等,将自己上次批准的提案和立案委员编号回复给“立案委员”,如果从未批准任何提案则回复最大提案的编号、立案委员编号和空提案内容给“立案委员”。
4.3.2 审批提案阶段
“立案委员”将抢占的提案编号n 、立案委员编号和自己的提案内容(包括编号 n 和根据 P2c 决定的内容)发送给“审批委员会”;
“审批委员”只批准大于或等于自己已经接受的提案编号的提案(此种情况称之为“审批成功”);并承诺不再接受小于 n 的提案;
“审批委员”收到提案后,如果提案的编号大于等于它已经接受的提案编号,则 “审批委员”将批准此提案内容(如果根据 P2c 没有已经批准的内容,那么该内容就是“提案委员”提案中的内容;
如果内容为value0,那么该提案内容就是value0)并将此批准过的提案回复给“立案委员”;
如果提交审批的提案编号小于它已经接受的提案编号,则审批失败。回复所接受的提案编号及立案委员编号。
如果“立案委员”收到多数派审批失败(此种情况也称为“提交失败”),则重新进入“准备提案阶段”查找出要转发的目标“立案委员”。
多数派审批失败后,重新进入“准备提案阶段”。此处由于在“准备提案阶段”中立案委员会转发有抢占提交权的“立案委员”所以它是快速收敛的不存在“活锁”的问题。
4.4 算法流程图
4.4.1流程活动描述
编号 | 活动名称 | 角色 | 阶段 | 活动描述 | 主要输入输出 | |
1 | 提案(SN1,RY1,AV1) | 立案委员 | 准备阶段 | 接收草案并给该草案编号形成提案 | 输入:草案 | 输出:提案 |
2 | 是否有提交权 | 立案委员 | 准备阶段 | 判断此时是否有提交权(如果有就省去一次抢占访问权的通讯) | 输入:判定是否有提交权 | 输出:判定结果 |
3 | 提交(SNx,RYx) | 立案委员 | 准备阶段 | 提交(SNx,RYx)抢占访问权 | 输入:(SNx,RYx) | 输出:发送到审批委员会(SNx,RYx) |
4 | 接收(如:SN1,RY1) | 审批委员 | 准备提案阶段 | 审批者接收提案编号与立案委员编号 | 输入:(SN1,RY1) | 输出:(SN1,RY1) |
5 | 判断SN1>Snmax | 审批委员 | 准备提案阶段 |
判断当前接收的提案编号是否大于审批过的最大提案编号 (令有值的提案编号要大于没有审批过任何提案的“空提案编号”) |
输入:判定SN1>SNmax | 输出:判定结果 |
6 | 令SN=SN1,RY=RY1 | 审批委员 | 准备提案阶段 | 如果SN1>SNmax则令SN=SN1,RY=RY1 | 输入:判定的结果SN1>SNmax及(SN1,RY1) | 输出:保存令SN=SN1,RY=RY1 |
7 | AV==NULL | 审批委员 | 准备提案阶段 | 判断当前已审批过的提案(注意不一定为决议内容)内容是否为空 | 输入:判定AV==NULL | 输出:判定结果 |
8 | AV==NULL | 审批委员 | 准备提案阶段 | 判断当前已审批过的提案(注意不一定为决议内容)内容是否为空 | 输入:判定AV==NULL | 输出:判定结果 |
9 | 回复(SNmax,RY,NULL) | 审批委员 | 准备提案阶段 | 回复已经接受的提案编号与立案委员编号 | 输入:判定的结果AV==NULL | 输出:(SNmax,RY,NULL) |
10 | 回复(SNmax,RY,AV) | 审批委员 | 准备提案阶段 | 回复已经接受的提案编号与立案委员编号及其已审批的内容 | 输入:判定的结果AV!=NULL | 输出:(SNmax,RY,AV) |
11 | 回复(SN1,RY1,NULL) | 审批委员 | 准备提案阶段 | 回复提案编号SN1与立案委员编号RY1 | 输入:判定的结果AV==NULL | 输出:(SN1,RY1,NULL) |
12 | 回复(SN1,RY1,AV) | 审批委员 | 准备提案阶段 | 回复提案编号SN1与立案委员编号RY1和AV | 输入:判定的结果AV!=NULL | 输出:(SN1,RY1,AV) |
13 |
接收回复 设置提案内容的值 |
立案委员 | 准备提案阶段 |
立案委员接收回复并从接收到回复的提案内容不为空的几个提案中, 取最大提案编号提案x的提案内容AVi;令AV=AVi 如果接收到的回复的提案内容全部为空则令AV=AV1 |
输入:审批委员回复的信息 | 输出:提案信息 |
14 | 多数SN==N | 立案委员 | 准备提案阶段 | 判断接收的回复是否多数派SN==N | 输入:判定多数SN==N | 输出:判定结果 |
15 | 提交提案审批 | 立案委员 | 审批提案 | 将准备提交的提案提交阶段 | 输入:判定的结果多数SN==N | 输出:(SN1,RY1,AV1) |
16 | 接收提案(SN1,RY1,AV1) | 审批委员 | 审批提案阶段 | 接收提案(SN1,RY1,AV1) | 输入:接收的提案(SN1,RY1,AV1) | 输出:(SN1,RY1,AV1) |
17 | SN1>=SNmax | 审批委员 | 审批提案阶段 |
将当前接收的提案编号与自身己接受的最大提案编号比较 (令SN为任何数值都大于NULL) |
输入:判定SN1>=SNmax | 输出:判定结果 |
18 | 令AV=AV1,RY=RY1 | 审批委员 | 审批提案阶段 | 如果SN1>=SNmax,保存AV1和RY1即令AV=AV1,RY=RY1 | 输入:判定的结果SN1>=SNmax | 输出:令AV=AV1,RY=RY1 |
19 | 回复(SN1,RY1,AV1) | 审批委员 | 审批提案阶段 | 向立案委员回复(SN1,RY1,AV1) | 输入:已保存的(SN1,RY1,AV1) | 输出:回复(SN1,RY1,AV1) |
20 | 回复(SNmax,RY,AV) | 审批委员 | 审批提案阶段 | 向立案委员回复(SNmax,RY,AV) | 输入:判定的结果SN1<SNmax | 输出:回复(SNmax,RY,AV) |
21 | 接收回复 | 立案委员 | 审批提案阶段 | 接收审批委员的回复提案信息 | 输入:回复的提案信息 | 输出:提案 |
22 | 多数派SN==N | 立案委员 | 审批提案阶段 | 判断多数派SN是否等于N | 输入:判定多数SN==N | 输出:判定结果 |
23 | 回复决议案 | 立案委员 | 审批提案阶段 | 向草案委员回复决议案 | 输入:判定的结果多数SN==N | 输出:回复(SN1,RY1,AV1) |
24 | 审批失败 | 立案委员 | 审批提案阶段 | 提交的提案审批失败 | 输入:判定的结果多数SN!=N | 输出:令此次审批失败 |
25 | 多数AV!=NULL | 立案委员 | 准备提案阶段 | 判断多数派AV是否不等于NULL | 输入:判定多数AV!=NULL | 输出:判定结果 |
26 | 多数AV相同 | 立案委员 | 准备提案阶段 | 判断多数派AV是否相同 | 输入:判定多数AV是否相同 | 输出:判定结果 |
27 | 回复决议案 | 立案委员 | 准备提案阶段 |
向草案委员回复决议案 (此处为已经审批通过的其它草案委员提交的提案而形成的决议案) |
输入:判定的结果多数AV相同 | 输出:回复(SNmax,RY,AV) |
28 | 多数SN相同 | 立案委员 | 准备提案阶段 | 判断多数SN是否相同 | 输入:判定多数SN是否相同 | 输出:判定结果 |
29 | 目标为SN的Ryx | 立案委员 | 准备提案阶段 | 多数SN相同说明其它立案委员已经取得提交权即令待转发的目标为RYx | 输入:判定的结果多数SN相同 | 输出:令待转发的目标为RYx |
30 | 取SN>N的SNmax的RYx | 立案委员 | 准备提案阶段 | 从多个SN中取大于自身提案编号的最大提案编号 | 输入:判定的结果AV不相同或多数SN不相同 | 输出:取SN>N的最大提案编号 |
31 | RY==NULL or RY==RY1 | 立案委员 | 准备提案阶段 | 判断有没有待转发的目标,没有就递增提案编号重新抢占访问权 | 输入:判定RY==NULL or RY==RY1 | 输出:判定结果 |
32 | 转发提案到RYx | 立案委员 | 准备提案阶段 |
将提案转发到RYx的立案委员,当前“立案委员”充当了“草案委员”的角色, 它将接收决议案并负责将接收到的决议案回复给草案提交者。 |
输入:判定的结果取得一最大提案编号 | 输出:提案转发到RYx的立案委员 |
33 | 是否转发成功 | 立案委员 | 准备提案阶段 | 判断提案是否转发成功,如果成功则由RYx领导提交提案 | 输入:判定是否转发成功 | 输出:判定结果 |
34 | 删除(SNmax,RYx) | 立案委员 | 准备提案阶段 | 转发目标失败则删除此转发失败的编号信息(SNmax,RYx) | 输入:判定的结果是转发失败 | 输出:删除此转发失败的编号信息(SNmax,RYx) |
5 MULTI-PPMC 算法:
与MULTI-PAXOS算法一样把PPMC每个两阶段提交过程:准备->承诺->提交->批准;称作 一个回合,每个PPMC实例内又可能经过多个回合才达成一致;
多个PPMC实例运行确认一系列的确定性值。这就是MULTI-PPMC算法。多个实例之间不相干,可以并行或串行进行。
并行处理可以批量执行PPMC实例,将多个PPMC实例在同一个通讯下完成抢占访问权或提交提案审批。
在多实例(PPMC)的串行处理可以将一个实例的审批提案阶段与临近的下一个实例的准备提案阶段合并到同一个通讯中以完成提交提案。
6 总结:
PPMC在PAXOS算法基础上解决活锁问题,从而发展了一致性算法,进一步提高了一致性算法的可用性。
一致性算法的历史发展体现在以下几个方面的发展;解决竞争死锁的方式从互斥锁发展到抢占锁;一致性结果值的存储从单个发展到多副本;确认协商一致从专断发展到民主投票多数派议断;解决竞争活锁的方式从并行重试提交发展到串行代理提交。
PPMC对PAXOS的改进正是在解决竞争活锁的方式上的改进;而现今一些协议对PAXOS的活锁这个问题改进主要是延迟随机时间重试和租约时间过期重试;
而PPMC采用的是将活锁中的竞争者由并行重试提交改进为串行代理提交运行的思路。
本人希望借此文章抛砖引玉,望有人会提出更加高可用性和强一致性的算法。
参考文献:
[1] LESLIE LAMPORT. 《PAXOS MADE SIMPLE》
[2] KEVINLYNX. “图解分布式一致性协议PAXOS” 2014-10-15
转载于:https://my.oschina.net/oosc/blog/1619168
Paxos分布式一致性协议相关推荐
- Paxos——分布式一致性协议
目录 1. Paxos的理解困境 2. Paxos和分布式存储系统 3. 确定一个不可变变量的取值 3.1 如何定义"确定一个不可变变量的取值"这样的系统问题? 3.2 确定一个不 ...
- 简述分布式一致性协议(2pc、3pc、paxos、zab)
分布式一致性协议 二阶段提交协议(2pc) 三阶段提交协议(3pc) paxos zab 在分布式系统中,每个机器都可以确定自己进行的事务操作是否成功,但是无法直接了解其他机器的操作结果.因此,当一个 ...
- 分布式一致性协议Raft,以及难搞的Paxos
Raft这玩意,网上已经有好多解读文章了,大概比Paxos还要多一些,所以,这篇,不求细节,但求核心思想方面,追一下本源,然后,给自己做个笔记. Raft是什么,它想解决什么问题? 所以Raft是什么 ...
- 分布式一致性协议三部曲-深入理解一致性协议Paxos
在理解分析分布式一致性协议前,我们必须先看下CAP理论 CAP CAP是指在一个分布式系统中,一致性(Consistency).可用性(Availability).分区容错性(Partition to ...
- 分布式一致性协议三部曲-从paxos幽灵复现看Raft实现原理
幽灵复现 Mutlti-Paxos下存在Leader切换情况,因而可能出现下面的场景 第一轮中A被选为 Leader,写下了 1-10 号日志,其中 1-5 号日志形成了多数派,并且已给客户端应答,而 ...
- paxos协议 对比_分布式一致性协议三部曲-深入理解一致性协议Paxos
CAP二阶段提交协议(2PC)协议详情改进缺陷参与者 还都处于锁定事务资源的状态中,而无法继续完成事务操.尽管协调者挂掉后可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问 ...
- [分布式一致性协议] ------ raft协议的解释与理解
前言 在分布式系统中,为了保证容错性,一般会维护多个副本集群,提高系统的高可用,但与之带来的问题就是多个副本的一致性(consensus)问题. 我们认为,对于一个具有一致性的的集群中,同一时刻所有节 ...
- 一文说清各种分布式一致性协议
本文来说下各种常见的分布式协议 文章目录 概述 CAP定理 Base理论 2PC 2PC 阶段一 2PC阶段二 举个例子 3PC CanCommit阶段 PreCommit阶段 doCommit阶段 ...
- 浅谈分布式一致性协议之2PC
由于BASE理论需要在一致性和可用性方面做出权衡,因此涌现了很多关于一致性的算法和协议.其中比较著名的有二阶提交协议(2 Phase Commitment Protocol),三阶提交协议(3 Pha ...
最新文章
- C++ STL容器值set
- BZOJ 3653: 谈笑风生(离线, 长链剖分, 后缀和)
- 前端学习(2346):uniapp环境搭建
- python电脑编程求圆的面积案例_学Python划重点七 网络编程(UPD Socket编程、上传文件实例、计算圆的面积实例)...
- Landsat 数据下载与预处理
- 二叉树遍历(递归与非递归版本)
- 恋爱物语iapp源码导入就可以用
- IP 防护等级技术白皮书
- 12.测试字体的练习
- 利用谷歌浏览器模拟网速慢的情况
- GoLand每次切换,光标跑到行首
- 正多边形和多面体的对称群
- Vosk可以用于Unity的离线语音识别
- 创基MIFI扩展坞自带4G上网功能扩展坞
- HTML前端静态网页制作
- 大学英语四级写作试题必背之35句型
- VS2017中自用部分插件的设置的翻译或功能介绍——Word Highlight With Margin
- 公司-便利蜂:便利蜂
- python应用环境
- 《C语言编程魔法书:基于C11标准》——1.6 本章小结