文章目录
- 前言
- [4 Seata AT 模式](#4 Seata AT 模式)
-
- [4.1 实现原理](#4.1 实现原理)
- [4.2 脏写问题](#4.2 脏写问题)
- [4.3 微服务实现AT模式](#4.3 微服务实现AT模式)
-
- [4.3.1 新建数据库表](#4.3.1 新建数据库表)
- [4.3.2 修改配置文件](#4.3.2 修改配置文件)
- [4.3.3 重启服务并测试](#4.3.3 重启服务并测试)
- [5 Seata Saga 模式](#5 Seata Saga 模式)
前言
分布式事务学习笔记(一)分布式事务问题、CAP定理、BASE理论、Seata
分布式事务学习笔记(二)Seata架构、TC服务器部署、微服务集成Seata
分布式事务学习笔记(三)微服务实现Seata XA模式
4 Seata AT 模式
AT 模式同样是分阶段提交的非侵入式分布式事务模型,不过弥补了 XA 模式中资源锁定周期过长的缺陷。
4.1 实现原理
- 第一阶段RM的工作
- 向TC注册分支事务
- 执行业务SQL并直接提交
- 记录更新前后快照(undo-log)
- 向TC报告分支事务状态
- 第二阶段RM的工作
- 如果收到TC确认提交的指令,则删除快照(undo-log)
- 如果收到TC确认回滚的指令,则根据快照(undo-log)恢复数据到更新前
其中,记录更新前后快照(undo-log) 是RM拦截业务SQL,根据业务SQL的where条件查询原始数据,并保存起来形成快照。后续需要回滚时,可以根据这个快照直接恢复。
- 与 XA 模式的比较
XA模式 | AT模式 | |
---|---|---|
事务特性 | 第一阶段不提交事务,锁定资源 | 第一阶段提交事务,不锁定资源 |
回滚机制 | 依赖数据库机制实现回滚 | 利用数据快照实现数据回滚 |
一致性 | 强一致 | 最终一致 |
4.2 脏写问题
在多线程并发访问AT模式的分布式事务时,有可能出现脏写问题,如图:
问题出现的关键原因在于,当前事务根据快照回滚之前,有另外一个事务也操作了当前数据,使得当前事务的快照不再正确。
解决脏写问题的思路就是引入了全局锁的概念。
事务在释放DB锁之前,先拿到全局锁,只有拿到全局锁的事务有资格操作当前数据,从而避免同一时刻有另外一个事务来操作当前数据,如图:
由上图可知,事务1持有全局锁,具备执行权。而事务2拿不到全局锁,则无法操作当前数据。事务1进行回滚时则不会有影响。
AT模式的优点在于:
- 第一阶段完成后直接提交事务,释放数据库资源,性能比较好
- 利用全局锁实现读写隔离
- 没有代码侵入,框架自动完成回滚和提交
AT模式的缺点在于:
- 两阶段之间属于软状态,属于最终一致
- 框架的快照功能会影响性能,但比XA模式要好很多
4.3 微服务实现AT模式
4.3.1 新建数据库表
AT模式中的快照生成、回滚等动作都是由框架自动完成,没有任何代码侵入,实现简单。但AT模式需要 lock_table 表来记录全局锁、undo_log 表来记录数据快照。
在学习 XA 模式时,就已经创建了 lock_table 表,而创建 undo_log 表的SQL可以在 https://gitee.com/seata-io/seata/blob/v1.5.1/script/client/at/db/mysql.sql 中找到:
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';
4.3.2 修改配置文件
修改微服务的application.yml文件,将事务模式修改为AT模式即可:
yaml
seata:
data-source-proxy-mode: AT # 默认就是AT
4.3.3 重启服务并测试
假设现在商品库存为10,用户余额为1000。用户使用300金额购买了6件商品:
程序执行到这里,已经扣除用户余额成功。而在 undo_log 表中记录了金额的快照:
程序继续执行,下单成功,undo_log 表的记录被删除:
假设此时用户再次使用300金额购买了6件商品,由于此时库存只剩下4件,所以会下单失败:
jd-account-service
服务的日志显示,余额先是扣除成功,而后被回滚:
最终余额和库存均没有改变:
可见AT模式的分布式事务已生效。
5 Seata Saga 模式
Saga 模式是 Seata 提供的长事务解决方案。其工作流程如下:
在该模式中,分布式事务内有多个参与者,每一个参与者都有一个正向服务 和一个补偿服务,需要用户根据业务场景分别实现其正向操作和逆向回滚操作。
分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去执行前面各参与者的补偿服务,回滚已提交的操作,使分布式事务回到初始状态。
因此,Saga 模式也分为两个阶段:
- 一阶段:各参与者直接提交本地事务
- 二阶段:均成功则什么都不做;有任意失败则通过补偿服务来回滚
Saga 模式的优点:
- 事务参与者可以基于事件驱动实现异步调用,吞吐高
- 一阶段直接提交事务,无锁,性能好
缺点:
- 软状态持续时间不确定,时效性差
- 没有锁,没有事务隔离,会有脏写
...
本节完,更多内容请查阅分类专栏:微服务学习笔记
感兴趣的读者还可以查阅我的另外几个专栏: