4.动手实践
下面我们就一起学习下Seata中的四种不同的事务模式。
4.1.XA模式
XA 规范 是 X/Open 组织定义的分布式事务处理(DTP,Distributed Transaction Processing)标准,XA 规范 描述了全局的TM与局部的RM之间的接口,几乎所有主流的数据库都对 XA 规范 提供了支持。
4.1.1.两阶段提交
XA是规范,目前主流数据库都实现了这种规范,实现的原理都是基于两阶段提交。
正常情况:
异常情况:
一阶段:
事务协调者通知每个事物参与者执行本地事务
本地事务执行完成后报告事务执行状态给事务协调者,此时事务不提交,继续持有数据库锁
二阶段:
事务协调者基于一阶段的报告来判断下一步操作
如果一阶段都成功,则通知所有事务参与者,提交事务
如果一阶段任意一个参与者失败,则通知所有事务参与者回滚事务
4.1.2.Seata的XA模型
Seata对原始的XA模式做了简单的封装和改造,以适应自己的事务模型,基本架构如图:
RM一阶段的工作:
① 注册分支事务到TC
② 执行分支业务sql但不提交
③ 报告执行状态到TC
TC二阶段的工作:
TC检测各分支事务执行状态
a.如果都成功,通知所有RM提交事务
b.如果有失败,通知所有RM回滚事务
RM二阶段的工作:
接收TC指令,提交或回滚事务
4.1.3.优缺点
XA模式的优点是什么?
事务的强一致性,满足ACID原则。
常用数据库都支持,实现简单,并且没有代码侵入
XA模式的缺点是什么?
因为一阶段需要锁定数据库资源,等待二阶段结束才释放,性能较差
依赖关系型数据库实现事务
4.1.4.实现XA模式
Seata 的 starter 已经完成了 XA 模式的自动装配,实现非常简单,步骤如下:
1)修改application.yml文件(每个参与事务的微服务),开启XA模式:
seata: data-source-proxy-mode: XA
2)给发起全局事务的入口方法添加@GlobalTransactional注解:
本例中是OrderServiceImpl中的create方法.
@Override @GlobalTranaction public Long create(Order order){ // TODO }
3)重启服务并测试
重启order-service,再次测试,发现无论怎样,三个微服务都能成功回滚。
4.2.AT模式
AT模式同样是分阶段提交的事务模型,不过却弥补了XA模型中资源锁定周期过长的缺陷。(效率比XA高 但是一致性比XA若)
4.2.1.Seata的AT模型
基本流程图:
阶段一RM的工作:
注册分支事务
记录undo-log(数据快照)
执行业务sql并提交
报告事务状态
阶段二提交时RM的工作:
删除undo-log即可
阶段二回滚时RM的工作:
根据undo-log恢复数据到更新前
4.2.2.流程梳理
我们用一个真实的业务来梳理下AT模式的原理。
比如,现在又一个数据库表,记录用户余额:
其中一个分支业务要执行的SQL为:
update tb_account set money = money - 10 where id = 1
AT模式下,当前分支事务执行流程如下:
一阶段:
1)TM发起并注册全局事务到TC
2)TM调用分支事务
3)分支事务准备执行业务SQL
4)RM拦截业务SQL,根据where条件查询原始数据,形成快照。
{ "id": 1, "money": 100 }
5)RM执行业务SQL,提交本地事务,释放数据库锁。此时 money = 90
6)RM报告本地事务状态给TC
二阶段:
1)TM通知TC事务结束
2)TC检查分支事务状态
a)如果都成功,则立即删除快照
b)如果有分支事务失败,需要回滚。读取快照数据({"id": 1, "money": 100}),将快照恢复到数据库。此时数据库再次恢复为100
流程图:
4.2.3.AT与XA的区别
简述AT模式与XA模式最大的区别是什么?
XA模式一阶段不提交事务,锁定资源;AT模式一阶段直接提交,不锁定资源。
XA模式依赖数据库机制实现回滚;AT模式利用数据快照实现数据回滚。
XA模式强一致;AT模式最终一致
4.2.4.脏写问题
在多线程并发访问AT模式的分布式事务时,有可能出现脏写问题,如图:
解决思路 就是引入了全局锁的概念。在释放DB锁之前,先拿到全局锁。避免同一时刻有另外一个事务来操作当前数据。
4.2.5.优缺点
AT模式的优点:
一阶段完成直接提交事务,释放数据库资源,性能比较好
利用全局锁实现读写隔离
没有代码侵入,框架自动完成回滚和提交
AT模式的缺点:
两阶段之间属于软状态,属于最终一致
框架的快照功能会影响性能,但比XA模式要好很多
4.2.6.实现AT模式
AT模式中的快照生成、回滚等动作都是由框架自动完成,没有任何代码侵入,因此实现非常简单。
只不过,AT模式需要一个表来记录全局锁、另一张表来记录数据快照undo_log。
1)导入数据库表,记录全局锁
导入如下的Sql语句,其中lock_table导入到TC服务关联的数据库,undo_log表导入到微服务关联的数据库:
SET NAMES utf8mb4; SET FOREIGN_KEY_CHECKS = 0; -- ---------------------------- -- Table structure for undo_log -- ---------------------------- DROP TABLE IF EXISTS `undo_log`; CREATE TABLE `undo_log` ( `branch_id` bigint(20) NOT NULL COMMENT 'branch transaction id', `xid` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'global transaction id', `context` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci 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 INDEX `ux_undo_log`(`xid`, `branch_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = 'AT transaction mode undo table' ROW_FORMAT = Compact; -- ---------------------------- -- Records of undo_log -- ---------------------------- -- ---------------------------- -- Table structure for lock_table -- ---------------------------- DROP TABLE IF EXISTS `lock_table`; CREATE TABLE `lock_table` ( `row_key` varchar(128) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `xid` varchar(96) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `transaction_id` bigint(20) NULL DEFAULT NULL, `branch_id` bigint(20) NOT NULL, `resource_id` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `table_name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `pk` varchar(36) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `gmt_create` datetime NULL DEFAULT NULL, `gmt_modified` datetime NULL DEFAULT NULL, PRIMARY KEY (`row_key`) USING BTREE, INDEX `idx_branch_id`(`branch_id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact; SET FOREIGN_KEY_CHECKS = 1;
2)修改application.yml文件,将事务模式修改为AT模式即可:
seata: data-source-proxy-mode: AT # 默认就是AT
3)重启服务并测试
5.高可用
Seata 的 TC 服务作为分布式事务核心,一定要保证集群的高可用性。
5.1.高可用架构模型
搭建TC服务集群非常简单,启动多个TC服务,注册到nacos即可。
但集群并不能确保100%安全,万一集群所在机房故障怎么办?所以如果要求较高,一般都会做异地多机房容灾。
比如一个TC集群在上海,另一个TC集群在杭州:
微服务基于事务组(tx-service-group)与TC集群的映射关系,来查找当前应该使用哪个TC集群。当SH集群故障时,只需要将vgroup-mapping中的映射关系改成HZ。则所有微服务就会切换到HZ的TC集群了。
5.2.实现高可用
总结
分布式事务: 在分布式架构中一个业务需要跨对个微服务,需要这多个微服务执行的状态保持一致. 分布式事务理论基础: CAP定理: C: 一致性 在微服务架构中,从任何节点读取到的数据应当是一致的. A: 可用性 在服务不宕机的情况下,必须返回预期的结果 P: 分区容错 在微服务架构中是避免不了的. BASE理论: - Basically Available 基本可用 - Soft State 软状态 - Eventually Consistent 最终一致性 方案: AP: 可用性 软状态: 允许不同的分支事务短时间内状态不一致 但软状态过后需要保证最终一致. 各个分支执行自己的操作,执行完毕后,直接提交事务即可. 如果有分支出现失败的请求,事务协调者会通过各个分支,进行补救 CP: 强一致性 在所有的分支事务执行完毕后,等待协调事务发送最终提交或回滚事务的通知 ===========================Seata====================== seata-TC: 事务协调者 连接mysql数据库: 将事务的各种状态信息保存到mysql数据库表中 记录全局事务 记录全局事务包含的分支事务 seata-TM: 事务管理器 开启全局事务 提交/回滚全局事务 seata-RM: 资源管理器 分支事务管理 ---------------------------------- Seata-分布式事务模式: XA模式: CP思想(强一致性) AT模式: AP思想(最终一致) 软状态: 每个分支事务都可以独立提交 TM--->TC: 开启全局事务 TM--->分支 获取全局事务锁 undo_log快照: before-image: 在分支事务对数据库中的数据操作前进行前置快照 after-image: 在分支事务对数据库中的数据操作后进行后置快照 如果各个分支都执行成功了---> 删除快照 如果执行失败--->恢复快照(执行事务的补偿过程) 释放全局事务锁 注意事项: 主要使用在关系型数据库中 TCC模式: AP思想 try: 判断cancel是否执行,如果未执行,正常执行即可 校验,冻结 如果执行过: 不再执行任何逻辑,删除冻结数据 confirm: 执行成功 删除冻结数据 cancel: 执行失败 执行补偿过程 判断try是否执行了,如果执行了 恢复冻结数据 删除冻结数据 如果try没有执行: 空回滚: 添加一条冻结数据,并设置状态 SAGA模式: AP思想
0.回顾
在分布式(微服务)系统中,往往一个业务需要调用多个微服务,且还需要保证当前业务的完整性. 一个业务(事务): 组成这个业务的各个单元,要么都成功,要么都失败. 指导思想: CAP定理: BASE理论: Alibaba:在CAP定理和BASE理论的基础上进行实践后的产物. Seata: Seata实现方式: Seata角色: TC: 事务协调者 安装Seata(服务)软件 TM: 事务管理器 RM: 资源管理器(分支事务管理器) 注意: 当我们在微服务项目中导入Seata的依赖后,就相当于拥有了TM和RM两个角色 Seata模式: XA模式: CP思想,强一致性 AT模式: AP思想,最终一致 TCC模式: AP思想,最终一致 SAGA模式: Ap思想,最终一致 Seata模式原理 Seata模式实现方式
管理器
开启全局事务
提交/回滚全局事务
seata-RM: 资源管理器
分支事务管理
Seata-分布式事务模式:
XA模式: CP思想(强一致性)
AT模式: AP思想(最终一致)
软状态: 每个分支事务都可以独立提交
TM—>TC: 开启全局事务
TM—>分支
获取全局事务锁
undo_log快照:
before-image: 在分支事务对数据库中的数据操作前进行前置快照
after-image: 在分支事务对数据库中的数据操作后进行后置快照
如果各个分支都执行成功了—> 删除快照
如果执行失败—>恢复快照(执行事务的补偿过程)
释放全局事务锁
注意事项: 主要使用在关系型数据库中
TCC模式: AP思想
try:
判断cancel是否执行,如果未执行,正常执行即可
校验,冻结
如果执行过:
不再执行任何逻辑,删除冻结数据
confirm: 执行成功
删除冻结数据
cancel: 执行失败
执行补偿过程
判断try是否执行了,如果执行了
恢复冻结数据
删除冻结数据
如果try没有执行:
空回滚: 添加一条冻结数据,并设置状态
SAGA模式: AP思想
## 0.当前存在的问题? ```java 在分布式(微服务)系统中,往往一个业务需要调用多个微服务,且还需要保证当前业务的完整性. 一个业务(事务): 组成这个业务的各个单元,要么都成功,要么都失败. 指导思想: CAP定理: BASE理论: Alibaba:在CAP定理和BASE理论的基础上进行实践后的产物. Seata: Seata实现方式: Seata角色: TC: 事务协调者 安装Seata(服务)软件 TM: 事务管理器 RM: 资源管理器(分支事务管理器) 注意: 当我们在微服务项目中导入Seata的依赖后,就相当于拥有了TM和RM两个角色 Seata模式: XA模式: CP思想,强一致性 AT模式: AP思想,最终一致 TCC模式: AP思想,最终一致 SAGA模式: Ap思想,最终一致 Seata模式原理 Seata模式实现方式