为了解决分布式系统的一致性问题,在长期的探索研究过程当中,涌现出了一大批经典的一致性协议和算法,其中最著名的就是二阶段提交协议、三阶段提交协议和Paxos算法。算法
在分布式系统中,每个机器节点虽然都可以明确地知道本身在进行事务操做过程当中的结果是成功或失败,但却没法直接获取到其余分布式节点的操做结果。所以,当一个事务操做须要跨越多个分布式节点的时候,为了保持事务处理的ACID特性,就须要引入一个称为”协调者“的组件来统一调度全部分布式节点的执行逻辑,这些被调度的分布式节点则被称为“参与者”。协调者负责调度参与者的行为,并最终决定这些参与者是否要把事务真正进行提交。基于这个思想,衍生出了二阶段提交和三阶段提交两种协议。数据库
2PC,是Two-Phase Commit的缩写,即二阶段提交,是计算机网络尤为是在数据库领域内,为了使基于分布式系统架构下的全部节点在进行事务处理过程当中可以保持原子性和一致性而设计的一种算法。一般,二阶段提交协议也被认为是一种强一致性协议,用来保证分布式系统数据的一致性。网络
二阶段提交协议是将事务的提交过程拆分红了两个阶段来进行处理,其执行流程以下:架构
一、事务询问分布式
协调者向全部的参与者发送事务内容,询问是否能够执行事务提交操做,并开始等待各参与者响应。学习
二、执行事务计算机网络
各参与者节点执行事务操做,并将Undo和Redo信息记入事务日志中。设计
三、各参与者向协调者反馈事务询问的响应日志
若是参与者成功执行了事务操做,那么就反馈给协调者Yes响应,表示事务能够执行;若是参与者没有成功执行事务,那么就反馈给协调者No响应,表示事务不能够执行。事务
协调者会根据各参与者的反馈状况来决定最终是否能够进行事务提交操做,正常状况下,包含如下两种可能:
执行事务提交:假如协调者从全部的参与者得到的反馈都是Yes响应,那么就会执行事务提交。
一、 发送提交请求
协调者向全部参与者节点发出Commit请求。
二、事务提交
参与者接收到Commit请求后,会正式执行事务提交操做,并在完成提交以后释放在整个事务执行期间占用的事务资源。
三、 反馈事务提交结果
参与者在完成事务提交以后,向协调者发送Ack消息。
四、完成事务
协调者接收到全部参与者反馈的Ack消息后,完成事务。
中断事务:假如任何一个参与者向协调者反馈了No响应,或者在等待超时以后,协调者尚没法接收到全部参与者的反馈响应,那么就会中断事务。
一、发送回滚请求
协调者向全部参与者节点发出Rollback请求。
二、事务回滚
参与者接收到Rollback请求后,会利用其在阶段一中记录的Undo信息来执行事务回滚操做,并在完成回滚以后释放在整个事务执行期间占用的资源。
三、反馈事务回滚结果
参与者在完成事务回滚以后,向协调者发送Ack消息。
四、中断事务
协调者接收到全部参与者反馈的Ack消息后,完成事务中断。
简单地讲,二阶段提交将一个事务的处理过程分为了投票和执行两个阶段,其核心是对每一个事务都采用先尝试后提交的处理方式,所以也能够将二阶段提交看做一个强一致性的算法。
一、同步阻塞
在执行过程当中,全部参与该事务操做的逻辑都处理于阻塞状态。即节点之间在等待对方的相应消息时,它将什么也作不了。特别是,当一个节点在已经占有了某项资源的状况下,为了等待其余节点的响应消息而陷入阻塞状态时,当第三个节点尝试访问该节点占有的资源时,这个节点也将连带陷入阻塞状态。
二、 单点问题
一旦协调者出现问题,那么整个二阶段提交流程将没法运转,更为严重的是,若是协调者是在阶段二中出现问题的话,那么其余参与者将会一直处于锁定事务资源的状态中,而没法继续完成事务操做。
三、 数据不一致
当协调者向全部的参与者发送Commit请求以后发生了局部网络异常或者是协调者在还没有发送完Commit请求以前自身发生了崩贵,致使最终只有部分参与者收到了Commit请求,因而整个分布式系统便出现了数据不一致性现象。
三、太过保守
二阶段提交没有设计较为完善的容错机制,任意一个节点的失败都会致使整个事务的失败。
3PC,是Three-Phase Commit的缩写,即三阶段提交,是2PC的改进版。由CanCommit、PreCommit和doCommit三个阶段组成的事务处理协议。
一、事务询问。
协调者向全部的参与者发送一个包含事务内容的canCommit请求,询问是否能够执行事务提交操做,并开始等待各参与者的响应。
二、各参与者向协调者反馈事务询问的响应。
参与者在接收到来自协调者的canCommit请求后,正常状况下,若是其自身认为能够顺利执行事务,那么会反馈Yes响应,并进入预备状态,不然反馈No响应。
协调者会根据各参与者的反馈状况来决定最终是否能够进行事务提交操做,正常状况下,包含如下两种可能:
执行事务预提交;假如协调者从全部的参与者得到的反馈都是Yes响应,那么就会执行事务的预执行。
一、发送预提交请求
协调者向全部参与者节点出preCommit的请求,并进入prepared阶段。
二、事务预提交
参与者接收到preCommit请求后,会执行事务操做,并将Undo和Redo信息记录到事务日志中。
三、各参与者向协调者反馈事务执行的响应
若是参与者成功执行了事务操做,那么就会反馈给协调者Ack响应,同时等待最终的指令:提交(commit)或停止(abort)。
中断事务:假如任何一个参与者向协调者反馈了No响应,或者在等待超时以后,协调者尚没法接收到全部者的反馈响应,那么就会中断事务。。
一、发送中断请求
协调者向全部参与者节点发生abort请求。
二、中断事务
不管是收到来自协调者的abort请求,或者是在等待协调者请求过程当中出现超时,参与者都会中断事务。
该阶段将进行真正的事务提交,会存在如下两种可能的状况。
执行提交
一、发送提交请求
进入这一阶段,假如协调者处于正常状态,而且它接收到了来自全部参与者的Ack响应,那么它将从“预提交”状态转换到“提交”状态,并向全部参与者发送doCommit请求。
二、事务提交
参与者接收到doCommit请求后,会正式执行事务提交操做,并在完成提交以后释放在整个事务执行期间占用的事务资源。
三、反馈事务提交结果
参与者在完成事务提交以后,向协调者发送Ack消息。
四、完成事务
协调者接收到全部参与者反馈的Ack消息后,完成事务。
中断事务
进入这一阶段,假设协调者处于正常工做状态,而且有任意一个参与者向协调者反馈了No响应,或者在等待超时以后,协调者尚没法接收到全部参与者的反馈响应。
一、发送中断请求
协调者向全部的参与者节点发送abort请求。
二、事务回滚
参与者接收到abort请求后,会利用其在阶段二中记录的Undo信息来执行事务回滚操做,并在完成回滚以后释放在整个事务执行期间占用的资源。
三、反馈事务回滚结果
参与者在完成事务会滚以后,向协调者发送Ack消息。
四、中断事务
协调者接收到全部参与者反馈的Ack消息后,中断事务。
在doCommit阶段,若是参与者没法及时接收到来自协调者的doCommit或者abort请求时,在等待超时以后,会继续进行事务的提交。即当进入第三阶段时,因为网络超时等缘由,虽然参与者没有收到协调者的doCommit或者abort请求,但事务仍然会提交。
三阶段提交协议与两阶段提交协议有两个主要的不一样:
增长了一个询问阶段,询问阶段能够确保尽量早的发现没法执行操做而须要停止的行为,可是它并不能发现全部的这种行为,只会减小这种状况的发生。
在准备阶段之后,协调者和参与者执行的任务中都增长了超时,一旦超时,协调者和参与者都继续提交事务,默认为成功,这也是根据几率统计上超时后默认成功的正确性最大。
三阶段提交协议在去除阻塞的同时也引入了新的问题,那就是在参与者接收到preCommit消息后,若是网络出现分区,此时协调者所在的节点和参与者没法进行正常的网络通讯,在这种状况下,该参与者依然会进行事务的提交,这必然出现数据的不一致性。
Paxos算法是一种基于消息传递且具备高度容错特性的一致性算法,是目前公认的解决分布式一致性问题最有效的算法之一。
在古希腊的有一个叫作Paxos的小岛,岛上采用会议的形式来经过法令,议会中的议员经过信使进行消息的传递。值得注意的是,议员和信使都是兼职的,他们随时有可能会离开议会厅,而且信使可能会重复的传递信息,也可能一去不复返。所以,议会协议要保证在这个状况下法令仍然可以正确的产生,而且不会出现冲突。
首先将议员的角色分为Proposer,Acceptor,和Learner(容许身兼数职)。Proposer提出提案,提案信息包括提案编号和提议的value;Acceptor收到提案后能够接受(accept)提案,若是提案得到多数Acceptors的接受,则称该提案被批准(chosen);Learner只能“学习”被批准的提案。划分角色后,就能够更精确的定义问题:
决议(value)只有在被Proposers提出后才能被批准(未经批准的决议称为“提案”(proposal);
在一次Paxos算法的执行实例中,只批准(chosen)一个value;
learners只能得到被批准(chosen)的value。
由上面的三个语义可演化为下面几个约束:
P1:一个Acceptor必须接受(accept)第一次收到的提案。
P2:一旦一个具备value v的提案被批准(chosen),那么以后批准(chosen)的提案必须具备value v。
P2a:一旦一个具备value v的提案被批准(chosen),那么以后任何Acceptor再次接受(accept)的提案必须具备value v。
P2b:一旦一个具备value v的提案被批准(chosen),那么之后任何Proposer提出的提案必须具备value v。
P2c:若是一个编号为n的提案具备value v,那么存在一个多数派,要么他们中全部人都没有接受(accept)编号小于n的任何提案,要么他们已经接受(accept)的全部编号小于n的提案中编号最大的那个提案具备value v。
P1a:当且仅当Acceptor没有回应过编号大于n的prepare请求时,Acceptor接受(accept)编号为n的提案。
阶段一(决议提出)
一、Proposer选择一个提案编号M,而后向Acceptors的某个超过半数的子集成员发送编号为M的prepare请求。
二、若是一个Acceptor接收到一个编号为M的pepare请求,且编号M大于该Acceptor已经响应的全部prepare请求的编号,那么它就会将它已经批准过的最大编号的提案做为响应反馈给Proposer,同时该Acceptor会承诺不会再批准任何编号小于M的提案。
阶段二(批准阶段)
一、若是Proposer收到来自半数以上的Acceptor对其发出的编号为M的prepare请求的响应,那么它就会发送一个针对[M,V]提案的accept请求给Acceptor。注意,V的值就是收到响应中编号最大的提案的值,若是响应中不包含任何提案,那么它就是任意值。
二、若是Acceptor收到这个针对[M,V]提案的Accept请求,只要该Acceptor还没有对编号大于M的prepare请求作出响应,它就能够经过这个提案。
如何让Learner获取提案,大致能够有如下几种方案:
方案一
Learner获取一个已经被选定的提案的前提是,该提案已经被半数以上的Acceptor批准了。所以,最简单的作法就是一旦Acceptor批准了一个提案,就将该提案发送给全部的Learner。这种作法虽然可让Learner尽快地获取被选定的提案,可是却须要让每一个Acceptor与全部Learner逐个进行一次通讯,通讯的次数至少为两者个数的乘积。
方案二
咱们可让全部的Acceptor将它们对提案的批准状况,统一发送给一个特定的Learner(这样的Learner称为“主Learner”),假定Learner之间能够经过消息通讯来互相感知提案的选定状况。当主Learner被通知一个提案被选定时,它会负责通知其余的Learner。
方案二虽然须要多一个步骤才能将提案通知到全部的Learner,但其通讯次数却大大减小了,一般只是Acceptor和Learner的个数总和。但同时,该方案引入了一个新的不稳定因素;主Learner随时可能出现故障。
方案三
将主Learner的范围扩大,即Acceptor能够将批准的提案发送给一个特定的Learner集合,该集合中的每一个Learner均可以在一个提案被选定后通知全部其余的Learner。这个Learner集合中的Learner个数越多,可靠性就越好,但同时网络通讯的复杂度也就越高。
假设存在这样一种极端状况,有两个Proposer依次提出了一系列编号递增的议案,可是最终都没法被选定,具体流程以下:
Proposer P1提出了一个编号为M1的提案,并完成了上述阶段一的流程。但与此同时,另一个Proposer P2提出了一个编号为M2(M2>M1)的提案,一样也完成了阶段一的流程,因而Acceptor已经承诺再也不批准编号小于M2的提案了。所以,当P1进入阶段二的时候,其发出的accept请求将被Acceptor忽略,因而P1再次进入阶段一并提出了一个编号为M3,(M3>M2)的提案,而这又致使P2在第二阶段的accept请求被忽略,以此类推,提案的选定过程将陷入死循环。
为了保证Paxos算法流程的可持续性,以免陷入上述提到的“死循环”,就必须选择一个主Proposer,并规定只有主Proposer才能提出议案。这样一来,只要主Proposer和过半的Acceptor可以正常进行网络通讯,那么但凡主Proposer提出一个编号更高的提案被提出或正在接受批准,那么它会丢弃当前这个编号较小的提案,并最终可以选出一个编号足够大的提案。所以,若是系统中有足够多的组建(包括Proposer、Acceptor和其余网络通讯组建)可以正常工做,那么经过选择一个主Proposer,整套Paxos算法流程就可以保持活性。
从Paxos到Zookeeper分布式一致性原理与实践