分布式事务常见的集中解决方案
一、前言
1. 事务
事务是由一组操作构成的可靠的独立的工作单元,事务具备ACID的特性,即原子性、一致性、隔离性和持久性。
2. 本地事务
单机环境下的一致性解决方案就是本地事务,
本地事务的优点就是支持严格的ACID特性,高效,可靠,状态可以只在资源管理器中维护,而且应用编程模型简单。
3. 全局事务
区别于本地事务,常用语分布式环境下的事务管理。可以理解成,对各个单机的事务进行协调管理。要提交一起提交,要回滚一起会馆
二、方案
1. 最大努力通知
可以利用mq的ack机制。使用场景插入流水信息时候,数据不需要立刻处理,放到MQ中慢慢处理
- 系统A本地事务执行完后,发送一个消息到MQ
- 有一专门消费MQ的最大努力通知服务,会消费MQ,接着调用系统B的接口。
- 如果成功就消息消费成功
- 如果失败消息消费失败
2. 本地消息表
使用场景: 菜鸟tob仓库占用库存时候,会将数据插入到表中,然后定时扫描失败的,重新去发送消息。
缺点是B如果处理成功了,但是没有把状态改成功,导致A重复去发消息
- A系统在本地事务里面同时,插入一条数据到本地消息表,然后将消息发送出去
- B系统受到消息后,进行处理自己的事务,如果处理成功了就将A消息表里面的状态改为成功。
- A定时扫描消息表,把没有成功的重新发出消息。当然B要做好幂等操作,防止重复发消息
3. 消息最终一致性
最终一致性就不是说成功一起成功,失败一起失败。而是说一个成功,运行另外一个稍微慢一点。只要最后成功就行了。
利用RocketMQ事务消息。
- A系统先发送一个
prepared
消息到MQ。MQ先做保存,不立马给消费方 - A系统事务执行成功,然后发一个确认消息,此时MQ收到了确认消息,才会把
prepared
发给消费方 - 如果A执行失败,就可以直接回滚掉前面发的
prepared
消息,此时MQ就不会把消息发给消费方 - B系统如果失败了,会自动重试。
可以看出来这个和本地消息表其实差不多,不过只是A系统去扫描消息表,而是都交给了消息队列来处理了。
1 | import org.apache.rocketmq.client.producer.LocalTransactionState; |
4. TCC方案
Try
、 Confirm
、 Cancel
- Try 对各个服务的资源做检测,对资源进行提前锁定或者预留
- Confirm 在各个服务中执行实际的操作
- Cancel 如果任何一个服务的业务方法执行出错,那么这里就需要进行补偿,即执行已操作成功的业务逻辑的回滚操作
国内的支付机构都是这样,翼支付就是这样,导致了支付控制模块的代码补偿逻辑超级多。但是没办法,设计到钱的操作,只能
这样搞,但是开发带来的问题就是补偿逻辑代码巨大,非常之恶心。
最近在逛论坛发现一个关于TCC写的很好的文章。这里转载分享给大家。
终于有人把“TCC分布式事务”实现原理讲明白了
5. XA方案
纯技术来解决分布式事务问题。如阿里提供的 Seata
、 fescar
依赖一个事务协调器。小编也没有在实践中使用过,所以没有深入去研究,就搜索了写资料整理在这里。
- 性能(阻塞性协议,增加响应时间、锁时间、死锁)
- 数据库支持完善度(MySQL 5.7之前都有缺陷)
- 协调者依赖独立的J2EE中间件(早期重量级Weblogic、Jboss,后期轻量级Atomikos、Narayana和Bitronix)
- 运维复杂,DBA缺少这方面经验
- 并不是所有资源都支持XA协议。
- 大厂懂所以不使用,小公司不懂所以不敢用
最后求关注,求订阅,谢谢你的阅读!