使用分布式事务 Seata 的 AT 模式

发布时间 2023-12-04 22:44:13作者: 乔京飞

有了上篇博客实现 XA 模式的基础,本篇博客在实现 AT 模式时,不需要修改任何代码,只需要增加一张数据库表,修改以下 application.yml 配置即可实现。AT 模式也是分两个阶段提交的事务模型,它缺弥补了 XA 模型中资源锁定周期过长的问题。

其实现的两个阶段的工作原理如下:

  • 第一阶段注册分支事务,记录 undo-log 快照,执行业务 sql 后,直接提交,释放资源。
  • 第二阶段也是根据第一阶段的执行结果而决定。如果一阶段都成功,则删除 undo-log 快照记录即可;如果一阶段任意一个参与者失败,根据 undo-log 快照记录进行数据恢复,并删除 undo-log 快照。

一、实现 AT 模式

本篇博客的 Demo 与上一篇博客的 Demo 相同,具体的实现代码没有进行更改,为了区分上一篇博客的 Demo,我复制了一份代码,将工程的名字修改为 springcloud_seata_at ,工程结构如下图所示:

image

基于上一篇博客的 Demo,实现 AT 模式只需要 2 个步骤:


1 在业务系统的数据库中,创建 undo-log 数据库表

在 seata 集群部署的博客中,我们解压了 seata 源码压缩包 seata-1.8.0.zip,解压后在其 script\client\at\db 目录中有个 mysql.sql 的文件,其内容如下:

-- for AT mode you must to init this sql for you business database. the seata server not need it.
CREATE TABLE IF NOT EXISTS `undo_log`
(
    `branch_id`     BIGINT       NOT NULL COMMENT 'branch transaction id',
    `xid`           VARCHAR(128) NOT NULL COMMENT 'global transaction id',
    `context`       VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization',
    `rollback_info` LONGBLOB     NOT NULL COMMENT 'rollback info',
    `log_status`    INT(11)      NOT NULL COMMENT '0:normal status,1:defense status',
    `log_created`   DATETIME(6)  NOT NULL COMMENT 'create datetime',
    `log_modified`  DATETIME(6)  NOT NULL COMMENT 'modify datetime',
    UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
ALTER TABLE `undo_log` ADD INDEX `ix_log_created` (`log_created`);

运行该 sql 到你的业务库中,在本篇博客的 demo 中,业务库的名称是 seatatest 数据库。


2 修改 AccountService、OrderService、StockService 的 application.yml 文件

修改 3 个微服务项目的 application.yml 配置文件,将 data-source-proxy-mode 修改为 AT 即可,以 OrderService 为例内容如下:

server:
  port: 9090
spring:
  application:
    name: order-service
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.136.128:3306/seatatest?characterEncoding=utf8&allowMultiQueries=true&useSSL=false
    username: root
    password: root
  cloud:
    nacos:
      server-addr: 192.168.136.128:8848

seata:
  registry:
    type: nacos
    nacos:
      server-addr: 192.168.136.128:8848
      # 空字符串表示使用 nacos 的默认 namespace(public)
      namespace: ""
      group: SEATA_GROUP
      application: seata-server
      #username: nacos
      #password: nacos
  tx-service-group: myseata_test
  service:
    vgroup-mapping:
      myseata_test: jobs
  # 这里配置 Seata 使用 AT 模式(默认值也是 AT 模式)
  data-source-proxy-mode: AT

其实对于 data-source-proxy-mode 来说,如果不进行配置,其默认值也是 AT


二、验证效果

由于 Demo 跟上篇博客一样,代码也没有改动,所以使用 Postman 请求的接口地址和参数也是一样的。这里仍然使用一个比较大的库存进行测试,最后导致库存量不足,程序回滚,然后看日志即可。

image

对于 AccountService ,最初是减钱成功,然后二阶段回滚,删除 undo_log 记录,其日志内容如下所示:

image

对于 OrderService ,也进行了回滚,删除了 undo_log 记录,其日志内容如下:

image


三、AT 模式存在的问题和优缺点

AT模式与XA模式最大的区别是:

  • XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
  • XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
  • XA模式强一致;AT模式最终一致。

AT 模式存在的问题是:如果在多线程的访问操作同一资源的情况下,如果存在不由 seata 控制的事务,则会引起数据脏写问题。所以必须要确保操作数据库资源的事务都是由 seata 进行控制管理的,这样的会即使是多线程操作同一资源,seata 内部会使用全局锁实现隔离,从而避免数据脏写的问题。

AT模式的优点:

  • 一阶段完成直接提交事务,释放数据库资源,性能比较好
  • 对资源的操作使用全局锁实现读写隔离
  • 没有代码侵入,框架自动完成回滚和提交

AT模式的缺点:

  • 两阶段之间属于软状态,属于最终一致
  • 框架的快照功能会影响性能,但比XA模式要好很多

OK,以上就是有关 Seata 的 AT 模式介绍,可以下载源代码进行运行验证。

本篇博客的源代码下载地址为:https://files.cnblogs.com/files/blogs/699532/springcloud_seata_at.zip