一、事务模式
1.1、XA模式
XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。
1.1.1、两阶段提交
XA是规范,目前主流数据库都实现了这种规范,实现的原理都是基于两阶段提交。
正常情况:
异常情况:
一阶段:
-
事务协调者通知每个事物参与者执行本地事务
-
本地事务执行完成后报告事务执行状态给事务协调者,此时事务不提交,继续持有数据库锁
二阶段:
-
事务协调者基于一阶段的报告来判断下一步操作
-
如果一阶段都成功,则通知所有事务参与者,提交事务
-
如果一阶段任意一个参与者失败,则通知所有事务参与者回滚事务
-
1.1.2、Seata的XA模型
Seata对原始的XA模式做了简单的封装和改造,以适应自己的事务模型,基本架构如图:
RM一阶段的工作:
① 注册分支事务到TC
② 执行分支业务sql但不提交
③ 报告执行状态到TC
TC二阶段的工作:
-
TC检测各分支事务执行状态
a.如果都成功,通知所有RM提交事务
b.如果有失败,通知所有RM回滚事务
RM二阶段的工作:
-
接收TC指令,提交或回滚事务
1.1.3、优缺点
XA模式的优点是什么?
-
事务的强一致性,满足ACID原则。
-
常用数据库都支持,实现简单,并且没有代码侵入
XA模式的缺点是什么?
-
因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
-
依赖关系型数据库实现事务
1.1.4、实现XA模式
Seata的starter已经完成了XA模式的自动装配,实现非常简单,步骤如下:
1)修改application.yml文件(每个参与事务的微服务),开启XA模式:
seata:
data-source-proxy-mode: XA
2)给发起全局事务的入口方法添加@GlobalTransactional注解:
1.2、AT模式
AT模式同样是分阶段提交的事务模型,不过缺弥补了XA模型中资源锁定周期过长的缺陷。
1.2.1、Seata的AT模型
基本流程图:
阶段一RM的工作:
-
注册分支事务
-
记录undo-log(数据快照)
-
执行业务sql并提交
-
报告事务状态
阶段二提交时RM的工作:
-
删除undo-log即可
阶段二回滚时RM的工作:
-
根据undo-log恢复数据到更新前
1.2.2、流程梳理
我们用一个真实的业务来梳理下AT模式的原理。
比如,现在又一个数据库表,记录用户余额:
id | money |
---|---|
1 | 100 |
其中一个分支业务要执行的SQL为:
update tb_account set money = money - 10 where id = 1
AT模式下,当前分支事务执行流程如下:
一阶段:
- TM发起并注册全局事务到TC
- TM调用分支事务
- 分支事务准备执行业务SQL
- RM拦截业务SQL,根据where条件查询原始数据,形成快照。
{ "id": 1, "money": 100 }
- RM执行业务SQL,提交本地事务,释放数据库锁。此时
money = 90
- RM报告本地事务状态给TC
二阶段:
- TM通知TC事务结束
- TC检查分支事务状态
a、如果都成功,则立即删除快照
b、如果有分支事务失败,需要回滚。读取快照数据({"id": 1, "money": 100}
),将快照恢复到数据库。此时数据库再次恢复为100
流程图:
1.2.3、AT与XA的区别
简述AT模式与XA模式最大的区别是什么?
-
XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
-
XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
-
XA模式强一致;AT模式最终一致
1.2.4、脏写问题
在多线程并发访问AT模式的分布式事务时,有可能出现脏写问题,如图: