对比7种分布式事务方案,还是偏爱阿里开源的Seata,真香!(原理+实战)
这篇文章主要介绍一些目前主流的几种分布式解决方案以及阿里开源的一站式分布式解决方案Seata。
文章目录如下:
什么是分布式事务?
什么是CAP原则?
一致性(Consistency)
也就是说,在一致性系统中,一旦客户端将值写入任何一台服务器并获得响应,那么之后client从其他任何服务器读取的都是刚写入的数据 一致性保证了不管向哪台服务器写入数据,其他的服务器能实时同步数据
可用性(Availability)
分区容忍性(Partition tolerance)
为什么只能在A和C之间做出取舍?
一致性有几种分类?
这里的 “牺牲一致性” 并不是完全放弃数据的一致性,而是放弃强一致性而换取弱一致性。
强一致性 弱一致性 最终一致性
强一致性
一个集群需要对外部提供强一致性,所以只要集群内部某一台服务器的数据发生了改变,那么就需要等待集群内其他服务器的数据同步完成后,才能正常的对外提供服务。 保证了强一致性,务必会损耗可用性。
弱一致性
最终一致性
总结
什么是Base理论?
BA(Basic Available)基本可用
“一定时间”可以适当延长 当举行大促(比如秒杀)时,响应时间可以适当延长 给部分用户返回一个降级页面 给部分用户直接返回一个降级页面,从而缓解服务器压力。但要注意,返回降级页面仍然是返回明确结果。
S(Soft State)柔性状态
E(Eventual Consisstency)最终一致性
分布式事务有哪几种解决方案?
2阶段提交(2PC)
准备阶段 提交阶段
事务协调者(事务管理器):事务的发起者 事务参与者(资源管理器):事务的执行者
准备阶段(投票阶段)
协调者向所有参与者发送事务内容,询问是否可以提交事务,并等待答复 各参与者执行事务操作,将 undo 和 redo 信息记入事务日志中(但不提交事务) 如参与者执行成功,给协调者反馈同意,否则反馈中止
提交阶段
协调者节点向所有参与者节点发出正式提交( commit
)的请求。参与者节点正式完成操作,并释放在整个事务期间内占用的资源。 参与者节点向协调者节点发送ack完成消息。 协调者节点收到所有参与者节点反馈的ack完成消息后,完成事务。
协调者节点向所有参与者节点发出回滚操作( rollback
)的请求。参与者节点利用阶段1写入的undo信息执行回滚,并释放在整个事务期间内占用的资源。 参与者节点向协调者节点发送ack回滚完成消息。 协调者节点受到所有参与者节点反馈的ack回滚完成消息后,取消事务。
裁判:A同学准备好了吗?准备进入第一赛道.... 裁判:B同学准备好了吗?准备进入第一赛道.... 裁判:C同学准备好了吗?准备进入第一赛道.... 如果有任意一个同学没准备好,则裁判下达回滚指令 如果裁判收到了所有同学的OK回复,则再次下令跑...... 裁判:1,2,3 跑............ A同学冲刺到终点,汇报给裁判 B,C同学冲刺失败,汇报给裁判
2PC的缺点
性能问题:执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。 可靠性问题:参与者发生故障。协调者需要给每个参与者额外指定超时机制,超时后整个事务失败。协调者发生故障。参与者会一直阻塞下去。需要额外的备机进行容错。 数据一致性问题:二阶段无法解决的问题:协调者在发出 commit
消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条事务的状态也是不确定的,没人知道事务是否被已经提交。实现复杂:牺牲了可用性,对性能影响较大,不适合高并发高性能场景。
2PC的优点
尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)
3阶段提交(3PC)
在协调者和参与者中都引入超时机制 在第一阶段和第二阶段中插入一个准备阶段。保证了在最后提交阶段之前各参与节点的状态是一致的。
CanCommit
、PreCommit
、DoCommit
三个阶段。处理流程如下:阶段一:CanCommit阶段
CanCommit
阶段其实和2PC的准备阶段很像。协调者向参与者发送commit
请求,参与者如果可以提交就返回Yes响应,否则返回No响应。事务询问:协调者向所有参与者发出包含事务内容的 canCommit
请求,询问是否可以提交事务,并等待所有参与者答复。响应反馈:参与者收到 canCommit
请求后,如果认为可以执行事务操作,则反馈 yes 并进入预备状态,否则反馈 no。
阶段二:PreCommit阶段
PreCommit
操作。根据响应情况,有以下两种可能。假如所有参与者均反馈 yes,协调者预执行事务。 发送预提交请求 :协调者向参与者发送 PreCommit
请求,并进入准备阶段事务预提交 :参与者接收到 PreCommit
请求后,会执行事务操作,并将undo
和redo
信息记录到事务日志中(但不提交事务)响应反馈 :如果参与者成功的执行了事务操作,则返回ACK响应,同时开始等待最终指令。
假如有任何一个参与者向协调者发送了No响应,或者等待超时之后,协调者都没有接到参与者的响应,那么就执行事务的中断。 发送中断请求 :协调者向所有参与者发送 abort
请求。中断事务 :参与者收到来自协调者的 abort
请求之后(或超时之后,仍未收到协调者的请求),执行事务的中断。
阶段三:doCommit阶段
进入阶段 3 后,无论协调者出现问题,或者协调者与参与者网络出现问题,都会导致参与者无法接收到协调者发出的 do Commit 请求或 abort 请求。此时,参与者都会在等待超时之后,继续执行事务提交。
执行提交 发送提交请求 协调接收到参与者发送的ACK响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送 doCommit
请求。事务提交 参与者接收到 doCommit
请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。响应反馈 事务提交完之后,向协调者发送ack响应。 完成事务 协调者接收到所有参与者的ack响应之后,完成事务。
中断事务:任何一个参与者反馈 no,或者等待超时后协调者尚无法收到所有参与者的反馈,即中断事务 发送中断请求 如果协调者处于工作状态,向所有参与者发出 abort 请求 事务回滚 参与者接收到abort请求之后,利用其在阶段二记录的undo信息来执行事务的回滚操作,并在完成回滚之后释放所有的事务资源。 反馈结果 参与者完成事务回滚之后,向协调者反馈ACK消息 中断事务 协调者接收到参与者反馈的ACK消息之后,执行事务的中断。
优点
缺点
preCommit
请求后等待 doCommit
指令时,此时如果协调者请求中断事务,而协调者无法与参与者正常通信,会导致参与者继续提交事务,造成数据不一致。TCC(事务补偿)
第一阶段:Try(尝试),主要是对业务系统做检测及资源预留 (加锁,锁住资源) 第二阶段:本阶段根据第一阶段的结果,决定是执行confirm还是cancel Confirm(确认):执行真正的业务(执行业务,释放锁) Cancle(取消):是预留资源的取消(出问题,释放锁)
①Try 阶段
完成所有业务检查( 一致性 ) 。 预留必须业务资源( 准隔离性 ) 。 Try 尝试执行业务。
②Confirm / Cancel 阶段
最终一致性保证
TCC 事务机制以初步操作(Try)为中心的,确认操作(Confirm)和取消操作(Cancel)都是围绕初步操作(Try)而展开。因此,Try 阶段中的操作,其保障性是最好的,即使失败,仍然有取消操作(Cancel)可以将其执行结果撤销。 Try阶段执行成功并开始执行 Confirm
阶段时,默认Confirm
阶段是不会出错的。也就是说只要Try
成功,Confirm
一定成功(TCC设计之初的定义) 。Confirm与Cancel如果失败,由TCC框架进行==重试==补偿 存在极低概率在CC环节彻底失败,则需要定时任务或人工介入
方案总结
性能提升:具体业务来实现控制资源锁的粒度变小,不会锁定整个资源。 数据最终一致性:基于 Confirm 和 Cancel 的幂等性,保证事务最终完成确认或者取消,保证数据的一致性。 可靠性:解决了 XA 协议的协调者单点故障问题,由主业务方发起并控制整个业务活动,业务活动管理器也变成多点,引入集群。
TCC 的 Try、Confirm 和 Cancel 操作功能要按具体业务来实现,业务耦合度较高,提高了开发成本。
本地消息表
事务主动方 事务被动方
业务处理成功、事务消息发送失败 业务处理失败、事务消息发送成功
①:事务主动方在同一个本地事务中处理业务和写消息表操作 ②:事务主动方通过消息中间件,通知事务被动方处理事务通知事务待消息。消息中间件可以基于 Kafka、RocketMQ 消息队列,事务主动方主动写消息到消息队列,事务消费方消费并处理消息队列中的消息。 ③:事务被动方通过消息中间件,通知事务主动方事务已处理的消息。 ④:事务主动方接收中间件的消息,更新消息表的状态为已处理。
当①处理出错,由于还在事务主动方的本地事务中,直接回滚即可 当②、③处理出错,由于事务主动方本地保存了消息,只需要轮询消息重新通过消息中间件发送,事务被动方重新读取消息处理业务即可。 如果是业务上处理失败,事务被动方可以发消息给事务主动方回滚事务 如果事务被动方已经消费了消息,事务主动方需要回滚事务的话,需要发消息通知事务主动方进行回滚事务。
优点
从应用设计开发的角度实现了消息数据的可靠性,消息数据的可靠性不依赖于消息中间件,弱化了对 MQ 中间件特性的依赖。 方案轻量,容易实现。
缺点
与具体的业务场景绑定,耦合性强,不可公用。 消息数据与业务数据同库,占用业务系统资源。 业务系统在使用关系型数据库的情况下,消息服务性能会受到关系型数据库并发性能的局限。
MQ事务方案(可靠消息事务)
步骤①:发送方向 MQ 服务端(MQ Server)发送 half 消息。 步骤②:MQ Server 将消息持久化成功之后,向发送方 ack 确认消息已经发送成功。 步骤③:发送方开始执行本地事务逻辑。 步骤④:发送方根据本地事务执行结果向 MQ Server 提交二次确认(commit 或是 rollback)。 步骤⑤:MQ Server 收到 commit 状态则将半消息标记为可投递,订阅方最终将收到该消息;MQ Server 收到 rollback 状态则删除半消息,订阅方将不会接受该消息。
步骤⑤:MQ Server 对该消息发起消息回查。 步骤⑥:发送方收到消息回查后,需要检查对应消息的本地事务执行的最终结果。 步骤⑦:发送方根据检查得到的本地事务的最终状态再次提交二次确认。 步骤⑧:MQ Server基于 commit/rollback 对消息进行投递或者删除。
优点
消息数据独立存储 ,降低业务系统与消息系统之间的耦合。 吞吐量大于使用本地消息表方案。
缺点
一次消息发送需要两次网络请求(half 消息 + commit/rollback 消息) 。 业务处理服务需要实现消息状态回查接口。
最大努力通知
Saga 事务
每个 Saga 事务由一系列幂等的有序子事务(sub-transaction) Ti 组成。 每个 Ti 都有对应的幂等补偿动作 Ci,补偿动作用于撤销 Ti 造成的结果。
命令协调(Order Orchestrator) 事件编排(Event Choreographyo)
命令协调
事务发起方的主业务逻辑请求 OSO 服务开启订单事务 OSO 向库存服务请求扣减库存,库存服务回复处理结果。 OSO 向订单服务请求创建订单,订单服务回复创建结果。 OSO 向支付服务请求支付,支付服务回复处理结果。 主业务逻辑接收并处理 OSO 事务处理结果回复。
事件编排
事务发起方的主业务逻辑发布开始订单事件。 库存服务监听开始订单事件,扣减库存,并发布库存已扣减事件。 订单服务监听库存已扣减事件,创建订单,并发布订单已创建事件。 支付服务监听订单已创建事件,进行支付,并发布订单已支付事件。 主业务逻辑监听订单已支付事件并处理。
优点
服务之间关系简单,避免服务之间的循环依赖关系,因为 Saga 协调器会调用 Saga 参与者,但参与者不会调用协调器。 程序开发简单,只需要执行命令/回复(其实回复消息也是一种事件消息),降低参与者的复杂性。 易维护扩展,在添加新步骤时,事务复杂性保持线性,回滚更容易管理,更容易实施和测试。
避免中央协调器单点故障风险。 当涉及的步骤较少服务开发简单,容易实现。
缺点
中央协调器容易处理逻辑容易过于复杂,导致难以维护。 存在协调器单点故障风险。
服务之间存在循环依赖的风险。 当涉及的步骤较多,服务间关系混乱,难以追踪调测。
由于 Saga 模型中没有 Prepare 阶段,因此事务间不能保证隔离性。
总结
2PC/3PC:依赖于数据库,能够很好的提供强一致性和强事务性,但相对来说延迟比较高,比较适合传统的单体应用,在同一个方法中存在跨库操作的情况,不适合高并发和高性能要求的场景。 TCC:适用于执行时间确定且较短,实时性要求高,对数据一致性要求高,比如互联网金融企业最核心的三个服务:交易、支付、账务。 本地消息表/MQ 事务:都适用于事务中参与方支持操作幂等,对一致性要求不高,业务上能容忍数据不一致到一个人工检查周期,事务涉及的参与方、参与环节较少,业务上有对账/校验系统兜底。 Saga 事务:由于 Saga 事务不能保证隔离性,需要在业务层控制并发,适合于业务场景事务并发操作同一资源较少的情况。Saga 相比缺少预提交动作,导致补偿动作的实现比较麻烦,例如业务是发送短信,补偿动作则得再发送一次短信说明撤销,用户体验比较差。Saga 事务较适用于补偿动作容易处理的场景。
什么是Seata?
对业务无侵入:即减少技术架构上的微服务化所带来的分布式事务问题对业务的侵入 高性能:减少分布式事务解决方案所带来的性能消耗
TC(Transaction Coordinator):事务协调者。管理全局的分支事务的状态,用于全局性事务的提交和回滚。 TM(Transaction Manager):事务管理者。用于开启、提交或回滚事务。 RM(Resource Manager):资源管理器。用于分支事务上的资源管理,向 TC 注册分支事务,上报分支事务的状态,接收 TC 的命令来提交或者回滚分支事务。
AT模式
一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。 二阶段: 提交异步化,非常快速地完成 回滚通过一阶段的回滚日志进行反向补偿。
TM 向 TC 申请开启一个全局事务,全局事务创建成功并生成一个全局唯一的 XID; XID 在微服务调用链路的上下文中传播; RM 向 TC 注册分支事务,将其纳入 XID 对应全局事务的管辖; TM 向 TC 发起针对 XID 的全局提交或回滚决议; TC 调度 XID 下管辖的全部分支事务完成提交或回滚请求。
搭建Seata TC协调者
陈某下载的版本是 1.3.0
,各位最好和我版本一致,这样不会出现莫名的BUG。
创建TC所需要的表
script\server\db
这个目录,将会看到以下SQL文件:修改TC的注册中心
seata-server-1.3.0\seata\conf
这个目录,其中有一个registry.conf
文件,其中配置了TC的注册中心和配置中心。file
形式,实际使用中肯定不能使用,需要改成Nacos形式,改动的地方如下图:type:改成nacos,表示使用nacos作为注册中心 application:服务的名称 serverAddr:nacos的地址 group:分组 namespace:命名空间 username:用户名 password:密码
最后这份文件都会放在项目源码的根目录下,源码下载方式见文末
修改TC的配置中心
file
形式,当然要是用nacos作为配置中心了。registry.conf
文件,需要改动的地方如下图:type:改成nacos,表示使用nacos作为配置中心 serverAddr:nacos的地址 group:分组 namespace:命名空间 username:用户名 password:密码
seata-1.3.0\script\config-center
中有一个config.txt
文件,其中就是TC所需要的全部配置。seata-1.3.0\script\config-center\nacos
中有一个脚本nacos-config.sh
则是将config.txt中的全部配置自动推送到nacos中,运行下面命令(windows可以使用git bash运行):# -h 主机,你可以使用localhost,-p 端口号 你可以使用8848,-t 命名空间ID,-u 用户名,-p 密码
$ sh nacos-config.sh -h 127.0.0.1 -p 8080 -g SEATA_GROUP -t 7a7581ef-433d-46f3-93f9-5fdc18239c65 -u nacos -w nacos
修改TC的数据库连接信息
## 采用db的存储形式
store.mode=db
## druid数据源
store.db.datasource=druid
## mysql数据库
store.db.dbType=mysql
## mysql驱动
store.db.driverClassName=com.mysql.jdbc.Driver
## TC的数据库url
store.db.url=jdbc:mysql://127.0.0.1:3306/seata_server?useUnicode=true
## 用户名
store.db.user=root
## 密码
store.db.password=Nov2014
store.mode
,如下图:store.mode=redis
store.redis.host=127.0.0.1
store.redis.port=6379
store.redis.password=123456
启动TC
seata-server-1.3.0\seata\bin
目录下直接点击seata-server.bat
(windows)运行。Seata客户端搭建(RM)
仓储服务:对给定的商品扣除仓储数量。 订单服务:根据采购需求创建订单。 帐户服务:从用户帐户中扣除余额。
仓储服务搭建
添加依赖
seata-storage9020
项目,新增依赖如下:springCloud Alibaba
依赖版本是2.2.1.RELEASE
,其中自带的seata版本是1.1.0
,但是我们Seata服务端使用的版本是1.3.0,因此需要排除原有的依赖,重新添加1.3.0的依赖。注意:seata客户端的依赖版本必须要和服务端一致。
创建数据库
seata-storage
,其中新建两个表:storage
:库存的业务表,SQL如下:
CREATE TABLE `storage` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
`num` bigint(11) NULL DEFAULT NULL COMMENT '数量',
`create_time` datetime(0) NULL DEFAULT NULL,
`price` bigint(10) NULL DEFAULT NULL COMMENT '单价,单位分',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
INSERT INTO `storage` VALUES (1, '码猿技术专栏', 1000, '2021-10-15 22:32:40', 100);
undo_log:回滚日志表,这是Seata要求必须有的,每个业务库都应该创建一个,SQL如下:
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;
配置seata相关配置
spring:
application:
## 指定服务名称,在nacos中的名字
name: seata-storage
## 客户端seata的相关配置
seata:
## 是否开启seata,默认true
enabled: true
application-id: ${spring.application.name}
## seata事务组的名称,一定要和config.tx(nacos)中配置的相同
tx-service-group: ${spring.application.name}-tx-group
## 配置中心的配置
config:
## 使用类型nacos
type: nacos
## nacos作为配置中心的相关配置,需要和server在同一个注册中心下
nacos:
## 命名空间,需要server端(registry和config)、nacos配置client端(registry和config)保持一致
namespace: 7a7581ef-433d-46f3-93f9-5fdc18239c65
## 地址
server-addr: localhost:8848
## 组, 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
group: SEATA_GROUP
## 用户名和密码
username: nacos
password: nacos
registry:
type: nacos
nacos:
## 这里的名字一定要和seata服务端中的名称相同,默认是seata-server
application: seata-server
## 需要server端(registry和config)、nacos配置client端(registry和config)保持一致
group: SEATA_GROUP
namespace: 7a7581ef-433d-46f3-93f9-5fdc18239c65
username: nacos
password: nacos
server-addr: localhost:8848
客户端seata中的nacos相关配置要和服务端相同,比如地址、命名空间.......... tx-service-group:这个属性一定要注意,这个一定要和服务端的配置一致,否则不生效;比如上述配置中的,就要在nacos中新增一个配置 service.vgroupMapping.seata-storage-tx-group=default
,如下图:
注意: seata-storage-tx-group
仅仅是后缀,要记得添加配置的时候要加上前缀service.vgroupMapping.
扣减库存的接口
@Transactional
开启了本地事务,并没有涉及到分布式事务。账户服务搭建
添加依赖
seata-account9021
服务,这里的依赖和仓储服务的依赖相同,直接复制创建数据库
seata-account
数据库,其中新建了两个表:account
:账户业务表,SQL如下:
CREATE TABLE `account` (
`id` bigint(11) NOT NULL,
`user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户userId',
`money` bigint(11) NULL DEFAULT NULL COMMENT '余额,单位分',
`create_time` datetime(0) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
INSERT INTO `account` VALUES (1, 'abc123', 1000, '2021-10-19 17:49:53');
undo_log:回滚日志表,同仓储服务
配置seata相关配置
service.vgroupMapping.seata-account-tx-group=default
,如下图:扣减余额的接口
@Transactional
开启了本地事务,是不是很爽............订单服务搭建(TM)
添加依赖
seata-order9022
服务,这里需要添加的依赖如下:Nacos服务发现的依赖 seata的依赖 openFeign的依赖,由于要调用账户、仓储的微服务,因此需要额外添加一个openFeign的依赖
创建数据库
seata_order
数据库,其中新建两个表,如下:t_order
:订单的业务表
CREATE TABLE `t_order` (
`id` bigint(11) NOT NULL AUTO_INCREMENT,
`product_id` bigint(11) NULL DEFAULT NULL COMMENT '商品Id',
`num` bigint(11) NULL DEFAULT NULL COMMENT '数量',
`user_id` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户唯一Id',
`create_time` datetime(0) NULL DEFAULT NULL,
`status` int(1) NULL DEFAULT NULL COMMENT '订单状态 1 未付款 2 已付款 3 已完成',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Compact;
undo_log:回滚日志表,同仓储服务
配置和seata相关配置
service.vgroupMapping.seata-order-tx-group=default
,如下图:扣减库存的接口
扣减余额的接口
创建订单的接口
@GlobalTransactional
而不是@Transactional
。@GlobalTransactional
是Seata提供的,用于开启才能全局事务,只在TM中标注即可生效。测试
seata-account9021
、seata-storage9020
、seata-order9022
,如下图:总结
seata客户端的版本需要和服务端保持一致 每个服务的数据库都要创建一个 undo_log
回滚日志表客户端指定的事务分组名称要和Nacos相同,比如 service.vgroupMapping.seata-account-tx-group=default
前缀: service.vgroupMapping.
后缀: {自定义}
项目源码已经上传,关注公众号 码猿技术专栏
回复关键词9528
获取!
AT模式原理分析
global_table
:全局事务表,每当有一个全局事务发起后,就会在该表中记录全局事务的IDbranch_table
:分支事务表,记录每一个分支事务的ID,分支事务操作的哪个数据库等信息lock_table
:全局锁
一阶段步骤
TM:seata-order.create()
方法执行时,由于该方法具有@GlobalTranscational
标志,该TM会向TC发起全局事务,生成XID(全局锁)RM:StorageService.deduct()
:写表,UNDO_LOG记录回滚日志(Branch ID),通知TC操作结果RM:AccountService.deduct()
:写表,UNDO_LOG记录回滚日志(Branch ID),通知TC操作结果RM:OrderService.create()
:写表,UNDO_LOG记录回滚日志(Branch ID),通知TC操作结果
二阶段步骤
AT 模式的一阶段、二阶段提交和回滚均由 Seata 框架自动生成,用户只需编写业务 SQL,便能轻松接入分布式事务,AT 模式是一种对业务无任何侵入的分布式事务解决方案。
有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️
评论