分布式事务
反向补偿
在分布式情况下,假如存在 A 同时调用 B、C多个微服务。假如 B 服务事务正常执行并提交,但是 C 事务提交失败,此时 B 和 C都需要回滚。
而 MySQL 的事务回滚是通过 redo log 机制来实现的,保证事务的持久化和一致性。
但是在分布式,使用了分布式事务的情况下,是通过一条更新SQL,还原原本的数据。
分布式事务解决方案
2PC - 两阶段提交
prepare - 准备阶段
各个参与者本地事务执行完成,向事务协调者提交本地事务状态。
commit/rollback - 提交/回滚阶段
如果所有参与者本地事务都执行完成,事务协调者则触发全体提交。
如果有一个参与者的本地事务执行失败,事务协调者则触发全体回滚。
缺点
同步阻塞
第一阶段之后,所有本地资源都要处于事务的锁定状态。
单点故障
事务协调者如果挂掉,整个事务无法进行。
数据不一致
如果网络故障影响到事务协调者下发命令,可能导致事务参与者的数据不一致问题。
3PC - 三阶段提交
3PC 的引入是为了解决 2PC 同步阻塞和减少数据不一致的情况。
3PC比2PC就多了一个询问阶段。
询问准备
预提交 - prepare
提交/回滚 - commit/rollback
加入一个询问节点,就像是在事务开始之前询问各个事务参与者的状态。保证大家都能达到起跑点,然后再开始事务的准备和提交。
就是在2PC基础上,增加一个保证各个事务参与者准备完成的阶段。
不管是 2PC 还是 3PC 都是依赖于数据库的事务提交和回滚。
TCC-补偿事务
TCC (Try-Confirm-Cancel)
TCC与2PC的思想很相似,事务处理流程也很相似,但2PC是应用于在DB层面,TCC则可以理解为在应用层面的2PC,是需要我们编写业务逻辑来实现。
TCC 分为两个阶段。
第一阶段是资源检查预留阶段即 Try。
第二阶段是 Confirm 或 Cancel,如果是提交的话就是执行真正的业务操作,如果是回滚则是执行预留资源的取消,恢复初始状态。
TCC问题
幂等实现
TCC的三个方法都会有重试机制,所以方法需要实现幂等性。
空回滚
如果try由于网络问题超时未执行,触发了Cancel方法。要确保Cancel在try未执行的情况正确执行。
防悬挂
指 Try 方法由于网络阻塞超时触发了事务管理器发出了 Cancel 命令,但是执行了 Cancel 命令之后 Try 请求到了。
本来事务都结束了,但是执行了try,后续也不会再执行。
需要我们在空回滚的情况下,记录一下已经Cancel,防止再执行try。
本地消息表+补偿事务
本地消息表的方案核心就是将分布式事务拆分为本地事务来执行。
通过本地事务来保证最终一致性。
事务A 写消息到本地消息表,消息状态为未完成。定时轮训消息状态,如果未成功会继续发给MQ。
通过MQ通知事务B去消费业务数据,事务B本地事务完成后,回调事务A的方法修改消息状态为已完成。
事务消息+补偿事务
利用RocketMQ的事务消息,既保证本地事务执行成功,又能保证事务消息能够投递成功。
如果其他事务执行失败,可以通过消息触发本地事务的回滚(补偿逻辑)。