本文主要是介绍分布式事务实战汇总,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
分布式方案
2PC 两阶段提交协议
- 两阶段提交协议:事务管理器分两个阶段来协调资源管理器,第一阶段准备资源,也就是预留事务所需的资源,如果每个资源管理器都资源预留成功,则进行第二阶段资源提交,否则协调资源管理器回滚资源。
- 我的一个实践是 seata的AT模式demo如下
基于springcloud nacos juejin.im/post/5f18ce…
基于dubbo nacos juejin.im/post/5f18e1…
- 基本流程图如下。
- seata的AT模式下的2pc方案对代码侵入性较小,回滚、确认的操作均由框架自动生成,但是会有行锁的存在。
最终一致性方案
方案一基于RocketMQ 事务消息
rocketmq有一个事务消息
具体来说,就是把消息的发送分成了2个阶段:Prepare阶段和确认阶段。
- (1) A系统发送Prepared消息
- (2) A系统执行本地事务
- (3) A系统根据update DB结果成功或失败,Confirm或者取消Prepared消息。
- (4) rocketmq轮询本地所有的未确认的prepare消息检查本地事务执行状态。
- (5) B系统收到confirm的prepare消息进行执行本地事务,如果失败返回稍后重试,直至成功。
- 流程图如下
- 这种最终一致性方案,比如a给b打钱,当a的钱扣除成功之后(扣除失败则本次失败),b则必须要加钱成功,如果失败,则要直到补偿成功。消费端消费失败怎么办?
消费失败了,重试,还一直失败怎么办?是不是要自动回滚整个流程?
答案是人工介入。从工程实践角度讲,这种整个流程自动回滚的代价是非常巨大的,不但实现复杂,还会引入新的问题。比如自动回滚失败,又怎么处理?
对应这种极低概率的case,采取人工处理,会比实现一个高复杂的自动化回滚系统,更加可靠,也更加简单。
- 我的一个实践是 demo如下
基于springcloud nacos juejin.im/post/5f1c0a…
方案二 本地做状态记录+单笔查询(对账)+调用方补偿重试
同样以上面的下单为例:调用方调订单系统下单,系统B扣库存,如何保证2个同时成功?
用户下单的时候,订单状态未 init,如果B系统接口是不幂等的(B系统扣库存时候需要记录相应订单流水)。
当用户调用系统B进行扣库存时候成功时候,状态修改为success
如果发现系统中某个订单状态一直为init状态,说明这条流水有问题,通过定时器定时load出来这些数据。
如果B系统接口是不幂等的需要调用B系统单笔查询接口查询是否已经扣过库存,根据查询结果修改订单状态。如果没有则进行补偿调用B系统根据调用结果,修改状态为successs或者fail 直至成功为止。
如果B系统接口幂等的 则进行补偿调用B系统根据调用结果,修改状态为successs或者fail 直至成功为止。
订单id |
订单状态 |
O_78923a2 |
init/success/fail |
方案三 本地做事务流水记录+调用方补偿重试
调用方维护1张事务状态表(或者说事务日志,日志流水),每次调用之前,落盘1条事务流水,生成1个全局的事务ID。表结构大致如下:
为止。
事务id |
入参 |
事务状态 |
O_78923a2 |
a |
init/success/fail |
,发现某条流水,在过了某个时间之后(假设1次事务执行成功通常最多花费30s),状态仍然是Init,那就说明这条流水有问题。就重新调用系统A,系统B,保证这条流水的最终状态是Success。当然,系统A, 系统B根据这个全局的事务ID,做幂等,所以重复调用也没关系。
这就是通过同步调用 + 后台任务异步补偿,最终保证系统一致性。
TCC 两阶段提交协议
TCC 模式需要用户根据自己的业务场景实现 Try、Confirm 和 Cancel 三个操作;事务发起方在一阶段执行 Try 方式,在二阶段提交执行 Confirm 方法,二阶段回滚执行 Cancel 方法。
TCC的不足之处则在于对应用的侵入性非常强,业务逻辑的每个分支都需要实现try、confirm、cancel三个操作。此外,其实现难度也比较大,需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。
这篇关于分布式事务实战汇总的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!