门户网站建设模板/优书网首页
分布式事务介绍
在微服务架构中,完成某一个业务功能可能需要横跨多个服务,操作多个数据库。这就涉及到到了分布式事务,需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一 致性。
两阶段提交协议(2PC)
角色
TC (Transaction Coordinator) - 事务协调者
维护全局和分支事务的状态,驱动全局事务提交或回滚。
TM (Transaction Manager) - 事务管理器
定义全局事务的范围:开始全局事务、提交或回滚全局事务。
RM (Resource Manager) - 资源管理器
管理分支事务处理的资源,与TC交谈以注册分支事务和报告分支事务的状态,并驱动分支事务提交或回滚。
其中,TC 为单独部署的 Server 服务端,TM 和 RM 为嵌入到应用中的 Client 客户端。
概念
两阶段提交(Two Phase Commit),就是将提交(commit)过程划分为两个阶段(Phase):
- 阶段1
TM通知各个RM准备提交它们的事务分支。如果RM判断自己进行的工作可以被提交,那就对工作内容进行持久化,再给TM肯定答复;要是发生了其他情况,那给TM的都是否定答复。
以mysql数据库为例,在第一阶段,事务管理器向所有涉及到的数据库服务器发出==prepare"准备提交"请求,数据库收到请求后执行数据修改和日志记录等处理,处理完成后只是把事务的状态改成commit"可以提交"==,然后把结果返回给事务管理器。
- 阶段2
TM根据阶段1各个RM prepare的结果,决定是提交还是回滚事务。如果所有的RM都prepare成功,那么TM通知所有的RM进行提交;如果有RM prepare失败的话,则TM通知所有RM回滚自己的事务分支。
以mysql数据库为例,如果第一阶段中所有数据库都prepare成功,那么事务管理器向数据库服务器发出=="确认提交"请求,数据库服务器把事务的"可以提交"状态改为"提交完成"状态==,然后返回应答。如果在第一阶段内有任何一个数据库的操作发生了错误,或者事务管理器收不到某个数据库的回应,则认为事务失败,回撤所有数据库的事务。数据库服务器收不到第二阶段的确认提交请求,也会 把=="可以提交"的事务回撤==
两阶段提交方案下全局事务的ACID特性,是依赖于RM的。一个全局事务内部包含了多个独立的事务分支,这一组事务分支要么都成功,要么都失败。各个事务分支的ACID特性共同构成了全局事务的ACID特性。也就是将单个事务分支支持的ACID特性提升一个层次到分布式事务的范畴。
2PC 存在的问题
同步阻塞问题
2PC 中的参与者是阻塞的。在第一阶段收到请求后就会预先锁定资源,一直到 commit 后才会释放。
单点故障
由于协调者的重要性,一旦协调者TM发生故障,参与者RM会一直阻塞下去。尤其在第二阶段, 协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。
数据不一致
若协调者第二阶段发送提交请求时崩溃,可能部分参与者收到commit请求提交了事务,而另一 部分参与者未收到commit请求而放弃事务,从而造成数据不一致的问题。
Seata 分布式事务框架
简介
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。AT模式是阿里首推的模式,阿里云上有商用版本的GTS(Global Transaction Service 全局事务服务)
官网:https://seata.io/zh-cn/index.html
源码: https://github.com/seata/seata
Seata 分布式事务生命周期
在 Seata 中,一个分布式事务的生命周期如下:
- TM 请求 TC 开启一个全局事务。TC 会生成一个 XID 作为该全局事务的编号。XID会在微服 务的调用链路中传播,保证将多个微服务的子事务关联在一起。
- RM 请求 TC 将本地事务注册为全局事务的分支事务,通过全局事务的 XID 进行关联。
- TM 请求 TC 告诉 XID 对应的全局事务是进行提交还是回滚。
- TC 驱动 RM 们将 XID 对应的自己的本地事务进行提交还是回滚。
SQL限制
Seata 事务目前支持 INSERT、UPDATE、DELETE 三类 DML 语法的部分功能,这些类型都是已经经过 Seata 开源社区的验证。SQL 的支持范围还在不断扩大,建议在本文限制的范围内使用。如果您有意帮助社区支持更多类型的 SQL,请提交 PR 申请。
使用限制
- 不支持 SQL 嵌套
- 不支持多表复杂 SQL(自1.6.0版本,MySQL支持UPDATE JOIN语句,详情请看 )
- 不支持存储过程、触发器
- 部分数据库不支持批量更新,在使用 MySQL、Mariadb、PostgreSQL9.6+作为数据库时支持批量,批量更新方式如下以 Java 为例
// use JdbcTemplatepublic void batchUpdate() {jdbcTemplate.batchUpdate("update storage_tbl set count = count -1 where id = 1","update storage_tbl set count = count -1 where id = 2");}// use Statementpublic void batchUpdateTwo() {statement.addBatch("update storage_tbl set count = count -1 where id = 1");statement.addBatch("update storage_tbl set count = count -1 where id = 2");statement.executeBatch();}
快速开始
下载:seata.io
Seata分TC、TM和RM三个角色,TC(Server端)为单独服务端部署,TM和RM(Client端)由业务系统集成。
资源目录:
https://github.com/seata/seata/tree/v1.5.1/script
- client :存放client端sql脚本,参数配置
- config-center :存放各个配置中心参数导入脚本,config.txt(包含server和client)为通用参数文件
- server :存放数据库脚本及各个容器配置
TC 端环境搭建
修改配置文件
配置文件的位置在/seata/config下
配置中心设置如果nacos上的配置与application.yml有相同配置,则覆盖
示例配置
实际配置(使用nacos
)
向nacos
上传配置文件
直接官方提供的即可,\script\config-center\config.txt
,复制文件内容,在nacos
上按照上述配置创建配置文件
TC 端注册中心设置
实际配置(使用nacos
)
日志配置
存储模式配置
Server端存储模式(store.mode)支持三种:
- file :单机模式,全局事务会话信息内存中读写并持久化本地文件root.data,性能较高
- db :高可用模式,全局事务会话信息通过db共享,相应性能差些
- 目前仅支持 mysql、oracle、postgresql
- redis :1.3及以上版本支持,性能较高,存在事务信息丢失风险,请提前配置适合当前场景的redis持久化配置
实际配置(使用db)
在naocs配置中心修改配置文件
由于seata是通过jdbc的executeBatch来批量插入全局锁的,根据MySQL官网的说明,连接参数中的rewriteBatchedStatements为true时,在执行executeBatch,并且操作类型为insert时,jdbc驱动会把对应的SQL优化成insert into () values (), ()的形式来提升批量插入的性能。根据实际的测试,该参数设置为true后,对应的批量插入性能为原来的10倍多,因此在数据源为MySQL时,建议把该参数设置为true。
创建需要的表(mysql)
在配置中的数据库下,执行\seata\script\server\db\mysql.sql文件即可
事务分组设置
配置事务分组, 之后client端配置需与其的事务分组一致
- 事务分组:seata的资源逻辑,可以按微服务的需要,在应用程序(客户端)对自行定义事务分组,每组取一个名字。
- 集群:seata-server服务端一个或多个节点组成的集群cluster。 应用程序(客户端)使用时需要指定事务逻辑分组与Seata服务端集群的映射关系。
在naocs配置中心修改配置文件
事务分组如何找到后端Seata集群(TC)?
- 首先应用程序(客户端)中配置了事务分组(GlobalTransactionScanner 构造方法的txServiceGroup参数)。若应用程序是SpringBoot则通过seata.tx-service-group配置。
- 应用程序(客户端)会通过用户配置的配置中心去寻找service.vgroupMapping .[事务分组配置项],取得配置项的值就是TC集群的名称。若应用程序是SpringBoot则通过seata.service.vgroup-mapping.事务分组名=集群名称 配置
- 拿到集群名称程序通过一定的前后缀+集群名称去构造服务名,各配置中心的服务名实现不同(前提是Seata-Server已经完成服务注册,且Seata-Server向注册中心报告cluster名与应用程序(客户端)配置的集群名称一致)
- 拿到服务名去相应的注册中心去拉取相应服务名的服务列表,获得后端真实的TC服务列表(即Seata-Server集群节点列表)
启动Seata Server
双击/bin/seata-server.bat
启动
在nacos
上注册成功
支持的启动参数
比如:
bin/seata-server.sh -p 8091 -h 127.0.0.1 -m db
Client(TM / RM) 端搭建(AT为例)
AT
模式依赖于seata
的undo_log
回滚日志,来进行事务的回滚。所以需要为每个微服务的对应的库创建undo_log
日志表(如果都在同一个库则仅需要一张undo_log
表即可)。
-- 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 = InnoDBAUTO_INCREMENT = 1DEFAULT CHARSET = utf8mb4 COMMENT ='AT transaction mode undo table';
TM 端
maven 依赖
<dependencies><!-- web容器 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- nacos 注册中心--><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><!-- openFeign --><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId><scope>provided</scope></dependency><!--Mysql数据库驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- druid连接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId></dependency><!-- seata 分布式事务 --><!-- 这里需要使用整合包,其实现了TM通过feign将xid向下传递(TM端关注) --><!-- 不使用整合包也可以自实现restTemplate扩展的client接口,实现xid向下传递 --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-seata</artifactId><exclusions><exclusion><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId></exclusion></exclusions></dependency><!--升级使用1.6.0版本--><dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.6.0</version></dependency><!-- MyBatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId></dependency></dependencies>
application.xml 配置
server:port: 7100spring:application:name: xa-ordercloud:# 应用自身nacos注册地址nacos:discovery:server-addr: 127.0.0.1:8848password: nacosusername: nacosdatasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/seata_order?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghaiusername: rootpassword: root# seata配置需保持和server端一致
seata:application‐id: ${spring.application.name}# 数据源代理模式 默认ATdata-source-proxy-mode: XA# seata 服务分组,要与服务端配置service.vgroup_mapping的后缀对应tx‐service‐group: default_tx_group# TC注册中心配置registry:type: nacosnacos:application: seata-serverserver-addr: 127.0.0.1:8848group: SEATA_GROUPnamespace: ad50a46c-e62f-4aa6-9ad2-1b1edbaaee03client:# tm端配置tm:# 一阶段全局提交结果上报TC重试次数 (配置值使用的是默认值)commit-retry-count: 5# 一阶段全局回滚结果上报TC重试次数 (配置值使用的是默认值)rollback-retry-count: 5# 全局事务超时时间 (配置值使用的是默认值)default-global-transaction-timeout: 6000# TM全局事务拦截器顺序 (配置值使用的是默认值)# 保证拦截器在本地事务拦截器之前执行,也可自定义全局事务和业务开发的拦截器执行顺序interceptor-order: -2147482648# 分布式事务降级开关degrade-check: false
业务代码有@GlobalTransactional
的即为TM
端
RM 端
RM
端和TM
端一致,如果不充当TM
则不需要使用cloud
整合依赖
<!-- seata 分布式事务 -->
<dependency><groupId>io.seata</groupId><artifactId>seata-spring-boot-starter</artifactId><version>1.6.0</version>
</dependency>
事务模式
AT 模式(推荐)
前提
基于支持本地 ACID 事务的关系型数据库。
Java 应用,通过 JDBC 访问数据库。
整体机制
两阶段提交协议的演变:
- 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
- 二阶段:
- 提交异步化,非常快速地完成。
- 回滚通过一阶段的回滚日志进行反向补偿。
设计思路
Seata AT模式的核心是对业务无侵入,是一种改进后的两阶段提交,其设计思路如下:
- 一阶段:业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。
- 二阶段:
- 提交异步化,非常快速地完成。
- 回滚通过一阶段的回滚日志进行反向补偿。
一阶段
业务数据和回滚日志记录在同一个本地事务中提交,释放本地锁和连接资源。核心在于对业务sql
进行解析,转换成undo_log
,并入库。
二阶段
分布式事务操作成功,则TC
通知RM
异步删除undo_log
。
分布式事务操作失败,TM
向TC
发送回滚请求,RM
收到协调器TC
发来的回滚请求,通过 XID
和 Branch ID
找到相应的回滚日志记录,通过回滚记录生成反向的更新 SQL
并执行,以完成分支的回滚。
大致流程
- TM发起全局事务开启请求TC,TC返回XID,并构建全局事务信息存储到global_table表中。
- TM端执行业务方法,并将XID向下游远程调用传递。
- 各个RM端执行自身的事务方法,并记录到seata的undo_log中。并向TC提交一阶段的分支事务记录
- 整个过程无异常,TM发起全局事务提交请求TC。
- TC释放全局锁,删除对应的全局锁记录,更新全局事务状态。
- TC向各分支事务发起异步二阶段分支事务提交。
- 各RM异步提交删除之前的undo_log日志任务到队列,返回二阶段提交完成状态。
- 整个过程出现异常,TM发起全局事务回滚请求TC。
- TC修改全局事务状态:Begin—>Rollbacking
- TC向各分支事务发起远程调用,通知RM删除对应undo_log
- 各RM收到通知,校验undo_log(数据的前后镜像对比)
成功:分支事务二阶段回滚成功
失败:分支事务二阶段回滚失败
镜像不一致失败,不进行重试
网络等其他原因,进行重试
4.TC收到各分支事务的响应,返回全局事务状态给TM
- TM收到TC响应的全局事务状态,若则失败根据事务状态进行处理
快速使用
按照上面的 Client端搭建即可。AT模式对业务代码几乎无侵入,仅需要在对应的TM端加上@GlobalTransactional注解即可。
回滚失败异常处理
Seata提供了 FailureHandler 可扩展接口,可以让开发自行处理一些提交或回滚失败后的处理。
作用于 TM端,利用各分支事务二阶段处理结果返回给TC,TC再将二阶段事务执行结果返回给TM。
import io.seata.tm.api.DefaultFailureHandlerImpl;
import io.seata.tm.api.FailureHandler;
import io.seata.tm.api.GlobalTransaction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@Slf4j
public class SeataFailureHandlerConfig {@Beanpublic FailureHandler failureHandler(){return new EmailSeataFailureHandler();}class EmailSeataFailureHandler extends DefaultFailureHandlerImpl {@Overridepublic void onBeginFailure(GlobalTransaction tx, Throwable cause) {super.onBeginFailure(tx, cause);log.warn("邮件通知:分布式事物出现异常:[onBeginFailure],xid:[{}]", tx.getXid());}@Overridepublic void onCommitFailure(GlobalTransaction tx, Throwable cause) {super.onCommitFailure(tx, cause);log.warn("邮件通知:分布式事物出现异常:[onCommitFailure],xid:[{}]", tx.getXid());}@Overridepublic void onRollbackFailure(GlobalTransaction tx, Throwable originalException) {super.onRollbackFailure(tx, originalException);log.warn("邮件通知:分布式事物出现异常:[onRollbackFailure],xid:[{}]", tx.getXid());}@Overridepublic void onRollbackRetrying(GlobalTransaction tx, Throwable originalException) {super.onRollbackRetrying(tx, originalException);log.warn("邮件通知:分布式事物出现异常:[onRollbackRetrying],xid:[{}]", tx.getXid());}}
}
注意事项
AT模式的回滚利用的是undo_log日志,所以如果在undo_log生成后,回滚前,这条数据被其他业务或人为所修改,则无法进行回滚(前后镜像不一致);且全局事务锁不会释放,那么意味着这条业务线在数据修正前将一直无法使用。
解决方案
利用上述的重写FailureHandler进行邮件、短信等通知人为及时处理
避免在全局事务执行期间RM端涉及业务数据被其他业务修改
如果是同方法下该RM必须被多个业务调用,则可以在该业务方法上使用@GlobalTransactional注解进行全局事务锁控制,这样就控制业务逐一执行,避免脏数据产生。
————————————————
XA 事务模式
前提
- 支持 XA 事务的数据库。
- Java 应用,通过 JDBC 访问数据库。
整体机制
在 Seata 定义的分布式事务框架内,利用事务资源(数据库、消息服务等)对 XA 协议的支持,以 XA 协议的机制来管理分支事务的一种事务模式。
执行阶段:
*
可回滚:业务 SQL 操作放在 XA 分支中进行,由资源对 XA 协议的支持来保证 可回滚
持久化:XA 分支完成后,执行 XA prepare,同样,由资源对 XA 协议的支持来保证 持久化(即,之后任何意外都不会造成无法回滚的情况)
完成阶段:
*
分支提交:执行 XA 分支的 commit
分支回滚:执行 XA 分支的 rollback
详见官网介绍
与 AT 模式的区别
AT的前提是支持ACID的关系型数据库。
XA的前提是支持XA事务的数据库。
XA模式的分支事务会一直等待TM向TC响应执行结果,再进行回滚或提交,在这期间由事务产生的锁是一直占用资源的。
AT模式则是异步化的,根据undo_log进行回滚。
XA的分支事务注册由TC统一生成的,所以 XA 模式分支注册的时机需要在 XA start 之前(未来也许会向AT模式一样)。
AT 模式则是在本地事务提交之前才注册分支,可以避免分支执行失败的情况下进行无意义的分支注册。
XA模式代码需要利用本地事务@Transactional注解,否则方法内的本地事务执行sql会有事务冲突,造成死锁
快速使用
相比较 AT 模式需要做的改动仅有两点:
TM端需要加上Spring的@Transactional注解
@Override
@Transactional
@GlobalTransactional(name="createOrder",rollbackFor=Exception.class)
public Order saveOrder(OrderVo orderVo, Boolean hasException) {log.info("=============用户下单=================");log.info("当前 XID: {}", RootContext.getXID());// ...
}
yml
配置文件中指定数据源代理模式为 XA
# seata配置需保持和server端一致
seata:application‐id: ${spring.application.name}# 数据源代理模式 默认ATdata-source-proxy-mode: XA# seata 服务分组,要与服务端配置service.vgroup_mapping的后缀对应tx‐service‐group: default_tx_group
TCC 模式
前提
TCC 模式,不依赖于底层数据资源的事务支持。它是一种手动控制的模式,可以应用在各种数据库中,例:Redis等。
整体机制
一个分布式的全局事务,整体是 两阶段提交 的模型。全局事务是由若干分支事务组成的,分支事务要满足 两阶段提交 的模型要求,即需要每个分支事务都具备自己的:
- 一阶段 prepare 行为
- 二阶段 commit 或 rollback 行为
TCC 模式不依赖于底层数据资源的事务支持:
- 一阶段 prepare 行为:调用自定义的 prepare 逻辑。
- 二阶段 commit 行为:调用自定义的 commit 逻辑。
- 二阶段 rollback 行为:调用自定义的 rollback 逻辑。
TCC 异常控制机制
-
TCC 模式是分布式事务中非常重要的事务模式,但是幂等、悬挂和空回滚一直是 TCC 模式需要考虑的问题。
Seata 的做法是在客户端新增一个 TCC 事务控制表( tcc_fence_log ),里面记录了分支事务一阶段操作的 XID、BranchId 及执行状态等,后续会根据这个记录处理各异常。
空回滚
空回滚指的是在一个分布式事务中,在没有调用参与方的 Try 方法的情况下,TM 驱动二阶段回滚调用了参与方的 Cancel 方法。
产生原因
如上图所示,全局事务开启后,参与者 A 分支注册完成之后会执行参与者一阶段 RPC 方法,如果此时参与者 A 所在的机器发生宕机、网络异常,都会造成 RPC 调用失败,即参与者 A 一阶段方法未成功执行,但是此时全局事务已经开启,Seata 必须要推进到终态,在全局事务回滚时会调用参与者 A 的 Cancel 方法,从而造成空回滚。
解决
根据 tcc_fence_log 的记录,在执行 Cancel / Rollback 方法时读取这条记录,如果记录不存在,说明 Try 方法没有执行。
幂等
幂等问题指的是 TC 重复进行二阶段提交,因此 Cancel 接口需要支持幂等处理,即不会产生资源重复提交或者重复释放。
产生原因

如上图所示,参与者 A 执行完二阶段之后,由于网络抖动或者宕机问题,会造成 TC 收不到参与者 A 执行二阶段的返回结果,TC 会重复发起调用,直到二阶段执行结果成功。
解决
根据 tcc_fence_log 记录状态的字段 status,该字段有 4 个值,分别为:
tried:1
committed:2
rollbacked:3
suspend:4
二阶段 Confirm / Cancel 方法执行后,将状态改为 committed 或 rollbacked 状态。当重复调用二阶段 Confirm/Cancel 方法时,判断事务状态即可解决幂等问题。
悬挂
悬挂指的是二阶段 Cancel 方法比 一阶段 Try 方法优先执行,由于允许空回滚的原因,在执行完二阶段 Cancel 方法之后直接空回滚返回成功,此时全局事务已结束,但是由于 Try 方法随后执行,这就会造成一阶段 Try 方法预留的资源永远无法提交和释放了。
产生原因
如上图所示,在执行参与者 A 的一阶段 Try 方法时,出现网路拥堵,由于 Seata 全局事务有超时限制,执行 Try 方法超时后,TM 决议全局回滚,回滚完成后如果此时 RPC 请求才到达参与者 A,执行 Try 方法进行资源预留,从而造成悬挂。
解决
当执行二阶段 Cancel 方法时,如果发现 TCC 事务控制表有相关记录,说明二阶段 Cancel 方法优先一阶段 Try 方法执行,因此插入一条 status=4 状态的记录,当一阶段 Try 方法后面执行时,判断 status=4 ,则说明有二阶段 Cancel 已执行,并返回 false 以阻止一阶段 Try 方法执行。
与 AT 等模式的区别
- AT等模式需要依赖数据库的事务特性
- TCC模式则是通过调用自定义的逻辑进行事务控制
- AT等模式对业务的侵入几乎为0
- TCC模式由于是调用自定义的逻辑,所以对业务有较大的的侵入
- AT模式利用了undo_log的镜像记录进行自动回滚处理
- TCC模式调用自定义的回滚逻辑,不过针对一些特殊的异常利用了tcc_fence_log 记录表
快速使用
TCC模式与之前两种的模式实现上有较大的区别,主要在于其是通过调用自定义的逻辑去实现事务控制
客户端建立 tcc_fence_log 表
TCC模式对一些特殊的异常处理依赖于tcc_fence_log表,所以需要在各客户端建立该表。(若不考虑这些特殊异常可以不使用)
-- 创建 tcc_fence_log 表,支持 tcc 解决空回滚、悬挂、幂等问题
CREATE TABLE IF NOT EXISTS `tcc_fence_log`
(`xid` VARCHAR(128) NOT NULL COMMENT 'global id',`branch_id` BIGINT NOT NULL COMMENT 'branch id',`action_name` VARCHAR(64) NOT NULL COMMENT 'action name',`status` TINYINT NOT NULL COMMENT 'status(tried:1;committed:2;rollbacked:3;suspended:4)',`gmt_create` DATETIME(3) NOT NULL COMMENT 'create time',`gmt_modified` DATETIME(3) NOT NULL COMMENT 'update time',PRIMARY KEY (`xid`, `branch_id`),KEY `idx_gmt_modified` (`gmt_modified`),KEY `idx_status` (`status`)) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
配置文件中也可以指定表名
# seata配置需保持和server端一致
seata:application‐id: ${spring.application.name}# seata 服务分组,要与服务端配置service.vgroup_mapping的后缀对应tx‐service‐group: default_tx_group# tcc 配置tcc:fence:# 表名配置log-table-name: tcc_fence_log
TCC 接口编写
TCC
模式依赖于自定义逻辑,所以需要自己去定义一阶段的预提交,二阶段的回滚/提交方法
注意: commit
和 rollback
方法必须有BusinessActionContext
,否则无法获取到上下文数据(除非不需要利用上下文数据)
import cn.zh.order.domain.vo.OrderVo;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.LocalTCC;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;/*** @author zh* OrderService对应的Tcc接口*/
@LocalTCC
public interface OrderServiceSeataTcc {/*** TCC的try方法:保存订单信息,状态为支付中** 定义两阶段提交,在try阶段通过@TwoPhaseBusinessAction注解定义了分支事务的 resourceId,commit和 cancel 方法* name = 该tcc的bean名称,全局唯一* commitMethod = commit 为二阶段确认方法* rollbackMethod = rollback 为二阶段取消方法* BusinessActionContextParameter注解 传递参数到二阶段中* useTCCFence seata1.5.1的新特性,用于解决TCC幂等,悬挂,空回滚问题,需增加日志表tcc_fence_log** @param orderVo* @return*/@TwoPhaseBusinessAction(name = "prepareSaveOrder", commitMethod = "commit", rollbackMethod = "rollback", useTCCFence = true)Integer prepareSaveOrder(OrderVo orderVo);/**** TCC的confirm方法:订单状态改为支付成功** 二阶段确认方法可以另命名,但要保证与commitMethod一致* context可以传递try方法的参数** @param actionContext* @return*/void commit(BusinessActionContext actionContext);/*** TCC的cancel方法:订单状态改为支付失败* 二阶段取消方法可以另命名,但要保证与rollbackMethod一致** @param actionContext* @return*/boolean rollback(BusinessActionContext actionContext);}
注解 / 参数说明
@LocalTCC
声明TCC接口Bean。适用于SpringCloud+Feign模式下,@LocalTCC需要注解在接口上,此接口可以是寻常的业务接口(并不需要一定独立),只要实现了TCC的两阶段提交对应方法便可。
@TwoPhaseBusinessAction
作用于一阶段预提交方法,常用参数:
name:当前tcc方法的bean名称,需保证全局唯一
commitMethod:指向当前接口中的提交方法,默认方法名 commit
rollbackMethod:指向当前接口的回滚方法,默认方法名 rollback
定义完三个方法后,seata会根据全局事务的成功或失败,去帮我们自动调用提交方法或者回滚方法。
@BusinessActionContextParameter
作用于预提交方法的参数上,被注解的参数会被传入 BusinessActionContext(TCC的业务上下文),这个类会被自动传递到另外两个方法
BusinessActionContext
TCC的业务上下文,内部利用Map结构对数据存储。
public class BusinessActionContext implements Serializable {private static final long serialVersionUID = 6539226288677737991L;private String xid;private String branchId;private String actionName;private Boolean isDelayReport;private Boolean isUpdated;private Map<String, Object> actionContext;// ...}
此外数据传递是利用了BusinessActionContextUtil,这个工具类利用ThreadLocal对BusinessActionContext进行数据传递,所以我们也可不使用@BusinessActionContextParameter,而直接使用BusinessActionContextUtil进行参数传递
public final class BusinessActionContextUtil {private BusinessActionContextUtil() {}private static final Logger LOGGER = LoggerFactory.getLogger(BusinessActionContextUtil.class);// ThreadLocalprivate static final ThreadLocal<BusinessActionContext> CONTEXT_HOLDER = new ThreadLocal<>();public static boolean addContext(String key, Object value) {if (value == null) {return false;}Map<String, Object> newContext = Collections.singletonMap(key, value);return addContext(newContext);}// ...
}// 例子
@Override
public Integer prepareSaveOrder(OrderVo orderVo) {// 保存订单Order order = new Order();order.setUserId(orderVo.getUserId());order.setCommodityCode(orderVo.getCommodityCode());order.setCount(orderVo.getCount());order.setMoney(orderVo.getMoney());order.setStatus(OrderStatus.INIT.getValue());Integer saveOrderRecord = orderMapper.insert(order);log.info("保存订单{}", saveOrderRecord > 0 ? "成功" : "失败");// 内部添加上下文数据BusinessActionContextUtil.addContext("orderId", order.getId());return saveOrderRecord;
}
TM 端方式使用 @GlobalTransactional 注解
@Override
@GlobalTransactional(name="createOrder",rollbackFor=Exception.class)
public void saveOrder(OrderVo orderVo, Boolean hasException) {// ...
}
Saga模式
Saga模式通常适用于业务流程长或多的场景。
这里不过多介绍,详见官网:https://seata.io/zh-cn/docs/user/saga.html
其他补充
除 AT 模式外二阶段只有成功 / 失败重试两种逻辑?
Seata设计认为二阶段应处于最终一致性状态,当不一致时理应就不断的去重试直到成功,并且二阶段并不应该考虑各种异常情况,异常应当只会有数据库连接不上这种情况。针对这种情况做法应该是监控系统及时报警然后及时恢复数据库的连接。
对于TCC模式二阶段的处理不应该有太多的复杂逻辑存在;若是程序的编码错误,不应该考虑在内。
对于AT模式由于其二阶段回滚依赖于undo_log镜像所以提供了失败异常处理。
TCC 模式异常重试机制
在TCC模式中如果二阶段出现异常,则会每秒一次去不断重试(1.6.0,后续或会增加重试间隔配置),即使服务重启也会去不断重试;这是根据server端记录的事务状态去驱动执行客户端二阶段方法,在server端branch_table中会记录此次的上下文信息,并传递执行二阶段。
示例项目代码地址
gitee:https://gitee.com/ahang-gitee/learn-seata
相关文章:

分布式事务seata的使用
分布式事务介绍 在微服务架构中,完成某一个业务功能可能需要横跨多个服务,操作多个数据库。这就涉及到到了分布式事务,需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据操作,要么全部成功&…...

使用宝塔面板安装mysql
1.第一步 在官网https://www.bt.cn/new/download.html下载页面直接在服务器控制面板复制这里的代码下载即可 2.第二步 下载好后按照服务器面版上有个公网地址,含有用户名和密码,保存好,然后通过公网地址打开一个网页,绑定自己注册…...

Flink 支持三种时间语义
在 Apache Flink 中,时间在流处理中是一个重要的概念,而时间语义则用于定义事件发生的时间。Flink 支持三种时间语义,分别是: Processing Time(处理时间): 以机器的系统时间为基准,…...

【大话Presto 】- 核心概念
文章目录 前言Operator Model And Iterator Model系统组成Connector数据模型查询执行模型StatementStageTaskSplitDriverOperatorExchangePipeLine 总结 前言 Presto(PrestoDB)是一个FaceBook开源的分布式MPP SQL引擎,旨在处理大规模数据的查…...

阎良区公益创投之“小飞机大梦想” 航模DIY主题活动
创造是人类探索迈出的第一步,科学是开启奇妙世界的金钥匙。为进一步提升“未来星”对科技知识的兴趣,培养他们的科学创新精神,11月16日,阎良区社会组织公益创投——“未来星”助力乡村留守儿童成长计划项目在阎良区聚宝小学开展“…...

springBoot集成websocket实时消息推送
springBoot集成websocket实时消息推送 WebSocket是一种在Web应用程序中实现双向通信的协议。它允许在客户端和服务器之间建立持久性的连接,并支持双向数据传输,实现了实时、低延迟的通信。 📍常见的消息推送方法 WebSocket:通过使…...

web:[BUUCTF 2018]Online Tool
题目 打开页面显示如下,进行代码审计 上述代码主要功能是接收‘host’参数,后使用nmap扫描主机端口 首先检查是否存在HTTP_X_FORWARDED_FOR头,若存在,将值赋值给EMOTE_ADDR,是为了跟踪用户真实的IP地址 后用检查get‘host’是否…...

决策树的Boosting策略是什么
在决策树的Boosting策略中,最常见的算法是梯度提升决策树(Gradient Boosting Decision Trees,简称GBDT)。GBDT是一种集成学习方法,通过串行训练多个决策树,并根据前一个树的预测结果来调整下一个树的训练目…...

SQL Server中substring的用法
SQL Server中substring的用法 SQL中的substring函数是用来截取一个栏位资料中的其中一部分。 例如,我们需要将字符串’abdcsef’中的‘abd’给提取出来,则可用substring 来实现: select substring(abdcsef,1,3)结果为 abd括号中数字’1’表…...

vscode设置latex
vscode配置latex 1.安装vscode,并添加环境变量路径 2.安装latex,bin文件夹添加到环境变量路径 3.vscode安装插件 4.vscode->文件->首选项->显示配置内容->setting.json文件,查看其位置目录,通过我的电脑找到此文件(不要使用v…...

Django模板层
模板之变量 所有的数据类型都可以在模板中使用 render(request, index.html, context{}) render(request, index.html, contextlocals()) """在模板中使用变量的时候,用的是字典的key值,key值value值一般保持一致"""详细…...

TP_Link WR886N 硬改闪存16M内存64M,刷入openwrt
一、换内存,拆闪存: 1、先原机开机试试是否功能正常; 2、拆机,比较难拆,容易坏外壳; 3、找到内存和闪存,用胶带把边上的小元件,电阻都贴好; 4、加助焊油,用风…...

websocket详解
一、什么是Websocket WebSocket 是一种在单个 TCP 连接上进行 全双工 通信的协议,它可以让客户端和服务器之间进行实时的双向通信。 WebSocket 使用一个长连接,在客户端和服务器之间保持持久的连接,从而可以实时地发送和接收数据。 在 Web…...

可以免费使用的设计素材网站分享
UI设计师最怕什么? 没有创意,没有灵感,没有思路! 在哪里可以得到idea?别担心,往下看! 你知道网络有多大,你想要什么吗?今天,我想和大家分享一些宝藏网页设…...

workman使用手册1.0
workman官网地址:高性能PHP应用容器 workerman 1:把workman项目放到linux服务器后,需要启动你的php文件,才可以使用 定位到项目根目录:例:cd /mnt/workman 启动代码:php outin.php start -d 停…...

Cesium深入浅出之自定义材质
引子 做为一名技术宅却没有能拿得出手的技术无疑是最可悲的事情。三年前,当我第一次接触Cesium的时候就被它强大和炫丽所折服,最关键的是它还是开源的。以前我一直是机械地敲着业务代码,好像计算机程序就只能干这点事情一样,而 C…...

Appium移动自动化测试--安装Appium
Appium 自动化测试是很早之前就想学习和研究的技术了,可是一直抽不出一块完整的时间来做这件事儿。现在终于有了。 反观各种互联网的招聘移动测试成了主流,如果再不去学习移动自动化测试技术将会被淘汰。 web自动化测试的路线是这样的:编程语…...

前端学习笔记--ES6
修正 ES6是ECMA为JavaScript制定的第6个标准版本,相关历史可查看此章节《ES6-ECMAScript6简介》。 标准委员会最终决定,标准在每年6月正式发布并作为当年的正式版本,接下来的时间里就在此版本的基础上进行改动,直到下一年6月草案…...

冥想第九百七十八天
1.周四,今天客户又发飙了,这次的锅我有点不好受。因为压力很大,所以日语课就不去上了。自己做了题。 2.今天中午跑了步,心率还是有点高了,估计跟穿得最后得薄款羽绒服有关。经过昨天晚上的休息,感觉身体好多…...

Maven分离资源文件
Spring Boot 项目默认的会将所有资源文件、依赖文件、配置文件等打包成单一的 jar 文件,但是有时候我们并不想让配置文件、依赖包都跟可执行文件打包到一起。 这时候可以在 pom.xml 文件中进行配置,从而使资源文件、依赖包和可执行文件分离。 本文主要…...

Linux CentOS 8(MariaDB概述)
Linux CentOS 8(MariaDB概述) 目录 一、项目描述二、相关知识2.1 数据库的基本介绍2.2 数据库的分类介绍 三、项目分析3.1 安装并启动 MariaDB3.2 登录 MariaDB 数据库3.3 提高 MariaDB 安装安全性 一、项目描述 Jan16 公司为满足部门之间数据共享、减少…...

简述几个我们对Redis 7开源社区所做的贡献
Redis 7 已经于2022年4月28号正式发布,其中包括了将近50个新的命令,增加了许多新的特性,并且在整个Redis 6到Redis 7的开发过程中,我也对Redis 的开源社区贡献了一些微薄的力量。在这篇文章中,我来给大家介绍几个自己亲…...

产品卖点怎么写,如何打造卖点?
...

跟李沐学AI-深度学习课程00-03【预告、课程安排、深度学习介绍、安装】
目录 00 预告 01 课程安排 02 深度学习介绍 03 安装 本地安装 04 数据操作数据预处理 数据操作 数据类型 创建数组 访问元素 数据操作实现 入门 运算符 广播机制 索引和切片 节省内存 转换为其他Python对象 数据预处理实现 读取数据集 处理缺失值 转换为张…...

C++ this 指针 面试
this 指针 this 指针是一个隐含于每一个非静态成员函数中的特殊指针。它指向调用该成员函数的那个对象。 当对一个对象调用成员函数时,编译程序先将对象的地址赋给 this 指针,然后调用成员函数,每次成员函数存取数据成员时,都隐…...

虚拟机上安装docker,并安装flink镜像
1. 安装docker 官网步骤:https://docs.docker.com/engine/install/centos/ sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo yum install docker-ce docker-ce-cli containerd.…...

【计算机网络】P1 计算机网络概述
P1 计算机网络概述 概念组成角度1:组成部分角度2:工作方式角度3:功能组成 功能分类按分布范围分按使用者分按交换技术按拓扑结构分按传输技术分 标准化工作及相关组织标准化工作相关组织 概念 三网融合 通过 计算机网络,将 电信网…...

003 OpenCV filter2D
目录 一、环境 二、图像卷积 三、代码演示 3.1、锐化 3.2、sobel边缘,x方向 3.3、sobel边缘,y方向 3.4、高斯模糊 3.5、完整代码 一、环境 本文使用环境为: Windows10Python 3.9.17opencv-python 4.8.0.74 二、图像卷积 在OpenCV…...

轻松玩转华为MateX5分屏功能,乐趣层出不穷!
✅小窗交互,沉浸体验不打断; ✅分区截屏,花式截图,一招搞定; ✅跨屏拖拽,随心分享易如反掌; ✅悬停视频会议,沟通效率大不同。...

springboot引入redisson分布式锁及原理
1.引入依赖 <dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.6</version> </dependency>2.配置类创建bean /*** author qujingye* Classname RedissonConfig* Description TOD…...