目录
(基于 Spring Cloud Alibaba 2.2.9.RELEASE + Seata 1.5.2 官方适配版本整理,覆盖 AT/TCC 模式、源码原理、生产踩坑)
版本说明:根据 Spring Cloud Alibaba 官方组件版本对应关系,2.2.9.RELEASE 原生适配的 Seata 版本为 1.5.2,该版本修复了 1.4.x 系列的若干安全与稳定性问题,新增了 XID 一致性负载均衡、服务端内置控制台等能力,是 2.2.x 生态下的官方推荐生产版本。
一、核心认知:Seata 解决什么问题
1.1 分布式事务的本质痛点
单体应用里,多个操作在同一个数据库中,一个本地事务就能保证要么全成功要么全失败。但微服务架构下,一个业务链路会跨多个服务、多个数据库,比如下单流程:订单服务创订单 → 库存服务扣库存 → 账户服务扣余额,三个服务三个库,本地事务管不了,就会出现订单创了但库存没扣、钱扣了订单没生成的不一致问题。
Seata(Simple Extensible Autonomous Transaction Architecture)就是阿里开源的分布式事务解决方案,专门解决微服务场景下跨服务、跨数据库的数据一致性问题,是 Spring Cloud Alibaba 生态的标准事务组件。
它的核心设计目标:
- 对业务代码低侵入,尽量不改变原有业务写法
- 支持多种事务模式,适配不同业务场景
- 高性能,尽量降低分布式事务带来的性能损耗
- 可视化管理,支持事务监控、异常回查、人工处理
1.2 四种事务模式对比
Seata 一共提供了 4 种事务模式,对应不同的业务场景,不是所有场景都用同一种:
| 模式 | 原理 | 业务侵入性 | 一致性强度 | 适用场景 |
|---|---|---|---|---|
| AT 模式 | 自动生成回滚日志,二阶段提交 / 回滚,基于本地事务 + 全局锁 | 极低,几乎不用改业务代码 | 最终一致 | 绝大多数基于关系型数据库的业务,追求开发效率 |
| TCC 模式 | 手动实现 Try-Confirm-Cancel 三个方法,补偿型事务 | 高,每个事务都要写三个方法 | 最终一致 | 非关系型数据库、需要自定义补偿逻辑、性能要求极高的场景 |
| SAGA 模式 | 长事务拆分,正向执行 + 反向补偿,失败时按顺序回滚 | 中,需要写补偿方法 | 最终一致 | 长链路事务、参与服务多、业务流程长的场景 |
| XA 模式 | 基于数据库 XA 协议,强一致两阶段提交 | 低 | 强一致 | 对一致性要求极高、并发量不高的金融类场景 |
实际项目里 90% 以上都用 AT 模式,也是 Seata 的主推模式;TCC 用在特殊场景;SAGA 和 XA 用得很少。下面笔记重点讲 AT,补充 TCC。
1.3 和其他分布式事务方案的区别
- 可靠消息最终一致性:基于 MQ 实现,适合异步场景,一致性弱,延迟高,优点是性能好。Seata AT 是同步调用,一致性更强,链路更直观。
- 最大努力通知:适合对一致性要求很低的场景,比如短信通知、日志记录。
- 2PC/3PC 传统方案:资源锁定时间长,性能差,可用性低。Seata AT 优化了锁的粒度和持有时间,性能好很多。
二、核心架构与三大角色
2.1 三大核心角色
Seata 的架构里有三个核心组件,各司其职:
-
TC(Transaction Coordinator)事务协调器 独立部署的服务端,负责管理全局事务的状态,接收 TM 的注册、提交、回滚请求,协调各个 RM 执行分支事务的提交或回滚。相当于全局事务的总指挥。
1.5.2 版本 TC 已内置可视化控制台,默认端口 7091,无需单独部署控制台组件,支持事务状态查询、锁信息查看、手动回滚等运维能力。
-
TM(Transaction Manager)事务管理器 在业务发起方的应用里,负责开启全局事务,发起全局提交或回滚的指令。一般加了
@GlobalTransactional注解的方法所在的服务就是 TM。 -
RM(Resource Manager)资源管理器 在每个参与事务的业务服务里,负责管理本地分支事务,和 TC 通信,上报分支事务状态,执行本地事务的提交和回滚。每个操作数据库的服务都是 RM。
2.2 整体执行流程
以 AT 模式下单链路为例,整体交互流程如下:

核心特点:一阶段就把本地事务提交了,释放数据库本地锁,只持有全局锁;二阶段提交就删日志,回滚就用日志补偿。这是 Seata AT 性能比传统 2PC 高的核心原因。
三、Spring Cloud 整合 AT 模式实战
3.1 前置准备
1.部署 Seata Server(TC) 下载 1.5.2 版本 Seata 服务端,修改配置文件指定注册中心(Nacos)和存储模式。1.5.x 版本同时支持 file.conf/registry.conf 传统配置和 application.yml 统一配置,生产推荐使用 YAML 配置。启动后可通过 http://tc地址:7091 访问内置控制台。
2.每个业务库都要建 undo_log 表 AT 模式依赖回滚日志表,每个参与分布式事务的数据库都必须建这张表,这是最容易漏的一步。1.5.x 版本表结构与 1.4.x 完全兼容,核心字段无变化。
CREATE TABLE `undo_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`branch_id` bigint(20) NOT NULL,
`xid` varchar(100) NOT NULL,
`context` varchar(128) NOT NULL,
`rollback_info` longblob NOT NULL,
`log_status` int(11) NOT NULL,
`log_created` datetime NOT NULL,
`log_modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3.TC 服务端 DB 模式建表 如果 TC 使用 db 存储模式,还需要在 TC 的数据库中创建全局事务表、分支事务表、锁表等服务端表结构,1.5.2 官方建表语句可在发布包的 conf/db 目录下获取。
3.2 项目依赖引入
每个参与事务的服务都引入 Seata 依赖,版本由 Spring Cloud Alibaba 父工程统一管理,无需单独指定:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
版本说明:Spring Cloud Alibaba 2.2.9.RELEASE 会自动依赖
io.seata:seata-all:1.5.2,无需手动指定版本,强行指定其他版本容易出现类不兼容、注册失败等问题。
3.3 配置文件
每个服务的 application.yml 里配置 Seata,1.5.x 完全兼容原有配置项:
spring:
cloud:
alibaba:
seata:
# 事务分组名称,要和服务端配置对应
tx-service-group: my_test_tx_group
datasource:
url: jdbc:mysql://127.0.0.1:3306/order_db
username: root
password: 123456
seata:
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: public
group: SEATA_GROUP
service:
vgroup-mapping:
# 事务分组映射到TC集群名
my_test_tx_group: default
3.4 业务代码改造
AT 模式对业务代码侵入极小,只需要在全局事务的发起方法上加一个注解就行,下游服务不用加任何事务相关代码。
以订单服务为发起方为例:
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StockFeignClient stockFeignClient;
@Autowired
private AccountFeignClient accountFeignClient;
/**
* 全局事务注解:开启分布式事务
* 只需要加在事务发起的入口方法上
* rollbackFor = Exception.class 表示所有异常都回滚
*/
@GlobalTransactional(rollbackFor = Exception.class)
@Override
public void createOrder(Long userId, Long goodsId, Integer count) {
// 1. 本地创建订单
Order order = new Order();
order.setUserId(userId);
order.setGoodsId(goodsId);
order.setCount(count);
order.setAmount(new BigDecimal(100).multiply(new BigDecimal(count)));
orderMapper.insert(order);
// 2. 远程调用库存服务,扣库存
stockFeignClient.deductStock(goodsId, count);
// 3. 远程调用账户服务,扣余额
accountFeignClient.deductBalance(userId, order.getAmount());
// 三个操作全部成功,全局事务自动提交
// 任意一步抛异常,全局事务自动回滚
}
}
下游的库存服务、账户服务,正常写业务就行,不用加任何 Seata 注解,RM 会自动拦截 SQL、生成回滚日志、和 TC 通信。
踩坑提醒:
@GlobalTransactional只能加在事务发起方,不能每个服务都加;下游服务如果有本地事务,用@Transactional就行,不要加全局事务注解。
四、AT 模式核心原理
4.1 两阶段执行机制
AT 模式的核心是改进版的两阶段提交,和传统 2PC 最大的区别是一阶段就提交本地事务。
一阶段:业务执行 + 生成回滚日志
- RM 拦截业务 SQL,解析 SQL 语义,找到要修改的数据
- 查询修改前的数据镜像(before image),保存下来
- 执行业务 SQL,更新数据
- 查询修改后的数据镜像(after image),保存下来
- 把前后镜像、SQL 信息组装成回滚日志,写入 undo_log 表
- 向 TC 注册分支事务,申请全局锁
- 本地事务提交,释放数据库本地锁
- 上报分支事务状态给 TC
关键:一阶段结束后,本地事务已经提交了,数据库的行锁、表锁都释放了,只在 TC 里持有全局锁。其他本地事务可以修改别的行,并发性能大大提升。
二阶段:提交 / 回滚
- 全局提交:如果所有分支都成功,TM 通知 TC 全局提交。TC 通知各个 RM 删除对应的 undo_log 记录,释放全局锁。提交过程非常快,只是删日志。
- 全局回滚:如果任一环节失败,TM 通知 TC 全局回滚。TC 通知各个 RM,根据 undo_log 里的前镜像数据,生成反向的 SQL,执行回滚,把数据恢复到修改前的状态,然后释放全局锁。
4.2 undo_log 回滚日志结构
回滚日志是 AT 模式能自动回滚的核心,里面存了数据修改前后的完整镜像:
- xid:全局事务 ID,唯一标识一个全局事务
- branch_id:分支事务 ID,每个服务的分支事务唯一
- rollback_info:核心字段,二进制存储,包含前后镜像数据、SQL 信息、表结构等
- log_status:日志状态,正常、回滚中、回滚完成等
回滚的时候,Seata 会根据前镜像和业务 SQL,自动生成反向的 UPDATE/DELETE/INSERT 语句,把数据还原。比如原来执行的是 update stock set count = count - 1 where id = 1,回滚就执行 update stock set count = count + 1 where id = 1。
4.3 全局锁与写隔离
有个问题:一阶段就提交了本地事务,别的事务修改了同一条数据怎么办?这就是全局锁的作用。
- 本地锁:数据库本身的行锁,一阶段执行 SQL 时持有,本地事务提交就释放
- 全局锁:Seata TC 维护的锁,记录哪些数据行正在被全局事务修改,全局事务结束才释放
写隔离保证:
- 本地事务修改数据前,先拿本地锁
- 提交本地事务前,要先拿到全局锁
- 拿不到全局锁,就不能提交本地事务,会重试等待
- 全局事务没结束,全局锁不释放,其他事务修改同一条数据拿不到全局锁,就提交不了
这样就保证了:被全局事务修改的数据,在全局事务结束前,不会被别的事务改掉,避免了脏写。
4.4 读隔离
默认情况下,Seata AT 模式的读隔离级别是读未提交,因为一阶段本地事务就提交了,别的事务能读到还没最终提交的中间状态。
如果需要读已提交,可以用 select for update 语句,Seata 会拦截这条语句,先去申请全局锁,拿不到锁就等待,保证读到的是全局事务提交后的数据。不过会降低性能,业务里一般很少用,大部分场景能接受短暂的读未提交。
五、TCC 模式补充说明
5.1 什么是 TCC
TCC 是 Try-Confirm-Cancel 的缩写,完全靠业务代码实现补偿,不依赖数据库事务,也不需要 undo_log 表。
- Try 阶段:预留资源,做检查,比如冻结库存、冻结余额,不真正扣减
- Confirm 阶段:确认执行,真正扣减冻结的资源
- Cancel 阶段:取消执行,把冻结的资源释放回去
5.2 和 AT 的区别
- AT 是声明式的,自动生成回滚日志,侵入性低,只能用在支持事务的关系型数据库上
- TCC 是编程式的,全靠自己写三个方法,侵入性高,但不挑数据库,Redis、Mongo 都能用,性能也更高
5.3 TCC 三大经典问题
- 幂等性:Confirm 和 Cancel 可能会被重复调用,必须保证多次调用结果一样,不能重复扣减
- 空回滚:Try 方法没执行到(比如网络丢包),但收到了 Cancel 请求,这时候不能报错,要直接返回成功
- 悬挂:Cancel 比 Try 先执行了(网络乱序),之后 Try 再执行就会预留资源没人释放,导致资源永久冻结
解决方法一般是建一张事务控制表,记录每个分支事务的状态:未执行、Try 成功、Confirm 成功、Cancel 成功,执行每个方法前先查状态,判断能不能执行。
六、生产环境部署与配置
6.1 TC 高可用集群
生产环境不能单点部署 TC,必须做集群:
- 至少部署 3 个 TC 节点,注册到 Nacos 上,组成集群
- 存储模式用 db 模式,全局事务、分支事务、锁信息都存在 MySQL 里,保证 TC 宕机数据不丢
- 客户端通过注册中心发现 TC 节点,自动负载均衡,单节点宕机自动切换
6.2 存储模式选择
| 模式 | 说明 | 适用场景 |
|---|---|---|
| file | 数据存在本地文件,性能好 | 单机测试、演示 |
| db | 数据存在 MySQL,持久化 | 生产集群,保证数据可靠 |
| redis | 数据存在 Redis,性能高 | 高并发、对持久化要求不高的场景 |
生产推荐 db 模式,稳定可靠,数据不丢。
6.3 常用参数调优
client.rm.report-success-enable=false:关闭分支成功上报,减少 TC 压力,只上报失败client.rm.lock.retry-times:全局锁重试次数,根据业务场景调整server.max.commit.retry.timeout:提交重试超时时间server.max.rollback.retry.timeout:回滚重试超时时间
七、项目踩坑汇总
1. 回滚失败
最常见的问题,原因一般有几个:
- 数据库里没建 undo_log 表,或者表结构不对
- 业务表没有主键,Seata 无法定位数据行,生成不了回滚 SQL
- 事务过程中修改了主键,回滚的时候找不到数据
- 其他事务手动修改了数据,导致前后镜像对不上,回滚校验失败
2. 数据源代理失效
Seata AT 必须代理数据源才能拦截 SQL,如果项目里手动配置了数据源,没被 Seata 代理,就会完全不生效,事务不回滚。
解决:确保用了 Spring Boot 自动配置的数据源,或者手动配置 DataSourceProxy 代理原始数据源。
3. 事务传播问题
@GlobalTransactional 不支持嵌套,内部方法再加注解不会开启新的全局事务。跨服务调用靠 Feign 自动传递 XID,本地调用如果不是跨服务,不会走分支事务。
4. Feign 调用 XID 丢失
默认情况下 Seata 会自动通过请求头传递 XID,但如果自定义了 Feign 拦截器、或者用了异步线程,XID 就会丢失,导致下游服务不参与全局事务。
解决:手动在拦截器里把 XID 放到请求头里,异步场景手动传递 XID。
5. 大事务性能差
全局事务链路越长、涉及的服务越多,锁持有时间就越长,并发性能越差,还容易出现死锁。
最佳实践:尽量缩短全局事务链路,非核心操作放到事务外;能不用分布式事务就不用,能用本地事务 + 可靠消息解决的就不用 Seata。
6. 异常没回滚
@GlobalTransactional 默认只回滚 RuntimeException 和 Error,如果抛出了检查型异常(比如 IOException)不会回滚。
解决:注解加上 rollbackFor = Exception.class,所有异常都回滚,和 Spring 的 @Transactional 规则一样。
八、复习速记
- 三大角色:TC 协调器管全局状态,TM 管理器开启 / 提交事务,RM 资源管理器管本地分支。
- AT 核心:一阶段执行业务 + 生成回滚日志 + 提交本地事务 + 持全局锁;二阶段提交删日志,回滚用日志反向补偿。
- 必备条件:每个业务库都要有 undo_log 表,业务表要有主键,数据源要被 Seata 代理。
- 写隔离:靠 TC 维护的全局锁保证,修改数据要先拿全局锁,避免脏写。
- TCC 三问题:幂等、空回滚、悬挂,用事务状态表解决。
- 生产建议:TC 集群部署,db 存储模式;尽量缩短事务链路,不要滥用分布式事务。
- 注解位置:
@GlobalTransactional只加在事务发起方的入口方法上,下游服务不用加。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_44648243/article/details/162848362




