系列目录:
-
《分布式事务(一)—— 事务的基本概念》
-
《分布式事务(二)—— CAP和Base理论》
一、常见分布式事务解决方案
- 两阶段提交(2PC,Two-phase Commit)
- TCC补偿模式
- 基于本地消息表实现最终一致性
- 基于可靠消息最终一致方案
- 最大努力通知
二、两阶段提交(2PC)
两阶段提交方案在目前的分布式事务中只是一种方案,因为其比较复杂,且问题比较多,实际使用的比较少,但是我们也可以进行了解。
2PC是一个非常典型的中心化原子提交协议:这里所说的中心化指协议中有两类节点,一个是中心化的协调节点(Coordinator)和N个参与者节点(Partcipant).
两个阶段指的是:
- 第一阶段:投票阶段
- 第二阶段:提交/执行阶段
两阶段提交的设计思路就是在N个调用的节点的上面加上一个协调的节点。在发生事务操作的时候,先让协调节点给每个需要参与的节点发送投票数据,也就是先发送一个预处理,等待所有的节点返回ok以后,再让协调节点给每个节点发送执行,如果在第一个阶段有返回错误,就发送回滚。
下面举个示例说明:
比如订单服务A需要调用支付服务B去支付,支付成功则处理购物订单为待发货状态,否则需要将订单设置为失败状态。
那么看看2PC是如何处理的
1、第一阶段:投票阶段
第一阶段主要有3步:
- 1、事务询问
协调者向所有参与者发送事务预处理请求,称为Prepare,并等待各参与者的响应。 - 2、执行本地事务
各个参与者节点执行本地事务操作,但在执行完成后并不会真正提交数据库本地事务,而是先向协调者报告这里能不能处理的状态 - 3、各个参与者向协调者反馈事务询问的响应
如果执行者成功执行了事务操作,那么久返回给协调者Yes响应,表示事务可以执行,如果没有参与者成功执行事务,那么就返回给协调者No响应,表示事务不可以执行
第一阶段执行完后,会有两种可能:1、所有都返回Yes,3、有一个或者多个返回No
2、第二阶段:提交/执行阶段(成功流程)
成功条件:所有参与者都返回Yes
主要分为两步:
- 1、协调者发送给各参与者提交事务Commit请求
- 2、各个参与者收到协调者发送的Commit请求后提交本地事务,并在完成提交之后释放整个事务执行期间占用的事物资源。
3、第二阶段:提交/执行阶段(异常流程)
异常条件:任何一个参与者向协调者反馈了No响应,或者等待超时之后,协调者尚未收到所有参与者的反馈响应。
异常流程也分为两步:
- 1、协调者向所有参与者发出RollBack请求
- 2、参与者收到RollBack请求后,回滚本地事务
4、2PC的缺点
-
- 性能问题
无论是第一阶段的过程中,还是第二阶段,所有的参与者资源和协调者资源都是被锁住的,只有当所有节点执行完毕,事务协调者才会通知全局提交。参与者进行本地事务提交后才会释放资源,这样的过程比较漫长,对性能影响比较大。
- 性能问题
-
- 单节点故障
由于协调者的重要性,一旦协调者发生故障,参与者会一直阻塞下去,尤其是在第二阶段,协调者发生故障,那么所有参与者还都处于锁定事务的状态,而无法完成事务提交。
2PC出现单点故障问题的三种情况。
- (1) 协调者正常,参与者故障
由于协调者无法收集到所有参与者的反馈,会陷入阻塞状态。
解决方案:引入超时机制,如果协调者在超过指定的时间还没有收到参与者的反馈,事务就失败,向所有节点发送终止事务请求。 - (2) 协调者故障,参与者正常
无论处于哪个阶段,如果协调者故障,无法发送提交或者回滚请求,所有处于执行了操作但是未提交状态的参与者都会陷入阻塞状态。
解决方案:引入协调者备份,同时协调者需要记录操作日志,当检测到协调者故障一段时间后,协调者备份取代协调者,并读取操作日志,向所有参与者询问状态。 - (3) 协调者和参与者都故障
如果发生在第一个阶段,所有参与者都没有真正执行commit,所以只需要重新选出协调者,新的协调者重新执行第一阶段和第二阶段就可以了。
如果发送在第二阶段并且挂了的参与者在挂掉之前没有收到协调者的指令,这是可能协调者还没有发送指令就挂了,这种情况下,新的协调者重新执行第一阶段和第二阶段就行。
但是如果在协调者发出了指令,有的协调者执行commit成功了,有的挂了,这个时候就会出现数据不一致,虽然可以重新执行,但是原来执行成功的数据无法回退,2PC无法解决这个问题
- 单节点故障
从上面可以看出,2PC实现很复杂,而且会引入很多的新问题,所以实际中使用的比较少。
后记
个人总结,欢迎转载、评论、批评指正