在之前的文章中我们介绍了关于二阶段提交协议的设计和实现原理,并且介绍了二阶段提交协议在使用过程中的可能存在的问题,例如同步阻塞、协调者单点问题、脑裂等等。因此在二阶段提交协议的基础上行出现了三阶段提交协议。下面我们就来详细介绍一下三阶段提交。
三阶段提交协议
三阶段提交,是Three-Phase Commit的缩写,是二阶段提交的改进版本,是将而阶段提交协议的提交事务请求的过程分为了两部分执行,三阶段提交由CanCommit、PreCommit和DoCommit三个阶段组成,如下图所示。
第一阶段:CanCommit
1、事务询问
协调者向所有的参与者发送一个包含事务内容的CanCommit请求,并且询问是否可以执行提交事务的操作,并且开始等待参与者的响应。
2、各个参与者向协调者反馈事务询问的响应
参与者在收到了来自协调者的CanCommit请求之后,在正常情况下,如果是可以执行事务操作,那么就会返回成功,并且进入到准备阶段状态,如果不能执行事务,就会反馈失败。
第二阶段:PreCommit
在第二个阶段,协调者会根据各个参与者所反馈的情况,最终来决定是否执行事务的PreCommit操作,在正常情况下,会有两种可能的执行结果。
事务执行预提交
假设协调者从所有的参与者中获得到反馈内容都是成功,那么就会执行事务执行的预提交操作。
1、发送预提交请求
协调者向所有的参与者节点发送PreCommit请求。并且进入到Prepared阶段
2、事务预提交
参与者收到了PreCommit请求之后,会执行事务操作,并且将Undo和Redo的信息记录到日志信息中。
3、参与者向协调者反馈事务执行情况
如果参与者都成功的执行了事务操作,就会给协调者反馈成功的响应,同时会等待最终的提交或者是终止的指令。
中断事务
假设在任何一个参与者在向协调者反馈消息的时候只要有一个反馈了失败,或者是在等待超时之后也没有反馈,那么就会执行事务中断操作。
1、发送中断请求
协调者向所有的参与者节点发送中断请求
2、中断事务
参与者无论是否收到中断请求,或者是出现等待超时的情况,都会中断自身事务操作。
第三阶段:DoCommit
在这个阶段开始真正提交事务请求,一般会出现如下的两种情况
执行提交请求
1、发送提交请求
在进入到这一阶段之后,假设协调者处于一个正常的工作状态,并且接收到了所有的参与者反馈的正常请求,这个时候它的状态就会从预提交转换为提交,并且向所有的参与者发送DoCommit请求。
2、事务提交
参与者在接收到了DoCommit请求之后,会正式的执行事务的提交操作,并且在提交完成之后释放资源。
3、反馈事务执行结果
参与者在完成事务正常操作之后,反馈执行事务的结果给协调者
4、完成事务
协调者在接收到参与者反馈的消息之后,完成整个事务的执行。
中断事务
进入到这个阶段,假设协调者是正常的工作状态,并且有任意的参与者反馈失败的响应,或者是在等待超时之后也没有响应,协调者还没有收到参与者的消息,那么就会执行事务的中断。
1、发送中断事务请求
协调者向所有的参与者发送事务中断请求
2、事务回滚
参与者收到了事务中断请求之后,会利用Undo的日志来执行事务的回滚,并且释放在回滚完成之后的资源
3、反馈事务结果
参与者在完成了事务回滚之后,向协调者反馈消息
4、中断事务
协调者在接收到所有参与者的反馈之后,中断事务执行。
这里需要注意的是,在进入到了第三阶段之后,与二阶段提交的情况类似,会出现两种故障情况。
第一、协调者出现问题,收不到反馈。
第二、协调者和参与者之间的网络出现故障
无论以上那种情况的出现,都会导致参与者无法接到来自协调者的DoCommit请求或者是接收不到中断请求,那么基于这种情况的发生,一般的操作就是再参与者完成了等待超时之后,继续进行事务操作。
优缺点
整体来讲,三阶段提交相对于二阶段提交来讲,最大的优势就是降低了参与者的阻塞范围,并且在出现了单点故障之后仍然可以达成一致。
而三阶段提交的缺点也是显然易见,三阶段提交协议去除了阻塞的同时引入了新的问题,如果在完成第二阶段参与者收到了PreCommit请求之后,如果这个时候出现了网络分区的情况,协调者存在的节点与参与者所在的节点出现了网络故障,那么这个时候,就会出现数据不一致的情况。