使用分布式事务 Seata 的 AT 模式
有了上篇博客实现 XA 模式的基础,本篇博客在实现 AT 模式时,不需要修改任何代码,只需要增加一张数据库表,修改以下 application.yml 配置即可实现。AT 模式也是分两个阶段提交的事务模型,它缺弥补了 XA 模型中资源锁定周期过长的问题。
其实现的两个阶段的工作原理如下:
- 第一阶段注册分支事务,记录 undo-log 快照,执行业务 sql 后,直接提交,释放资源。
- 第二阶段也是根据第一阶段的执行结果而决定。如果一阶段都成功,则删除 undo-log 快照记录即可;如果一阶段任意一个参与者失败,根据 undo-log 快照记录进行数据恢复,并删除 undo-log 快照。
一、实现 AT 模式
本篇博客的 Demo 与上一篇博客的 Demo 相同,具体的实现代码没有进行更改,为了区分上一篇博客的 Demo,我复制了一份代码,将工程的名字修改为 springcloud_seata_at ,工程结构如下图所示:
基于上一篇博客的 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 请求的接口地址和参数也是一样的。这里仍然使用一个比较大的库存进行测试,最后导致库存量不足,程序回滚,然后看日志即可。
对于 AccountService ,最初是减钱成功,然后二阶段回滚,删除 undo_log 记录,其日志内容如下所示:
对于 OrderService ,也进行了回滚,删除了 undo_log 记录,其日志内容如下:
三、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