本文已收录在Github,关注我,紧跟本系列专栏文章,咱们下篇再续!
- 🚀 魔都架构师 | 全网30W技术追随者
- 🔧 大厂分布式系统/数据中台实战专家
- 🏆 主导交易系统百万级流量调优 & 车联网平台架构
- 🧠 AIGC应用开发先行者 | 区块链落地实践者
- 🌍 以技术驱动创新,我们的征途是改变世界!
- 👉 实战干货:编程严选网
0 前言
基于MQ,JTA实现多服务的分布式事务:
Order service监听新订单队列中的消息,获取后新增订单:
- 成功,则往新订单缴费队列中写消息,中间新增订单的过程用JTA事务管理,当新增失败则事务回滚,不会往新订单缴费队列中写消息
User service扣费成功后,往新订单转移票队列写消息,这时Ticket service 正在处理中或者处理中发生了失败,这中间的过程中用户查看自己的余额已经扣费成功,但票的信息却没有,此时可以使用事务失败回滚的方式依次回退,这种叫弱一致性;又或者可以把处理失败的内容发送至一个错误队列中,由人工处理等方式解决,即最终一致性。
使用Spring JTA
可用:
- 如JBoss之类应用服务器提供的JTA事务管理器
- Atomikos、Bitronix等库提供的JTA事务管理器
禁用JTA
为啥禁用JTA?因为JTA采用两阶段提交方式:
- 预备阶段
- 正式提交
当第一次提交出现错误,则整个事务出现回滚,一个事务的时间可能会较长,因为它要跨越多个数据库多个数据资源的的操作,所以在性能上可能会造成吞吐量低。
不用JTA,咋实现事务?依次提交两事务:
- start MQ transaction
- receive message 读取消息:出错时,message transaction直接回滚
- start DB transaction
- update DB:出错时,由于此时database transaction、message transaction都尚未提交,这时虽然已经读取了消息,但只要 MQ 支持事务功能,消息就会被回滚,重新放回MQ,重试重新触发该方法
- commit DB transaction:出错时,和上一点原因相同
- commit MQ transaction:出错时,database transaction已被提交了,所以无法回滚。MQ 事务尚未提交,所以可直接回滚。这也就是不使用 JTA 时遇到的最大难题。所以 spring 也提供了很多机制保障
消息放回至MQ队列,重试重新触发该方法。 当这一步出现错误时,上面的因为已经commit,所以不会rollback
1 多数据源的事务同步解决方案
1.1 XA与最后资源博弈
1.start MQ transaction 2.receive message # 针对 DB 用 JTA 事务 3.start JTA transaction on DB 4.update DB # DB 一阶段提交 5.phase-1 commit on DB transaction # 该步出错时,由于DB还在XA的第一次提交预备状态,DB可回滚 6.commit MQ transaction # 等到MQ事务提交完成,才做DB二阶段提交 # 该步出错时,因为MQ不是XA方式,提交后无法回滚,虽然DB都可回滚 7.phase-2 commit on DB transaction
1.2 共享资源
适用场景
两个数据源可共享同一底层资源时。
如ActiveMQ使用DB作为底层资源存储,使用DB的connection控制事务提交,需要数据源支持指定底层资源存储方式。
1.3 最大努力一次提交
依次提交事务,可能会出错,尽量通过AOP或Listener实现事务直接的同步。
1.4 JMS最大努力一次提交+重试
适用场景:其中一个数据源是MQ,并且事务由读MQ消息开始。
利用MQ消息的重试机制,重试的时候需要考虑重复消息。
1.start message transaction 2.receive message 3.start database transaction # 数据库操作出错,消息会被放回MQ,重试重新触发该方法 4.update database 5.commit database transaction 6.commit message transaction
这种时候没有问题,都能成功回滚。
1.start message transaction 2.receive message 3.start database transaction 4.update database 5.commit database transaction # 提交MQ事务出错,消息放回MQ,重试重新触发该方法 6.commit message transaction
DB 已经提交了,所以这时会重复 DB操作,因为DB transaction不是使用JTA事务管理,所以DB已经commit成功. 那如何避免呢,需要忽略重发消息,比如唯一性校验等手段。
1.5 链式事务管理
定义一个事务链,多个事务在一个事务管理器里依次提交。
依旧可能出错。
2 事务方案选型
业务一致性要求
- 强一致性事务:JTA,性能最差、只适用于单个服务内。
- 弱、最终一致性事务:最大努力一次提交、链式事务(设计相应的错误处理机制)
业务场景
- MQ-DB:最大努力一次提交 + 重试
- 多DB:链式事务管理
多数据源:链式事务、或其他事务同步方式