1. MySQL XA方案
MySQL从5.7开始加入了分布式事务的支持。MySQL XA中拥有两种角色:
- RM(Resource Manager):用于直接执行本地事务的提交和回滚。在分布式集群中,一台MySQL服务器就是一个RM。
- TM(Transaction Manager):TM是分布式事务的核心管理者。事务管理器与每个RM进行通信,协调并完成分布式事务的处理。发起一个分布式事务的MySQL客户端就是一个TM。
XA的两阶段提交分为Prepare阶段和Commit阶段,过程如下:
- 阶段一为准备(prepare)阶段。即所有的RM锁住需要的资源,在本地执行这个事务(执行sql,写redo/undo log等),但不提交,然后向Transaction Manager报告已准备就绪。
- 阶段二为提交阶段(commit)。当Transaction Manager确认所有参与者都ready后,向所有参与者发送commit命令。
如下图所示:
![](https://img2020.cnblogs.com/blog/45323/202012/45323-20201222103301146-1196851524.png)
MySQL XA拥有严重的性能问题。一个数据库的事务和多个数据库间的XA事务性能对比可发现,性能差10倍左右。另外,XA过程中会长时间的占用资源(加锁)直到两阶段提交完成才释放资源。
2. Seata
Seata的分布式事务解决方案是业务层面的解决方案,只依赖于单台数据库的事务能力,特点是:效率高;原因是:生成反SQL(事务回滚时执行)、以空间换时间的思路。
AT 模式是无侵入的分布式事务解决方案,适用于不希望对业务进行改造的场景,几乎0学习成本。
TCC 模式是高性能分布式事务解决方案,适用于核心系统等对性能有很高要求的场景。(对业务代码有一定的侵入性, TCC 模式无 AT 模式的全局行锁)
Saga 模式是长事务解决方案,适用于业务流程长且需要保证事务最终一致性的业务系统,Saga 模式一阶段就会提交本地事务,无锁,长流程情况下可以保证性能,多用于渠道层、集成层业务系统。事务参与者可能是其它公司的服务或者是遗留系统的服务,无法进行改造和提供 TCC 要求的接口,也可以使用 Saga 模式。
XA模式是分布式强一致性的解决方案,但性能低而使用较少。
SeataServer部署:http://seata.io/zh-cn/docs/ops/deploy-by-docker.html,通常结合注册中心使用,避免单点故障。对应脚本参考---https://github.com/seata/seata/tree/develop/script/server/db
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<!--seata--> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-seata</artifactId> <version>2.1.0.RELEASE</version> <exclusions> <exclusion> <artifactId>seata-all</artifactId> <groupId>io.seata</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>${seata.version}</version> </dependency>
2.分别在各业务数据库中创建undo_log表,此表为seata框架使用,sql地址:https://github.com/seata/seata/tree/develop/script/client/at/db
3.配置registry.conf,file.conf。主要是配置中心,redis,mysql连接
4.Service实现,@GlobalTransactional注解用以开启全局事务,@Transactional注解用于分支事务
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Service @Slf4j public class AccountServiceImpl implements AccountService { @Autowired private AccountInfoMapper accountInfoMapper; @Autowired private Bank2Client bank2Client; @Transactional @GlobalTransactional//开启全局事务 public void updateAccountBalance(String accountNo, Long amount) { log.info("bank1 service begin,XID:{}", RootContext.getXID()); //扣减张三的金额 accountInfoMapper.updateAccountBalance(accountNo,amount *-1); //调用李四微服务,转账 String transfer = bank2Client.transfer(amount); if("fallback".equals(transfer)){ //调用李四微服务异常 throw new RuntimeException("调用李四微服务异常"); } if(amount == 2){ //人为制造异常 throw new RuntimeException("bank1 make exception.."); } } }