XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。

两个阶段的提交

XA是规范,目前主流数据库都实现了这种规范,实现的原理都是基于两阶段提交。
正常情况下:
image-20210724174102768.png
异常情况下:
image-20210724174234987.png
一阶段:

  • 事务协调者通知每个事物参与者执行本地事务
  • 本地事务执行完成后报告事务执行状态给事务协调者,此时事务不提交,继续持有数据库锁

二阶段:

  • 事务协调者基于一阶段的报告来判断下一步操作

    • 如果一阶段都成功,则通知所有事务参与者,提交事务
    • 如果一阶段任意一个参与者失败,则通知所有事务参与者回滚事务

      Seata的XA模型

      Seata对原始的XA模式做了简单的封装和改造,以适应自己的事务模型,基本架构如图:
      image-20210724174424070.png

      XA模式的优缺点

      XA模式的优点是什么?
  • 事务的强一致性,满足ACID原则。

  • 常用数据库都支持,实现简单,并且没有代码侵入

XA模式的缺点是什么?

  • 因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
  • 依赖关系型数据库实现事务,数据库不支持这么做就不行

    实现XA模式

    案例说明:
    看这篇文章的项目流程图

Seata的starter已经完成了XA模式的自动装配,步骤如下:

  1. 修改application.yml文件(每个参与事务的微服务都要设置),开启XA模式:

    1. seata:
    2. data-source-proxy-mode: XA
  2. 给发起全局事务的入口方法添加@GlobalTransactional注解:

案例中的全局入口就是下订单的入口

  1. @Override
  2. @GlobalTransactional
  3. public Long create(Order order) {
  4. // 创建订单
  5. orderMapper.insert(order);
  6. try {
  7. // 扣用户余额
  8. accountClient.deduct(order.getUserId(), order.getMoney());
  9. // 扣库存
  10. storageClient.deduct(order.getCommodityCode(), order.getCount());
  11. } catch (FeignException e) {
  12. log.error("下单失败,原因:{}", e.contentUTF8(), e);
  13. throw new RuntimeException(e.contentUTF8(), e);
  14. }
  15. return order.getId();
  16. }
  1. 重启服务测试

数据库中用户有1000元,库存有10个,我们买20个,花费200元,结果失败了。
image.png
看代码日志,本来扣款过的,但是减库存失败,扣款就回滚了
image.png