一致性协议
在分布式系统中,每一个机器节点都能明确知道自己在进行事务操作中的结果是成功还是失败,但是无法直接获取到其他分布式节点操作的结果.因此当一个事务操作需要跨越多个分布式节点的时候,为了保持事务处理的ACID特性,就需要引入一个叫协调者"Coordinator"的组件来统一调度所有分布式节点的执行逻辑,这些被调度的分布式节点被称为参与者(Participant).协调者负责调度参与者的行为,并最终决定这些参与者是否要把事务真正进行提交.基于这个思想,衍生除了二阶段提交(2PC)和三阶段提交(3PC)两种协议.
二阶段提交 2PC
2PC,是TWO-PHASE-COMMIT的缩写,即是二阶段提交,适用于分布式系统下节点在进行事务处理过程中能保持原子性和一致性而设计的算法.通常情况下,二阶段提交也被认为是一种一致性协议,用来保证分布式数据的一致性.二阶段提交协议是将事务的提交分为了两个阶段来处理.
阶段一:提交事务请求(又称为投票阶段)
1.事务询问
协调者向所有的参与者发送事务内容,询问是否可以进行执行事务提交操作.并开始等待各参与者的响应.
2.执行事务
各个参与者节点执行事务操作,并讲Undo和Redo信息记入事务日志中.
3.各参与者向协调者反馈事务询问的响应.
如果参与者成功执行了事务操作,那么就反馈给协调者Yes响应,表示事务可以执行,如果没有参与者成功执行事务,那么就反馈给协调者No响应,表 示事务不可以执行.
阶段二:执行事务提交
在阶段二中,协调者会根据各参与者的反馈情况来决定最终是否可以进行事务提交操作,正常情况下有两种可能:
可能性一:执行成功(条件:所有参与者给协调者的信息都是YES)
1.所有的参与者反馈给协调者的信息都是Yes,那么就会执行事务提交.
协调者向所有参与者节点发出Commit请求.
2.事务提交
参与者收到Commit请求之后,就会正式执行事务提交操作,并在完成提交之后释放整个事务执行期间占用的事务资源.
3.反馈事务提交的结果
参与者在完成事务提交之后,向协调者发送ACK消息.
4. 完成事务
协调者接收所有参与者反馈的Ack消息,完成事务.
可能性二:执行失败,中断事务(任何一个参与者向协调者反馈了No响应,或者等待超时之后,协调者尚未收到所有参与者的反馈响应,就会中断事务)
1.发送回滚请求
协调者向所有参与者节点发出RoollBack请求.
2.事务回滚
参与者接收到RoollBack请求后,会利用其在阶段一中记录的Undo信息来
3.反馈事务回滚结果
参与者在完成事务回滚之后,向协调者发送ACK消息.
4.中断事务
协调者收到所有参与者反馈的ACK消息后,完成事务中断.
2PC小结:
二阶段提交将一个分布式事务处理过程分为了投票和执行两个阶段,其核心是对每个事务都采用先尝试后提交的处理方式.因此可以讲二阶段提交看作是一个强一致性算法.这样的2PC算法.原理简单.但是这样同时也带来了很多问题.
2PC可能存在的问题:
1.同步阻塞
无论是在第一阶段投票的过程中还是在第二阶段执行的阶段,所有的参与者资源和协调者资源都是被锁住的,在实际应用中非常容易造成长时间阻塞,这个问题是可以通过超时判断来解决.
2.单点问题(single point of failure)
指的是单个点发生故障的时候会波及到整个系统和网络.
2PC出现单点问题的三种情况:
(1)协调者正常,参与者宕机.
发生在第一阶段:由于协调者无法收集到所有参与者的反馈,会陷入阻塞情况。
解决方案:引入超时机制,如果协调者在超过指定的时间还没有收到参与者的反馈,事务就失败,向所有节点发送终止事务请求.
(2)协调者宕机,参与者正常.
无论处于哪个阶段,由于协调者宕机,无法发送提交请求,所有处于执行了操作但是未提交状态的参与者都会陷入阻塞情况.
解决方案:引入协调者备份,同时协调者需记录操作日志.当检测到协调者宕机一段时间后,协调者备份取代协调者,并读取操作日志,向所有参与者询问状态。
(3)协调者和参与者都宕机
这可能就比较无语了,只有重启后恢复状态看看.
3.脑裂(split of brain)
指在一个高可用系统中,当联系着的两个节点断开时,本来为一个整体的系统.分裂成为两个独立节点,这时两个节点开始争抢共享资源,结果会导致系统混乱,数据损坏.
2PC中出现的脑裂
在提交阶段中,如果只有部分参与者接收并执行了Commit请求,会导致节点数据不一致.比如上面提到的第三种情况:所有的参与者和协调者都挂掉.
解决方案:
(1)仲裁
当两个节点出现分歧时,由第三方的仲裁者决定听谁的.
(2)fencing
当不能确定某个节点的状态时,通过fencing把对方干掉,确保共享资源被完全释放,前提是必须要有可靠的fence设备.
三阶段提交 3PC(Three-Phase-Commit)
3PC是2PC的改进版,其改进之处在于把二阶段提交协议的"提交事务请求"的第二阶段一分为二,形成了CanCommit,PreCommit接DoCommit三个阶段组成的事务处理协议.
阶段一: CanCommit
1.事务询问
协调者向所有的参与者发送一个包含事务内容的canCommit请求,询问是否可以进行事务提交操作.
2.各参与者向协调者反馈事务询问的响应
参与者在接收到来自协调者的canCommit请求后,如果自身认为可以顺利执行事务,那么会反馈YES响应,并且进入预备状态,否则反馈No.
阶段二:PreCommit
在阶段二中,协调者会根据参与者的反馈情况来决定是否可以进行事务的PreCommit操作,正常情况下.包含两种可能.
执行事务预提交
1.执行预提交请求
协调者向所有的参与者发出preCommit的请求,并进入Prepared阶段.
2.事务预提交
参与者接收到了preCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中.
3.各参与者向协调者反馈事务执行的响应
如果参与者成功执行了事务操作,那么就会反馈给协调者Ack响应,同事等待最终的指令:提交(commit)或中止(abort)
中断事务
假如任何一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务.
1.发送中断请求
协调者向所有参与者节点发出abort请求
2.中断事务
无论是收到来自协调者的abort请求,或者是在等待请求过程中超时,参与者都会中断事务.
阶段三:doCommit
该阶段将进行真正的事务提交,也会存在两种可能情况.
执行事务提交
1.发送提交请求
进入这一阶段,假设协调者处于正常工作的状态,并且它收到了来自所有参与者的ACK响应.那么就会从预提交转换到"提交"状态,并向所有的参与者发出doCommit请求.
2.事务提交
参与者接收到doCommit请求后.会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间所占用的资源.
3.反馈事务提交结果
参与者在完成事务提交之后,会向协调者发送ack消息.
4.完成事务
协调者接收到所有参与者反馈的ACK消息后,完成事务.
中断事务
进入到了这个阶段,假设协调者处于正常工作状态,任意有一个参与者向协调者反馈了no响应.或者超时之后协调者不能收到所有参与者的反馈响应,就会中断事务.
1.发送中断请求
协调者向所有的参与者节点发送abort请求.
2.事务回滚
参与者接收到abort请求之后,会利用其在阶段二中记录的Undol信息来执行事务回滚操作,并在完成事务回滚后释放所占用资源.
3.反馈事务回滚的结果.
参与者在完成事务回滚之后,向协调者发送ACK消息.
4.中断事务
协调者接收到所有参与者反馈的ACK消息后,中断事务.
总结:
3PC相对于2PC来说最大的改进就是降低了参与者的阻塞范围,并且能够在出现单点故障之后继续达成一致. 但是3PC这样的做法同时也引入了新的问题,那就是在参与者收到preCommit消息之后,如果出现了网络分区,协调者和参与者无法正常进行网络通信.那么在这种情况下,参与者依然会进行事务提交,这必然会导致数据出现不一致性.