TCC<br>又称补偿事务,与2PC思想和处理流程都很相似<br>2PC应用于DB层面,TCC可以理解为应用层面的2PC,需要我们编写业务逻辑实现
Try阶段:调用Try接口,尝试执行业务,完成所有业务检查,预留业务资源。例如:下单时通过try操作去扣除库存资源
Confirm阶段或Cancel阶段:
两者互斥只能进一个,且都需满足幂等性,允许失败重试
Comfirm:对业务做确认提交
Cancel:业务执行错误时,回滚执行业务取消,释放预留资源
失败如何处理:TCC会添加事务日志,如果Confirm或Cancel阶段出错,则会进行重试,所以这两个阶段需要支持幂等;如果重试失败,需要人工介入进行恢复和处理
缺点
空回滚
如果服务发生宕机或网络异常,导致未执行try方法,当服务恢复后一般会进入cancel阶段,如果cancel不能处理此情况就会出现空回滚
解决方案:分支事务表<br>(seata的AT模式就使用了分支事务表)
1.主业务发起事务时,生成全局事务唯一id,再创建一张分支事务表,用于记录分支事务
2.执行try时,将全局事务id和分支事务id存入分支事务表中,表示某个服务执行了try方法
3.当执行cancel时,通过通过表来判断分支事务是否执行,有则回滚,否则不做操作
幂等性
如果服务宕机或网络问题导致方法调用超时,为了保证事务正常执行往往需要重试,因此需要保证confirm或cancel操作的幂等性
悬挂
如果执行Try接口时网络波动超时,使得触发cancel接口回滚,在回滚之后,try方法又被执行了,这就导致cancel接口比try接口先执行,从而造成资源一直被锁无法释放
解决方案:与空回滚类似,在try执行时判断是否执行了confirm或cancel
SAGA
Saga是由一系列的本地事务构成<br>每一个本地事务在更新完数据库后,会发布一条消息或一个事件来触发Saga中的下一个本地事务的执行<br>如果一个本地事务因某些原因失败,Saga会执行在这个失败的事务之前成功提交的所有事务的补偿操作
基于事件的方法
一个事务中,每个服务执行业务后,都发布一个事件。例如:订单服务创建订单后,发布一个订单创建的事件
其他服务监听到某个事件后,就执行业务逻辑,然后再发布一个事件。例如:库存服务监听到订单创建的事件,就执行库存扣减操作,并发布一个事件,让支付服务监听并执行扣款操作
如果某个流程出现错误,就执行预留的失败补偿接口
优点:简单易理解,且完全解耦,适用于步骤较少的事务
缺点:如果涉及调用方过多,就容易失控,甚至最后搞混各个事件和各个服务的逻辑关系,而且容易产生环形监听(也就是两个服务互相监听对方的事件)
基于命令的方法
定义了一个新的服务:协调中心,该服务通过命令/回复的方式来和Saga中其他服务进行交互
优点
避免了服务间的环形依赖
将分布式事务的管理交由协调中心管理,协调中心对整个逻辑非常清楚
减少了服务间的复杂度,各服务不再需要监听不同的消息,只需要响应命令并回复消息
测试和回滚更容易
缺点:需要维护协调中心,而这个协调中心并不属于任何业务方
本地消息表
核心思路:将分布式事务拆分称本地事务进行处理
1.事务发起方通过本地事务保证业务和消息表同时写入成功
2.事务发起方启动定时任务轮询消息表,通过MQ的发送确认模式,保证消息投递到MQ中(RocketMQ,kafaka等)
3.事务被动方消费消息执行业务,并返回
4.事务主动方更新消息状态为成功
需要做对应的失败重试机制,以及保证幂等性
消息事务<br>(RocketMQ4.3)<br>
基于MQ的分布式事务方案其实是对本地消息表的封装,将本地消息表基于MQ内部,其他方面的协议与本地消息表一致
1.发送方向MQ服务端发送half消息,MQ服务端将消息状态标记为Prepared(预备状态),此时订阅方是无法消费该消息的
2.MQ服务端将消息持久化成功后,向发送方发送ack消息表示该消息已成功发送到MQ
3.发送方执行本地事务,并根据结果向MQ服务端提交二次确认(commit或rollback)
4.MQ服务端收到commit状态则将消息标记为可消费,订阅方将正常消费该消息<br> 收到rollback状态则删除该消息,订阅方就不会消费该消息
事务回查:指的是,发送方在执行本地事务超时或断网,MQ服务端未收到二次确认,此时MQ会查询发送方事务执行状态,根据结果来判断是commit或rollback
优点:相较于本地消息表吞吐量更大,且可以降低业务系统与消息系统间的耦合(不需要在业务中手动编写本地消息表相关的逻辑代码)
缺点:一次消息发送需要两次网络请求(half消息 + commit/rollback消息),发送方需要实现消息状态回查接口
使用:需要继承RocketMQLocalTransactionListener并实现executeLocalTransaction(执行本地事务,也就是业务逻辑和事务日志表)和checkLocalTransaction(用于消息回查)两个方式<br>1.调用接口,生产者发送MQ半事务消息(此时消息不会被消费者察觉)<br>2.MQ服务端收到消息并响应生产者,执行executeLocalTransaction方法(本地业务逻辑+事务日志),生产者返回提交或回滚(回滚的话,MQ服务器删除消息就结束)<br>3.生产者返回COMMIT或ROLLBACK,或者网络问题宕机超时等无法发送出消息<br>4.MQ服务端未收到响应,则执行checkLocalTransaction方法(事务回查),并返回COMMIT或ROLLBACK<br>4.COMMIT则标记消息可投递,消费者消费消息(消费者消费消息失败的话,会重试,实现不行则人工介入,所以消费者需要保证幂等性)<br>4.ROLLBACK则流程结束,消费不会被消费者消费,且在一定时间后删除<br>
最大努力通知方案
发起方通知方通过一定的机制最大努力将业务处理结果通知到接收方
有一定的消息重复通知机制
消息校对机制
适用于业务通知类型的场景,例如:微信支付的结果,通过最大努力通知方式通知给商户,既有回调通知,也有交易查询接口