zk基础—1.一致性原理和算法

大纲

1.分布式系统特点

2.分布式系统的理论

3.两阶段提交Two-Phase Commit(2PC)

4.三阶段提交Three-Phase Commit(3PC)

5.Paxos岛的故事来对应ZooKeeper

6.Paxos算法推导过程

7.Paxos协议的核心思想

8.ZAB算法简述

1.分布式系统特点

(1)分布性

(2)对等性

(3)并发性

(4)缺乏全局时钟

(5)故障随时会发生

分布式系统的特点:分布性、对等性、并发性、缺乏全局时钟、故障随时会发生。

(1)分布性

既然是分布式系统,最显著的特点肯定就是分布性。比如电商项目会分成不同的功能,或者不同的微服务。这些服务部署在不同的服务器中,甚至不同的集群中。整个架构都是分布在不同的地方的,在空间上是随意的,而且随时会增加、删除服务器节点。

(2)对等性

对等性是分布式设计的一个目标。比如订单服务,为了防止订单服务出现问题,一般需要有一个备份,在订单服务出现问题时能顶替原来的订单服务。这就要求这两个或两个以上的订单服务完全是对等的,功能完全是一致的。这其实就是服务副本的冗余,当然还有数据副本的冗余,比如数据库、缓存等。

(3)并发性

在一个分布式系统中的多个节点,可能会并发地操作一些共享资源,可以通过分布式锁来准确、高效地协调分布式并发操作。

(4)缺乏全局时钟

在分布式系统中,节点可能会在任意位置,每个位置上的节点都会有自己的时间系统。因此在分布式系统中,很难定义两个事务究竟谁先谁后,原因就是因为缺乏一个全局的时钟序列进行控制。当然现在这已不是什么问题,已有很多时间服务器提供给系统调用。

(5)故障随时会发生

任何一个节点都可能出现停电、死机等现象,服务器集群越多,出现故障的可能性就越大。随着集群数目的增加,出现故障甚至都会成为一种常态。

2.分布式系统的理论

(1)分布式系统带来的问题

(2)CAP理论

(3)BASE理论

(4)分布式一致性算法

(1)分布式系统带来的问题

分布式系统主要会有:通讯异常、网络分区、三态、节点故障等问题。

一.通信异常

通讯异常其实就是网络异常。网络系统本身就是不可靠的,由于分布式系统需要通过网络进行数据传输,网络硬件难免出现问题。只要网络出现问题,就会影响消息的发送与接受过程,因此数据消息的丢失或者延长就会变得非常普遍。

二.网络分区

网络分区,其实就是脑裂现象。同一个分布式集群出现两个相互冲突的决策者时,就是网络分区。脑裂产生原因是网络故障后出现了两个决策者。

三.三态

三态就是成功与失败以外的第三种状态,即超时态。在一个JVM中,应用程序调用一个方法函数后会得到一个明确的响应:要么成功,要么失败。在分布式系统中,虽然绝大多数情况下能接受到成功或者失败的响应,但一旦网络出现异常就非常有可能出现超时。当出现这种超时现象,网络通讯的发起方是无法确定请求是否成功处理的。

四.节点故障

节点故障在分布式系统下是比较常见的问题,指的是组成服务器集群的节点会出现的宕机或僵死的现象。

(2)CAP理论

CAP指的是一致性(C)、可用性(A)、分区容错性(P)这三个词的缩写。

一.一致性Consistency

在分布式系统中,一致性是数据在多个副本之间是否能够保证一致的特性。如果在分布式系统中针对某一个数据项的变更成功执行后,所有用户都能马上读取到最新值,则这样的系统就被认为具有强一致性。

二.可用性Avaliable

可用性指系统提供服务必须一直处于可用状态,对于用户的操作请求总是能够在有限的时间内访问结果。这里的重点是:有限的时间和返回结果。为了做到有限的时间需用到缓存和负载,这时增加节点是为考虑性能。为了返回结果需考虑节点主备,当主节点出现问题时备节点能快速顶上来。

三.分区容错性

分布式系统在遇到任何网络分区故障时,仍然需要能够对外提供满足一致性和可用性的服务,不能出现脑裂的情况,除非整个网络环境都发生了故障。

CAP理论具体描述:一个分布式系统不可能同时满足一致性、可用性和分区容错性,最多只能同时满足其中的两项。

注意:P是不能放弃的,不可能把所有应用全部放到一个节点上。A和C也不能全部放弃,因此要在A和C间寻求平衡,平衡的基础就是BASE理论。

(3)BASE理论

BASE理论:即使无法做到强一致性,但分布式系统可以根据自己的业务特点,采用适当的方式来使系统达到最终的一致性。

一.Basically Avaliable基本可用

当分布式系统出现不可预见的故障时,允许损失部分可用性,保障系统的基本可用,体现在响应时间上的损失和功能上的损失。例如:大促高峰部分用户的页面卡顿会使用降级处理。

二.Soft State软状态

其实就是前面讲到的三态,即允许系统中的数据存在中间状态。不同节点的数据副本进行数据同步的过程存在延时,并不影响系统可用性。例如:通过消费消息队列更新本地缓存。

三.Eventually Consistent最终一致性

所有的数据副本在经过一段时间的同步后,最终能达到一个一致的状态。例如:手机充值总金额短时不到账,但一段时间后一定到账。

BASE理论一句话概括:就是平时系统要基本可用,除了成功失败,运行时允许出现可容忍的延迟状态,但是无论如何经过一段时间的延迟后系统最终必须达到数据是一致的。

(4)分布式一致性算法

不管是CAP理论,还是BASE理论,这些理论都需要算法来实现。2PC、3PC、Paxos算法、ZAB算法就是用来实现CAP、BASE理论的。这些理论的前提一定是分布式,解决的问题都是:在分布式环境下,怎么让系统尽可能高可用,且数据能最终能达到一致。

3.两阶段提交Two-Phase Commit(2PC)

(1)数据库事务通过undo和redo保证数据强一致性

(2)分布式事务通过2PC保证数据强一致性

(3)2PC的优点和缺点

(1)数据库事务通过undo和redo保证数据强一致性

2PC即二阶段提交算法,是强一致性算法。它是数据库领域内,为了使基于分布式系统架构下的所有节点,在进行事务处理过程中能够保持原子性和一致性而设计的算法。所以很适合用作数据库的分布式事务,其实数据库经常用到的TCC本身就是一种2PC。

在InnoDB存储引擎中,对数据库的事务修改都会写undo日志和redo日志。其实不只是数据库,很多需要事务支持的都会用到undo和redo思路。

一.对数据的修改操作首先写undo日志记录数据原值

二.然后执行事务修改操作,把修改写到redo日志里

三.万一事务失败了,可以从undo日志恢复数据

数据库通过undo和redo能保证数据强一致性。

(2)分布式事务通过2PC保证数据强一致性

解决分布式事务的前提就是节点是支持事务的。在这个前提下,2PC把整个分布式事务分两个阶段:投票阶段(Prepare)和执行阶段(Commit)。

阶段一:投票阶段

在阶段一中,各参与者投票表明是否要继续执行接下来的事务提交操作。

步骤一:协调者向参与者发起事务询问。协调者向所有的参与者发送事务内容,询问是否可以执行事务操作,并开始等待各参与者的响应。

步骤二:参与者收到协调者的询问后执行事务。各参与者节点执行事务操作,并将undo和redo信息记入事务日志中。

步骤三:参与者向协调者反馈事务询问的响应。如果参与者成功执行事务操作,就反馈协调者Yes响应,表示事务可执行。如果参与者没成功执行事务,就反馈协调者No响应,表示事务不可执行。

阶段二:执行阶段

在阶段二中,协调者会根据各参与者的反馈来决定是否可以进行事务提交。有两种提交可能:执行事务提交和中断事务。

可能一:执行事务提交

假如协调者从所有参与者获得的反馈都是Yes响应,那么就会执行事务提交。

步骤一:协调者向参与者发送提交请求,协调者向所有参与者节点发出Commit请求。

步骤二:参与者收到协调者的Commit请求后进行事务提交。参与者接收到Commit请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源。

步骤三:参与者向协调者反馈事务提交结果(Ack消息)。参与者在完成事务提交之后,向协调者发送Ack消息。

步骤四:协调者收到所有参与者反馈(Ack)后完成事务。协调者接收到所有参与者反馈的Ack消息后,完成事务。

可能二:中断事务

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

步骤一:协调者向参与者发送回滚请求,协调者向所有参与者节点发出Rollback请求。

步骤二:参与者收到协调者的Rollback请求后进行事务回滚。参与者接收到Rollback请求后,会利用undo信息来执行事务回滚操作,并在完成回滚之后释放占用的事务资源。

三.参与者向协调者反馈事务回滚结果(Ack消息)。参与者在完成事务回滚之后,向协调者发送Ack消息。

四.协调者收到所有参与者反馈(Ack)后中断事务。协调者接收到所有参与者反馈的Ack消息后,完成事务中断。

(3)2PC的优点和缺点

一.2PC优点

优点一:原理简单

优点二:实现方便

二.2PC缺点

总结来说有四个缺点:同步阻塞、单点故障、数据不一致、容错机制不完善。

缺点一:同步阻塞

在二阶段提交过程中,所有节点都在等其他节点响应,无法进行其他操作,这种同步阻塞极大的限制了分布式系统的性能。

缺点二:单点问题

协调者在整个二阶段提交过程中很重要。如果协调者在提交阶段出现问题,那么整个流程将无法运转。而且其他参与者会处于一直锁定事务资源的状态中,无法完成事务操作。

缺点三:数据不一致

假设当协调者向所有参与者发送Commit请求后,发生了局部网络异常。或者是协调者在尚未发送完所有Commit请求之前自身发生了崩溃,导致最终只有部分参与者收到了Commit请求,那么这会出现部分参与者的数据不一致问题。

缺点四:容错性不好

二阶段提交协议没有较为完善的容错机制,任意一个参与者或协调者故障都会导致整个事务的失败。

4.三阶段提交Three-Phase Commit(3PC)

(1)第一阶段canCommit

(2)第二阶段preCommit

(3)第三阶段doCommit

(4)3PC的优缺点

(5)3PC与2PC区别

(1)第一阶段canCommit

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

步骤二:参与者收到协调者的询问后反馈响应。参与者在接收到协调者的canCommit请求后,如果认为可以顺利执行事务,会反馈Yes响应并进入预备状态,否则反馈No响应。

这一阶段其实就是确认所有的资源是否都是健康、在线的。因为有了这一阶段,大大的减少了2PC提交的阻塞时间。

因为这一阶段优化了以下这种情况:2PC提交时,如果有两个参与者,恰好参与者2出现问题,参与者1执行了耗时的事务操作,最后却发现参与者2连接不上,参与者1白执行耗时操作了。

(2)第二阶段preCommit

包含两种可能:执行事务预提交和中断事务。

可能一:执行事务预提交

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

步骤一:协调者向参与者发送预提交请求,协调者向所有参与者发出preCommit请求,然后协调者会进入预提交状态。

步骤二:参与者收到协调者的preCommit请求后执行事务。参与者接收到协调者发出的preCommit请求后,会执行事务操作,并将undo和redo信息记录到事务日志中。

步骤三:参与者向协调者反馈事务执行的响应(Ack)。如果参与者成功执行了事务操作,那么就会反馈给协调者一个Ack响应。

可能二:中断事务

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

步骤一:协调者向参与者发送中断请求,协调者向所有参与者节点发出abort请求。

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

(3)第三阶段doCommit

包含两种可能:执行提交和中断事务。

可能一:执行提交

接收到来自所有参与者的Ack响应。

步骤一:协调者向参与者发送提交请求。协调者向所有参与者发出doCommit请求,然后协调者会由预提交状态进入提交状态。

步骤二:参与者收到协调者的doCommit请求后提交事务。参与者接收到doCommit请求后,会正式执行事务提交操作,并在完成提交之后释放整个事务执行期间占用的事务资源。

步骤三:参与者向协调者反馈事务提交结果。参与者在完成事务提交之后,向协调者发送Ack消息。

步骤四:协调者收到所有参与者反馈(Ack)完成事务。协调者接收到所有参与者反馈的Ack消息后,完成事务。

可能二:中断事务

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

步骤一:协调者向参与者发送回滚请求,协调者向所有参与者节点发出abort请求。

步骤二:参与者收到协调者的abort请求后进行事务回滚。参与者接收到协调者的abort请求后,会利用undo信息执行事务回滚操作,并在完成回滚之后释放占用的事务资源。

步骤三:参与者向协调者反馈事务回滚结果(Ack消息)。参与者在完成事务回滚之后,会向协调者发送Ack消息。

步骤四:协调者收到所有参与者反馈(Ack)后中断事务。协调者接收到所有参与者反馈的Ack消息后,完成事务中断。

**注意:**一旦进入阶段三doCommit,无论出现哪一种故障:协调者出现了问题、协调者和参与者之间网络故障,最终都会导致参与者无法及时接收来自协调者的doCommit或abort请求。参与者都会在等待超时后,继续进行事务提交。

(4)3PC的优缺点

三阶段提交协议的优点

优点一:改善同步阻塞

与2PC相比,降低了参与者的阻塞范围。

优点二:改善单点故障

与2PC相比,出现单点故障后能继续达成一致。

三阶段提交协议的缺点

缺点一:同步阻塞

相比2PC虽然降低阻塞范围,但依然存在阻塞。

缺点二:单点故障

虽然单点故障后能继续提交,但单点故障依然存在。

缺点三:数据不一致

正是因为出现单点故障后可以继续提交,所以可能会出现数据不一致。

缺点四:容错机制不完善

参与者或协调者节点失败会导致事务失败,所以数据库的分布式事务一般都是采用2PC,而3PC更多是被借鉴扩散成其他的算法。

(5)3PC与2PC区别

区别一:3PC第二阶段才写undo和redo事务日志。

区别二:3PC第三阶段协调者出现异常或网络超时参与者也会commit。

5.Paxos岛的故事来对应ZooKeeper

(1)Paxos岛的故事背景

(2)Paxos岛的故事运作

(3)Paxos岛的故事与ZooKeeper对应关系

(4)Paxos岛的故事的总统

先说Paxos,它是一个基于消息传递的一致性算法。Chubby和ZooKeeper都是基于Paxos的理论来实现的,Paxos还被认为是到目前为止唯一的分布式一致性算法,其它的算法都是Paxos的改进或简化。

但是Paxos有一个前提:没有拜占庭将军问题。就是Paxos只有在一个可信的计算环境中才能成立,这个环境是不会被入侵所破坏的。

(1)Paxos岛的故事背景

有一个叫做Paxos小岛(对应zk集群),上面住了一批居民(对应zk的Client)。岛上面的事情由一些特殊的人决定,他们叫做议员(对应zk的Server)。议员(对应zk的Server)的总数是确定的,不能更改。

岛上的居民(对应zk的Client)每次进行事情变更都需要通过一个提议(Proposal)。每个提议都有一个编号(PID,对应zk的ZXID)。这个编号是一直增长的,不能倒退。每个提议都要超过半数议员同意才能生效。因为如果全部通过就是强一致性,会破坏可用性。

每个议员(zk的Server)只会同意大于当前编号(zk的ZXID)的提议(Proposal),包括已生效和未生效的(分布式节点要排除由于网络延迟而晚收到的消息)。如果议员收到小于等于当前编号的提议,他会拒绝,并告知对方(对应zk的Client):你的提议已经有人提过了(保证数据的版本性)。当前编号指的是每个议员在记事本上记录的编号,他会不断更新该编号。整个会议不能保证所有议员记事本上的编号总是相同的,因为网络延迟可能半数人已通过第10版本了,部分人还停留在第9版本。

现在会议有一个目标:保证所有议员(zk的Server)对提议(Proposal)都能达成一致的看法(完成所有数据的同步)。

(2)Paxos岛的故事运作

好,现在会议开始运作,所有议员一开始记事本上面记录的编号都是0。

说明一:有一个议员收到了居民的一个提议:将电费设定为1元/度。他首先看一下记事本,发现当前提议编号是0,则他的这个提议编号就是1。于是他给所有议员发起提议:1号提议,设定电费1元/度。

说明二:其他议员收到消息以后查了一下记事本,发现当前提议编号是0。所以接收到的这个提议是可以接受的,于是记录下这个提议并回复:我接受你的1号提议,同时在记事本上记录:当前提议编号为1。

说明三:发起提议的议员收到了超过半数的回复。于是他立即给所有人发通知:1号提议生效!收到该通知的议员会修改他的记事本,将1号提议由记录改成正式的法令。当有居民问他电费多少时,他会查看并告诉对方:1元/度。

上面这个议员发出第一个提议的阶段其实隐含了两阶段提交的概念,总的来说,过半通过 + 两阶段提交,可以非常好地解决消息的传递。

说明四:冲突的解决。假设总共有三个议员S1、S2、S3,S1和S2同时发起了一个提议:1号提议,设定电费。但S1想设为1元/度,S2想设为2元/度。此时S1和S2的提议各有一票了。结果S3先收到了S1的提议,于是他做了和前面同样的操作。紧接着他又收到了S2的提议,结果他一查记事本,发现这个提议的编号小于等于我的当前编号1,于是拒绝了这个提议:对不起,这个提议先前提过了。因此S2的提议被拒绝,S1的提议被S1和S3同意。所以S1可以正式发布提议:1号提议生效。S2向S1或S3打听并更新1号法令的内容,之后可选择继续发起2号提议。

至此,Paxos的核心已通过故事介绍完毕。

(3)Paxos岛的故事与ZooKeeper对应关系

复制代码
小岛------ZooKeeper Server Cluster
议员------ZooKeeper Server
居民------ZooKeeper Client
总统------ZooKeeper Server Leader
提议(Proposal)------ZNode Change(Create/Delete/SetData)
提议编号(PID)------ZXID(ZooKeeper Transaction Id)
正式法令------所有ZNode及其数据

Paxos岛上的议员应该是人人平等,而ZooKeeper Server有一个Leader的概念。如果议员人人平等,在某种情况下会由于提议的冲突而产生一个活锁。所谓活锁意思是大家都没有死都在动,但一直解决不了冲突问题。

Lamport在《The Part-Time Parliament》中阐述了活锁问题并给出了方案:在所有议员中设立一个总统,只有总统有权发出提议。如果议员有自己的提议,必须发给总统并由总统来提出。

(4)Paxos岛的故事的总统

又一个问题产生了,总统怎么选出来的?ZooKeeper是如何选出Leader的?

现在假设总统已经选好了,下面看看ZooKeeper Server是怎么实施的。

**情况一:**对应zk的客户端读数据。居民甲到某个议员那里询问(Get)某条法令的情况(ZNode的数据),议员毫不犹豫的拿出他的记事本,查阅法令并告诉他结果。同时该议员会向居民甲声明:我的数据不一定是最新的,如果想要最新的数据,那么需要等一会,等我找总统Sync一下再告诉你。

**情况二:**对应zk的客户端进行数据修改,修改权交由Leader处理。居民乙到某个议员那里要求政府归还(Set)欠他的一万元钱(ZNode的数据)。议员让他在办公室等着,自己将问题反映给了总统。总统询问所有议员的意见,多数议员表示欠居民的钱一定要还。于是总统发表声明,从国库中拿出一万元还债,国库由100万变成99万。居民乙拿到钱回去了。

**情况三:**ZooKeeper Server的Leader挂了。总统突然挂了,议员接二连三的发现联系不上总统,于是各自发表声明,推选新的总统。总统大选期间政府停业,拒绝居民的请求。Leader选举的过程中,节点不提供对外服务。Leader挂了,影响写操作。

6.Paxos算法推导过程

(1)Paxos的概念

(2)问题描述

(3)推导过程

(4)Proposer生成提案

(5)Acceptor批准提案

(6)Paxos算法描述

(7)Learner学习被选定的value

(8)如何保证Paxos算法的活性

(1)Paxos的概念

一.Paxos的角色

二.Paxos的提案

三.Paxos角色对数据达成一致的情况

一.Paxos的角色

在Paxos算法中,有三种角色:Proposer、Acceptor、Learner。在具体的实现中,一个进程可能同时充当多种角色。比如一个进程可能既是Proposer又是Acceptor又是Learner。

还有一个重要概念叫提案(Proposal),最终要达成一致的value就在提案里。

假如只有一个角色,那么其实就是分布式节点自己。各自认为自己的表达是正确的,这时候是无法达成一致的。所以需要引入多一个角色来处理各个节点的表达,最后还要引入一个角色将达成一致的结果同步给各分布式节点。

二.Paxos的提案

暂且认为提案(Proposal)只包含value,但在接下来的推导过程中会发现如果提案(Proposal)只包含value会有问题。

暂且认为Proposer可以直接提出提案,在接下来的推导过程中会发现:如果Proposer直接提出提案也会有问题,需要增加一个学习提案的过程。

Proposer可以提出(propose)提案,Acceptor可以批准(accept)提案。如果某个提案被选定(chosen),那么该提案里的value就被选定了。

注意:批准和选定是不一样的,批准未必就代表选定。根据后面所述,多数Acceptor批准了某提案,才能认为该提案被选定了。

三.Paxos角色对数据达成一致的情况

对某个数据的值达成一致,指的是:Proposer、Acceptor、Learner都认为同一个value被选定(chosen)。

Proposer、Acceptor、Learner分别在什么情况才认为某个value被选定:

情况一:Proposer

只要Proposer发出的提案被Acceptor批准,那么Proposer就认为该提案里的value被选定了。

情况二:Acceptor

只要Acceptor批准了某个提案,那么Acceptor就认为该提案里的value被选定了。

情况三:Learner

Acceptor告诉Learner哪个value被选定,Learner就认为那个value被选定。

上面只有一个节点时是没问题的,但多个节点时批准就不能等于选定了。

(2)问题描述

一.该一致性算法的基本实现目标

二.该一致性算法满足的安全性

三.推导该一致性算法的默认条件

四.提案被选定的规定

一.该一致性算法的基本实现目标

假设有一组可以提出(propose)value的进程集合(value在提案Proposal里),那么一个一致性算法需要做出如下保证。

保证一:在提出的这么多value中,只有一个value会被选定(chosen)

保证二:如果没有value被提出,那么就不应该有value被选定

保证三:如果一个value被选定,那么所有进程都应该能学习(learn)或者获取到这个被选定的value

二.该一致性算法满足的安全性

一个分布式算法有两个最重要的属性:安全性(Safety)和活性(Liveness)。安全性是指那些需要保证永远都不会发生的事情,活性是指那些最终一定会发生的事情。

对于一致性算法,安全性要求如下:

要求一:只有被提出的value才能被选定

要求二:只有一个value能被选定

要求三:如果某进程认为某value被选定,则该value必须是真的被选定

在对Paxos算法的介绍中,我们不去精确定义其活性需求,只需要确保:Paxos算法的目标是保证最终有一个提出的value被选定,当一个value被选定后,进程最终也能学习或者获取到这个value。

三.推导该一致性算法的默认条件

假设三个角色间可通过发送消息来进行通信,那么默认以下两个情况:

情况一:每个角色以任意的速度执行,可能因出错而停止,也可能会重启。同时即使一个value被选定后,所有的角色也可能失败然后重启。除非失败后重启的角色能记录某些信息,否则重启后无法确定被选定的值。

情况二:消息在传递过程中可能任意延迟、可能会重复、也可能丢失。但是消息不会被损坏,即消息内容不会被篡改,也就是无拜占庭将军问题。即各将军管理的军队被地理分割开,只能依靠通讯员传递信息,而通信员可能存在叛徒篡改信息欺骗将军。

四.提案被选定的规定

下面规定在存在多个Acceptor的情况下,如何判断选定一个提案。

规定:Proposer会向一个Acceptor集合发送提案。同样,集合中的每个Acceptor都可能会批准(Accept)该提案。当有足够多的Acceptor批准这个提案时,我们就认为该提案被选定了。

所以,批准和选定是不一样的,批准未必就代表选定。多数Acceptor批准了某提案,才能认为该提案被选定了。

足够多指的是:我们假定足够多的Acceptor其实是整个Acceptor集合的一个子集,并且让这个集合大得可以包含Acceptor集合中的大多数成员,因为任意两个包含大多数Acceptor的子集至少有一个公共成员。

(3)推导过程

一.要选定一个唯一提案的最简单方案------只允许一个Acceptor存在

二.多个Acceptor和多个Proposer------如何使得只有一个value被选定

三.提案变成了一个由编号和value组成的组合体:[编号, value]

四.如何证明P2b

五.如何根据P2c去证明P2b

复制代码
约束P1:一个Acceptor必须批准它收到的第一个提案。
规定R1:一个Proposer的提案能够发送给多个Acceptor(Acceptor集合)。
规定R2:一个提案(value)被选定需要被半数以上的Acceptor批准。
规定R3:每一个Acceptor必须能够批准不止一个提案(value)。


约束P2:如果提案[M0, V0]被选定了:
那么所有比编号M0更高的且被选定的提案,其value值必须也是V0。


约束P2a:如果提案[M0, V0]被选定了:
那么所有比编号M0更高的且被Acceptor批准的提案,其value值必须也是V0。


约束P2b:如果提案[M0, V0]被选定了:
那么之后任何Proposer产生的编号更高的提案,其value值必须也是V0。


约束P2c:对于任意的Mn和Vn,如果提案[Mn, Vn]被提出:
那么肯定存在一个半数以上的Acceptor组成的集合S,满足以下条件中的任意一个:


条件一:S中每个Acceptor都没有批准过编号小于Mn的提案。
条件二:S中Acceptor批准过的编号小于Mn的且编号最大的提案的value为Vn。

一.要选定一个唯一提案的最简单方案------只允许一个Acceptor存在

要使得只有一个value被选定,最简单的方式莫过于只有一个Acceptor,当然可以有多个Proposer,这样Proposer只能发送提案给该Acceptor。

此时,Acceptor就可以选择它收到的第一个提案作为被选定的提案,这样就能够保证只有一个value会被选定。

但是,如果这个唯一的Acceptor宕机了,那么整个系统就无法工作。因此,必须要有多个Acceptor来避免Acceptor的单点问题。

二.多个Acceptor和多个Proposer------如何使得只有一个value被选定

如果希望即使只有一个Proposer提出一个value,该value也能被选定。那么,就得到下面的约束:

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

但是,这又会引出另一个问题:如果每个Proposer分别提出不同的value,发给不同的Acceptor。根据约束P1,每个Acceptor分别批准自己收到的第一个value,这就会导致不同的value被选定,出现value不一致。于是满足不了只有一个value会被选定的要求,如下图示:

上面是由于:"一个提案只要被一个Acceptor批准,则该提案的value就被选定了",以及"Acceptor和Proposer存在多个",才导致value不一致问题。因此问题转化为:在存在多个Acceptor和多个Proposer情况下,如何进行提案的选定?

最简单的情况:如果一个Proposer的提案只发送给一个Acceptor,由上图可知必然会导致value不一致问题。因此,可以有以下规定:

**规定R1:**一个Proposer的提案能够发送给多个Acceptor(Acceptor集合)。

既然一个Proposer的提案能够发送给多个Acceptor,当Proposer可以向Acceptor集合发送提案时,集合中的每个Acceptor都可能会批准该提案。当有足够多的Acceptor批准这个提案时,我们才可认为该提案被选定了。那么什么才是足够多呢?

我们假定足够多的Acceptor是整个Acceptor集合的一个子集,并且让该子集大得可以包含Acceptor集合中的大多数成员,因为任意两个包含大多数Acceptor的子集至少有一个公共成员。因此,有了如下规定:

**规定R2:**一个提案(value)被选定需要被半数以上的Acceptor批准。

在约束P1(一个Acceptor必须要批准它收到的第一个提案)的基础上,再加上规定R2(一个提案(value)被选定需要被半数以上的Acceptor批准),假如每个Acceptor最多只能批准一个提案,那么又回到了下图的问题:

因此,有了如下规定:

**规定R3:**每一个Acceptor必须能够批准不止一个提案(value)。

既然一个Proposer的提案能够发送给多个Acceptor,那么一个Acceptor就会收到多个提案。

考虑情形1:当多个Proposer将其提案发送给多个Acceptor后,突然大部分Acceptor挂了,只剩一个Acceptor存活,如何进行提案的批准。该存活的Acceptor收到多个提案,由规定R3,它可以批准多份提案,那么如何保证最后只有一个提案被选定保证value一致。

考虑情形2:有5个Acceptor,其中2个批准了提案v1,另外3个批准了提案v2。此时如果批准v2的3个Acceptor中有一个挂了,那么v1和v2的批准者都变成了2个,此时就没法选定最终的提案。

因此,可以引入全局唯一编号来唯一标识每一个被Acceptor批准的提案:当一个具有某value值的提案被半数以上的Acceptor批准后,我们就认为该value被选定了,也就是该提案被选定了。唯一编号的作用其实就是用来辅助:当出现多个value都被同一半数批准时,可以选定唯一的value,比如选择唯一编号最大的value。

三.提案变成了一个由编号和value组成的组合体:[编号, value]

由上可知,选定其实认的只是value值。当提案[value]变成[编号, value]后,是允许多个提案[编号, value]被选定的。根据规定R2(一个提案被选定需要半数以上的Acceptor批准),存在多个提案被半数以上的Acceptor批准,此时就有多个提案被选定。那么就必须保证所有被选定的提案[编号, value]都具有相同的value值。否则,如果被选定的多个提案[编号, value]其value不同,又会出现不一致。因此,可以得到如下约束:

**约束P2:**如果提案[M0, V0]被选定了,那么所有比编号M0更高的且被选定的提案,它的value值也必须是V0。

编号可理解为提案被提出的时间,比编号M0更高可理解为晚提出。所以提案[M0,V0]被选定后,所有比该提案晚提出的提案被选定时。为了value一致,那么这些晚提出的被选定的提案的value也要是V0。而所有比该提案早提出的提案,可以忽视它不进行选定即可。

一个提案[编号, value]只有被Acceptor批准才可能被选定,因此我们可以把约束P2改写成对"Acceptor批准的提案"的约束P2a:

**约束P2a:**如果提案[M0, V0]被选定了,那么所有比编号M0更高的且被批准的提案,它的value值也必须是V0。

所以提案[M0,V0]被选定后,所有比该提案晚提出的提案被批准时。为了value一致,那么这些晚提出的被批准的提案的value也要是V0。而所有比该提案早提出的提案,可以忽视它不进行批准即可。因此只要满足了P2a,就能满足P2。

由于通信是异步的,一个提案可能会在某个Acceptor还未收到任何提案时就被选定了。假设有5个Acceptor和2个提案,在Acceptor1没收到任何提案情况下,其他4个Acceptor已经批准了来自Proposer2的提案。而此时Proposer1产生了一个具有其他value值的且编号更高的提案,并发送给了Acceptor1。那么根据P1,Acceptor1要批准该提案,但这与约束P2a矛盾。

因此,如果要同时满足P1和P2a,需要对P2a进行强化。因为P2a只是说晚提出的提案被Acceptor批准时value才为V0,需要对P2a强化为晚提出的提案不管是否被批准其value都是V0。

**约束P2b:**如果提案[M0, V0]被选定了,那么之后任何Proposer产生的编号更高的提案,其value值必须也是V0。

否则一个提案得到多数批准被选定后,再提出一个值不同的新提案,这个提案会作为第一个提案发到某个Acceptor然后被批准,与P2a矛盾。

因为一个提案必须在被Proposer提出后才能被Acceptor批准,所以P2b可以推出P2a,P2a可以推出P2。

复制代码
P2:提案[M,V]被选定后,晚提出的提案如果被选定,那么其value也是V;(否则会出现选定多个value了)
P2a:提案[M,V]被选定后,晚提出的提案如果被批准,那么其value也是V;(P2a可以推出P2)
P2b:提案[M,V]被选定后,晚提出的提案不管是否被批准,那么其value也是V;(否则根据P1可能会出现与P2a矛盾的情况)
而对于比提案[M,V]早提出的提案,可以采取忽略无视处理;(P2b可以推出P2a)


P1:一个Acceptor必须批准它收到的第一个提案;(否则只有一个提案被提出时就无法选定一个value了)
R1:一个Proposer的提案能够发送给多个Acceptor(Acceptor集合);(否则根据P1, 就会出现选定多个value了)
R2:一个提案(value)被选定需要被半数以上的Acceptor批准;(选定提案时对足够多的规定)
R3:每一个Acceptor必须能够批准不止一个提案(value);(否则一个提案就没法做到被半数以上的Acceptor批准了)

四.如何证明P2b

即提案[M0,V0]被选定后,Proposer提出的编号更高的提案的value都为V0。如果要证明P2b成立,则具体是要证明:假设提案[M0,V0]已被选定,则编号大于M0的提案Mn,其value值都是V0。

通过对Mn使用第二数学归纳法来证明,也就是说需要证明结论:假设编号在M0到Mn-1之间的提案,其value值都是V0,那么编号为Mn的提案的value值也为V0。

复制代码
第二数学归纳法:
要证明n时的value值为v,则先假设0~n-1时的value值都为v;
然后再推导出n时的value值为v;

证明:

根据第二数学归纳法,当提案[M0,V0]被选定时,要证明编号大于M0的提案Mn,其value值都是V0。也就是假设编号M0到Mn-1的提案的value值都是V0时,证明Mn的value值为V0。

因为编号为M0的提案已经被选定了,这意味着存在一个由半数以上的Acceptor组成的集合C,C中的每个Acceptor都批准了编号为M0的提案。

根据归纳假设,编号为M0的提案被选定意味着:C中的每个Acceptor都批准了一个编号在M0到Mn-1范围内的提案,并且每个编号在M0到Mn-1范围内的被Acceptor批准的提案,其value为V0。

根据归纳假设,因为编号M0到Mn-1的提案的value值都是V0,所以C中的Acceptor1可以批准M0提案,Acceptor2可以批准M0 + M1提案,Acceptor3可以批准M0 + M1 + M2 + M3提案......也就是C中每个Acceptor批准的一个M0到Mn-1范围内的提案的value都是V0。

因为任何包含半数以上Acceptor的集合S都至少包含C中的一个成员,所以S中必然存在一个Acceptor,它批准的M0到Mn-1提案的value都是V0。这是根据归纳假设得出的结论,因此可以根据此而进一步加强到P2c。

因此只要满足如下P2c,就能让编号为Mn的提案的value值也为V0,也就是只要满足P2c,就可以证明P2b。只要满足如下P2c约束 + 上述归纳假设得出的结论,就能证明Mn也为V0。

**约束P2c:**对于任意的Mn和Vn,如果提案[Mn, Vn]被提出,则存在一个半数以上的Acceptor组成的集合S,满足以下条件中的任一个。

条件一:S中每个Acceptor都没有批准过编号小于Mn的提案

条件二:S中Acceptor批准过的编号小于Mn且编号最大的提案的value为Vn

五.如何根据P2c去证明P2b

从P1到P2c的过程其实是对一系列条件的逐步加强。如果需要证明这些条件可以保证一致性,那么就要反向推导:P2c => P2b => P2a => P2,然后通过P1和P2来保证一致性。

实际上,P2c规定了每个Propeser应该如何产生一个提案(P2c规定的提案生成规则)。对于产生的每个提案[Mn, Vn],需要满足:存在一个由超过半数的Acceptor组成的集合S满足以条件的任意一个:

条件一:要么S中没有Acceptor批准过编号小于Mn的任何提案

条件二:要么S中所有Acceptor批准的所有编号小于Mn的提案中,编号最大的那个提案的value值为Vn

当每个Proposer都按照这个规则来产生提案时,就可以保证满足P2b了。

下面在P2c的生成规则下证明P2b:

首先假设提案[M0,V0]被选定了,设比该提案编号M0大的提案为[Mn,Vn],那么在P2c的生成提案规则前提下,证明Vn = V0;

数学归纳法第一步:验证某个初始值成立

当Mn = M0 + 1时,如果有这样一个编号为Mn的提案,根据P2c的提案生成规则可知,一定存在一个超半数Acceptor的子集S,由于提案[M0, V0]已被选定,所以S中必然有Acceptor批准过编号小于Mn的提案,也就是M0提案,即此时P2c的提案生成规则的条件一不成立,进入条件二来生成Vn。

所以,由于S中有Acceptor已经批准了编号小于Mn的提案(即M0提案)。于是,Vn只能是多数集S中编号小于Mn但为最大编号的那个提案的值。而此时因为Mn = M0 + 1,因此理论上编号小于Mn但为最大编号的那个提案肯定是[M0, V0],同时由于S和选定[M0, V0]的Acceptor集合都是多数集,故两者肯定有交集,也就是说由于两者都是多数集,所以S中必然存在一个Acceptor批准了M0。根据Mn = M0 + 1,M0其实就是编号小于Mn但是编号是最大的。这样Proposer在确定Vn取值的时候,就一定会选择V0(根据Vn只能是多数集S中编号小于Mn但为最大编号的那个提案的值)。

数学归纳法第二步:假设编号在M0 + 1到Mn - 1内成立,推导编号Mn也成立

根据假设,编号在M0 + 1到Mn - 1区间内的所有提案的value值为V0,需要证明的是编号为Mn的提案的value值也为V0。

由于编号在M0 + 1到Mn - 1区间内的所有提案都是按P2c的规则生成的,所以一定存在一个超半数Accepter的子集S,而且S中有Acceptor已经批准了编号小于Mn的提案(P2c条件一不成立)。于是,Vn只能是多数集S中编号小于Mn但为最大编号的那个提案的值。如果这个最大编号落在M0 + 1到Mn - 1区间内,那么Vn肯定是V0。如果不落在M0 + 1到Mn - 1区间内,则它的编号不可能比M0小,肯定是M0。这时因为S肯定会与批准[M0, V0]这个提案的Acceptor集合S'有交集,也就是说S中肯定存在一个Acceptor是批准了[M0, V0]的。又由于此时S中编号最大的提案其编号就是M0,根据上述,Vn只能是多数集S中编号小于Mn但为最大编号的那个提案的值。从而可知,此时Vn也是V0,因此得证。

(4)Proposer生成提案

在P2c的基础上,如何进行提案的生成?

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

**步骤一:**Proposer选择一个新的编号M向某Acceptor集合的成员发送请求,即编号为M的提案的Prepare请求,要求集合中的Acceptor做出两个回应。回应一是:向Proposer承诺,保证不再批准任何编号小于M的提案。回应二是:如果Acceptor已经批准过任何提案,那么就向Proposer反馈当前其已批准的、编号小于M但为最大编号的提案。

**步骤二:**如果Proposer收到了来自半数以上的Acceptor的响应结果,那么就可产生提案[M, V],这里V取收到响应的编号最大的提案的value值。当然还存在另外一种情况,就是半数以上的Acceptor都没有批准任何提案。也就是响应中不包含任何提案,那么此时V就可以由Proposer任意选择。Proposer确定好生成的提案[M, V]后,会将该提案再次发送给某个Acceptor集合,并期望获得它们的批准,此请求称为编号为M的提案的Accept请求。

注意:此时接收Accept请求的Acceptor集合,不一定是之前响应Prepare请求的Acceptor集合。

(5)Acceptor批准提案

根据Proposer生成提案的算法,一个Acceptor可能会收到来自Proposer的两种请求:编号为M的提案的Prepare请求和编号为M的提案的Accept请求。一个Acceptor会对Prepare请求做出响应的条件是:Acceptor可以在任何时候响应一个Prepare请求。一个Acceptor会对Accept请求做出响应的条件是:在不违背Acceptor现有承诺前提下,可响应任意Accept请求。

Acceptor可以忽略任何请求而不用担心破坏算法的安全性。因此,我们这里要讨论什么时候Acceptor可以响应一个请求。我们对Acceptor批准提案给出如下约束:

**约束P1a:**一个Acceptor只要尚未响应过任何编号大于M的Prepare请求,那么它就可以批准这个编号为M的提案。

可见,P1a包含了P1(一个Acceptor必须批准它收到的第一个提案)。

假设一个Acceptor收到一个编号为M的Prepare请求,在此之前它已经响应过编号大于M的Prepare请求。根据P1a,该Acceptor不可能再批准任何新的编号为M的提案,Acceptor也就没有必有对这个Prepare请求做出响应。因此,该Acceptor可以忽略编号为M的Prepare请求。

因此,每个Acceptor只需记住:它已批准提案的最大编号 + 它已响应Prepare请求的最大编号。这样即便Acceptor出现故障或者重启,也能保证满足P2c生成提案的规则。

而对于Proposer来说,只要它可以保证不会产生具有相同编号的提案,那么就可以丢弃任意的提案以及它所有的运行时状态信息。

(6)Paxos算法描述

结合Proposer和Acceptor对提案的处理逻辑;可以得出类似于两阶段提交的算法执行过程。

阶段一:(Prepare请求)

一.Proposer选择一个提案编号M,然后向半数以上的Acceptor发送编号为M的Prepare请求。

二.如果一个Acceptor收到一个编号为M的Prepare请求,且M大于该Acceptor已经响应过的所有Prepare请求的编号,那么它就会将它已经批准过的编号最大的提案作为响应反馈给Proposer,同时该Acceptor承诺不再批准任何编号小于M的提案。

阶段二:(Accept请求)

一.如果Proposer收到半数以上Acceptor,对其发出的编号为M的Prepare请求的响应,那么它就会发送一个针对[M, V]提案的Accept请求给半数以上的Acceptor。注意:V就是收到的响应中编号最大的提案的value。如果响应中不包含任何提案,那么V就由Proposer自己决定。

二.如果Acceptor收到一个针对[M, V]提案的Accept请求,只要该Acceptor没有对编号大于M的Prepare请求做出过响应,它就批准该提案。

(7)Learner学习被选定的value

Learner获取提案,有三种方案:

(8)如何保证Paxos算法的活性

一个极端的活锁场景:

(9)总结

二阶段提交协议解决了分布式事务的原子性问题,保证了分布式事务的多个参与者要么都执行成功,要么都执行失败。但是在二阶段解决部分分布式事务问题的同时,依然存在一些难以解决的诸如同步阻塞、无限期等待和脑裂等问题。

三阶段提交协议则是在二阶段提交协议的基础上,添加了PreCommit过程,从而避免了二阶段提交协议中的无限期等待问题。

Paxos算法引入过半的理念,也就是少数服从多数的原则。Paxos算法支持分布式节点角色之间的轮换,极大避免了分布式单点故障。因此Paxos算法既解决了无限期等待问题,也解决了脑裂问题。

整个Paxos算法就是想说明:每个Proposer生成的提案都去争取大多数Acceptor的批准。一旦有某个Proposer生成的提案[M0, V0]被大多数批准了,即便后面发现还有更多其他Proposer生成的提案[Mn, Vn]也被大多数Acceptor批准,那么这些提案的value值其实都是一样的,都为V0。因此就可以让每个Proposer都统一为一个value值为V0的提案,从而保证一致性。

7.Paxos协议的核心思想

(1)Paxos协议的核心思想

(2)Paxos协议的基本概念

(3)Paxos协议过程

(4)Paxos协议最终解决什么问题

(5)Paxos协议证明

(6)为什么要被多数派接受

(7)为什么需要做一个承诺

(8)为什么第二阶段A要从返回的提议中选择一个编号最大的

(9)Paxos协议的学习过程

(1)Paxos协议的核心思想

"与其预测未来,不如限制未来",这应该是Paxos协议的核心思想。Paxos协议本身是比较简单的,如何将Paxos协议工程化才是真正的难题。

(2)Paxos协议的基本概念

Proposal Value:提议的值

Proposal Number:提议编号,编号不能冲突

Proposal:提议 = 提议的值 + 提议编号

Proposer:提议发起者

Acceptor:提议接受者

Learner:提议学习者

**说明一:**协议中的Proposer有两个行为,一个是向Acceptor发Prepare请求,另一个是向Acceptor发Accept请求。

**说明二:**协议中的Acceptor则会根据协议规则,对Proposer的请求作出应答。

**说明三:**最后Learner可以根据Acceptor的状态,学习最终被确定的值。

为方便讨论,记[n, v]为提议编号为n、提议值为v的提议,记(m, [n, v])为承诺了Prepare(m)请求编号不再比m小,并接受过提议[n, v]。

(3)Paxos协议过程

一.第一阶段A

Proposer选择一个提议编号n,向所有的Acceptor广播Prepare(n)请求。

二.第一阶段B

Acceptor接收到Prepare(n)请求,若提议编号n比之前接收的Prepare请求都要大,则返回承诺将不会接收提议编号比n小的提议,并且带上之前Accept的提议中编号小于n的最大的提议,否则不予理会。

三.第二阶段A

Proposer得到了多数Acceptor的承诺后,如果没有发现有一个Acceptor接受过一个值,那么向所有的Acceptor发起自己的值和提议编号n。否则,从所有接受过的值中选择对应的提议编号最大的,作为提议的值,此时提议编号仍然为n。

四.第二阶段B

Acceptor接收到提议后,如果该提议编号不违反自己做过的承诺,则接受该提议。

需要注意的是,Proposer发出Prepare(n)请求后,得到多数派的应答。然后可以随便再选择一个多数派广播Accept请求,这时不一定要将Accept请求发给有应答的Acceptor。

五.协议过程总结

上面的图例中:首先P1广播了Prepare请求,但是给A3的Prepare请求丢失了。不过A1、A2成功返回了,即该Prepare请求得到多数派的应答。然后P1可以广播Accept请求,但是给A1的Accept请求丢失了。不过A2、A3成功接受了这个提议,因为这个提议被多数派(A2、A3形成多数派)接受,所以我们称被多数派接受的提议对应的值被Chosen。

**情况一:**如果三个Acceptor之前都没有接受过提议(即Accept请求),那么在第一阶段B中,就不用返回接受过的提议。

**情况二:**如果三个Acceptor之前接受过提议(即Accept请求),那么就需要在第一阶段B中,带上之前Accept的提议中编号小于n的最大的提议值,进行返回。

如下图示:Proposer广播Prepare请求之后,收到了A1和A2的应答,应答中携带了它们之前接受过的[n1, v1]和[n2, v2]。Proposer则根据n1、n2的大小关系,选择较大的那个提议对应的值。比如n1 > n2,那么就选择v1作为提议值,最后向Acceptor广播提议[n, v1]。

(4)Paxos协议最终解决什么问题

当一个提议被多数派接受后,这个提议对应的值会被Chosen(选定)。一旦有一个值被Chosen(选定),那么只要按照协议的规则继续交互。后续被Chosen的值都是同一个值,也就是保持了这个Chosen值的一致性。

(5)Paxos协议证明

上文就是基本Paxos协议的全部内容,其实是一个非常确定的数学问题。下面用数学语言表达,进而用严谨的数学语言加以证明。

一.Paxos原命题

如果一个提议[n0, v0]被大多数Acceptor接受,那么不存在提议[n1, v1]被大多数Acceptor接受,其中n0 < n1,v0 != v1。

二.Paxos原命题加强

如果一个提议[n0, v0]被大多数Acceptor接受,那么不存在Acceptor接受提议[n1, v1],其中n0 < n1,v0 != v1。

三.Paxos原命题进一步加强

如果一个提议[n0, v0]被大多数Acceptor接受,那么不存在Proposer发出提议[n1, v1],其中n0 < n1,v0 != v1。

如果"Paxos原命题进一步加强"成立,那么"Paxos原命题"显然成立。下面通过证明"Paxos原命题进一步加强",从而证明"Paxos原命题"。

四.归纳法证明

假设提议[m, v](简称提议m)被多数派接受,那么提议m到n(如果存在)对应的值都为v,其中n不小于m(m <= n)。

这里对n进行归纳假设,当n = m时,结论显然成立。设n = k时结论成立,即如果提议[m, v]被多数派接受,那么提议m到k对应的值都为v,其中k不小于m(m <= k)。

当n = k + 1时,若提议k + 1不存在,那么结论成立。若提议k + 1存在,对应的值为v1。

因为提议m已经被多数派接受,又k + 1的Prepare被多数派承诺并返回结果。基于两个多数派必有交集,易知提议k + 1的第一阶段B有带提议回来。那么v1是从返回的提议中选出来的,不妨设这个值是选自提议[t, v1]。

根据第二阶段B,因为t是返回的提议中编号最大的,所以t >= m。又由第一阶段A,知道t < n,即t < k + 1,也就是m <= t < k + 1。所以根据假设可知,提议m到k对应的值都为v。所以再根据m <= t < k + 1,可得出t对应的值就为v,即有v1 = v。因此由假设的n = k结论成立,可以推出n = k + 1成立。

于是对于任意的提议编号不小于m的提议n,对应的值都为v,所以命题成立。

五.反证法证明

要证明的是:如果一个提议[n0, v0]被大多数Acceptor接受,那么不存在Proposer发出提议[n1, v1],其中n0 < n1,v0 != v1。

假设存在,不妨设n1是满足条件的最小提议编号。

即存在提议[n1, v1],其中n0 < n1,v0 != v1。-----------------------(A)

那么提议n0、n0 + 1、n0 + 2、...、n1 - 1对应的值为v0。-------------(B)

由于存在提议[n1, v1],则说明大多数Acceptor已经接收n1的Prepare,并承诺将不会接受提议编号比n1小的提议。

又因为[n0, v0]被大多数Acceptor接受,所以存在一个Acceptor既对n1的Prepare进行了承诺,又接受了提议n0。

由协议的第二阶段B可知,这个Acceptor先接受了[n0, v0]。所以发出[n1, v1]提议的Proposer会从大多数的Acceptor返回中得知,至少某个编号不小于n0而且值为v0的提议已经被接受。----------(C)

由协议的第二阶段A知,该Proposer会从已经被接受的值中选择一个提议编号最大的值,作为提议的值。由(C)知可该提议编号不小于n0,由协议第二阶段B可知,该提议编号小于n1。于是由(B)知v1 == v0,与(A)矛盾。

所以命题成立。

(6)为什么要被多数派接受

因为两个多数派之间必有交集,所以Paxos协议一般是2F + 1个Acceptor。然后允许最多F个Acceptor停机,而保证协议依然能够正常进行,最终得到一个确定的值。

(7)为什么需要做一个承诺

可以保证第二阶段A中Proposer的选择不会受到未来变化的干扰。另外,对于一个Acceptor而言,这个承诺决定了:它回应提议编号较大的Prepare请求和接受提议编号较小的Accept请求的先后顺序。

(8)为什么第二阶段A要从返回的提议中选择一个编号最大的

这样选出来的提议编号一定不小于已经被多数派接受的提议编号,进而可以根据假设得到该提议编号对应的值是Chosen的那个值。

(9)Paxos协议的学习过程

如果一个提议被多数Acceptor接受,则这个提议对应的值被选定。一个简单直接的学习方法就是:获取所有Acceptor接受过的提议,然后看哪个提议被多数的Acceptor接受,那么该提议对应的值就是被选定的。

另外一个学习方法是:把Learner看作一个Proposer,根据协议流程,发起一个正常的提议,然后看这个提议是否被多数Acceptor接受。

注意:这里强调"一个提议被多数Acceptor接受",而不是"一个值被多数Acceptor接受"。

上图中,提议[3, v3],[5, v3]分别被B、C接受。虽然出现了v3被多数派接受,但不能说明v3被选定(Chosen)。只有提议[7, v1]被多数派(A和C组成)接受,才能说v1被选定,而这个选定的值随着协议继续进行不会改变。

8.ZAB算法简述

zk的ZAB协议是Paxos协议的一个精简版。ZAB协议即Zookeeper Atomic Broadcast,zk原子广播协议。ZAB协议是用来保证zk各个节点之间数据的一致性的。

ZAB协议包括以下特色:

**特色一:**Follower节点上全部的写请求都转发给Leader

**特色二:**写操作严格有序

**特色三:**使用改编的两阶段提交协议来保证各个节点的事务一致性,半数以上的参与者回复yes即可

广播模式:

广播模式就是指zk正常工作的模式。正常状况下,一个写入命令会通过以下步骤被执行:

**步骤一:**Leader从客户端或者Follower那里收到一个写请求

**步骤二:**Leader生成一个新的事务并为这个事务生成一个唯一的ZXID

**步骤三:**Leader将这个事务以Proposal形式发送给全部的Follower节点

**步骤四:**Follower节点将收到的事务请求加入队列,并发送ACK给Leader

**步骤五:**当Leader收到大多数Follower的ACK消息,会发送Commit请求

**步骤六:**当Follower收到Commit请求时,会判断该事务的ZXID是否是比队列中任何事务的ZXID都小来决定Commit

恢复模式:

当第一次启动集群时,先启动的过半机器中ZXID、myid最大的为Leader。当Leader故障时,zk集群进入恢复模式,此时zk集群不能对外提供服务。此时必须选出一个新的Leader完成数据一致后才能从新对外提供服务,zk官方宣称集群能够在200毫秒内选出一个新Leader。

正常模式下的几个步骤,每一个步骤都有可能由于Leader故障而中断,可是恢复过程只与Leader有没有Commit有关。

首先看前三个步骤,只做了一件事,把事务发送出去。若是事务没有发出去,全部Follower都没有收到这个事务,Leader故障了,全部的Follower都不知道这个事务的存在。

根据心跳检测机制,Follower发现Leader故障,需要重新选出一个Leader。此时会根据每一个节点ZXID来选择。谁的ZXID最大,表示谁的数据最新,就会被选举成新的Leader。若是ZXID都同样,那么就表示在Follower故障以前,全部的Follower节点数据一致,此时选择myid最大的节点成为新的Leader。

所以由于有一个固定的选举标准会加快选举流程,新的Leader选出来后,全部节点的数据同步一致后就能够对外提供服务。

假设新的Leader选出来以后,原来的Leader又恢复了,此时原来的Leader会自动成为Follower。

以前的事务即便发送给新的Leader,由于新的Leader已经开启了新的纪元,而原先的Leader中ZXID仍是旧的纪元,所以该事务就会被丢弃,而且该节点的ZXID也会更新成新的纪元。纪元就是标识当前Leader是第几任Leader,至关于改朝换代时候的年号。

若是在Leader故障以前已经Commit,zk会根据ZXID或者myid选出数据最新的那个Follower作为新的Leader。

新Leader会为Follower创建FIFO的队列,首先将自身有而Follower缺失的事务发送给该队列,然后再将这些事务的Commit命令发送给Follower,这样便保证了全部的Follower都保存了全部的事务数据。

ZAB协议确保那些已经在Leader提交的事务最终会被全部服务器提交,ZAB协议确保丢弃那些只在Leader提出或复制,但是没有提交的事务。

相关推荐
东阳马生架构13 小时前
zk基础—1.一致性原理和算法二
zookeeper·分布式原理
东阳马生架构18 小时前
zk基础—2.架构原理和使用场景
zookeeper
Julian.zhou1 天前
MCP服务:五分钟实现微服务治理革命,无缝整合Nacos/Zookeeper/OpenResty
人工智能·微服务·zookeeper·交互·openresty
千航@abc1 天前
zookeeper的作用介绍
分布式·zookeeper·云原生·云原生组件
酷爱码2 天前
zookeeper详细介绍以及使用
分布式·zookeeper·云原生
Hadoop_Liang2 天前
openEuler24.03 LTS下安装Kafka集群
zookeeper·kafka·openeuler
伊泽瑞尔ts4 天前
Zookeeper特性与节点数据类型
分布式·zookeeper·云原生
:)小白学编程4 天前
zookeeper部署教程
分布式·zookeeper·debian