本文目录导读:

这是一个非常核心且实用的分布式技术栈组合。Spring Cloud Alibaba + Seata 是目前 Java 微服务领域解决分布式事务问题的主流方案。
下面我将从 核心概念、AT模式原理、实战配置、注意事项 四个维度,帮你系统地梳理 Seata 分布式事务。
核心概念:Seata 的三大组件
Seata(Simple Extensible Autonomous Transaction Architecture)的设计思路是解耦,它把一次分布式事务拆解为三个核心角色:
- TC (Transaction Coordinator) - 事务协调器
- 这是独立部署的 Seata-Server。
- 负责维护全局事务和分支事务的状态,驱动全局事务的提交或回滚。
- TM (Transaction Manager) - 事务管理器
- 定义事务的范围,比如在微服务A(发起方)的方法上,通过
@GlobalTransactional注解标记,该方法就是全局事务的起点和终点。 - TM 负责向 TC 申请、开启、提交或回滚全局事务。
- 定义事务的范围,比如在微服务A(发起方)的方法上,通过
- RM (Resource Manager) - 资源管理器
- 管理分支事务,通常是每一个参与事务的微服务数据库或消息队列。
- RM 向 TC 注册分支事务,并负责执行本地事务(SQL)以及分支的提交/回滚。
工作流程: TM 通知 TC 开启全局事务 -> 微服务A 调用 微服务B -> B 的 RM 向 TC 注册分支 -> ... -> 全部成功 -> TM 通知 TC 提交 -> TC 通知所有 RM 提交 -> 完成。
最核心的 AT 模式原理
Seata 有多种模式(AT、TCC、Saga、XA),AT 模式 是最常用且对业务代码侵入最小的自动补偿模式。
核心思想: 两阶段提交(2PC)+ 快照回滚。
Branch(执行与记录)
- 拦截 SQL。
- 解析 SQL,生成 前镜像(
Before Image):查询修改前的数据。 - 执行业务 SQL (
UPDATE/DELETE/INSERT)。 - 生成 后镜像(
After Image):查询修改后的数据。 - 生成
UNDO_LOG记录(前镜像+后镜像+行锁信息),并插入到本地数据库的undo_log表中(与业务SQL在同一个本地事务中)。 - 向 TC 注册分支,并获取全局锁。
- 提交本地事务(业务数据 + undo_log 同时提交)。
Commit(全局提交)或 Rollback(全局回滚)
- Commit: 如果全局事务成功,RM 收到 TC 的提交请求,Seata 会异步删除 Phase 1 生成的
UNDO_LOG和全局锁,业务数据已提交,不需再恢复。 - Rollback: 如果全局事务失败(如微服务B报错),RM 收到 TC 的回滚请求。
- 查找
UNDO_LOG记录。 - 校验后镜像与当前数据是否一致(防止脏写),如果一致,则使用前镜像执行回滚 SQL。
- 如果不一致(被其他未受 Seata 管理的程序修改了),说明发生了脏写,需要人工介入或抛出异常。
- 查找
AT 模式的优缺点
- 优点: 零业务侵入,无需编写回滚逻辑(自动补偿)。
- 缺点: 性能损耗较大(需要读写UNDO_LOG、全局锁),不适用于高并发热点数据(可能会产生全局锁等待)。
实战落地:一整套配置与代码实现
步骤 1:部署 Seata-Server (TC)
- 下载: 从 GitHub 发布页下载
seata-server-1.x.x.tar.gz。 - 配置存储模式(重要):
- 生产环境建议使用
db模式,存储在 MySQL 中(需要有global_table、branch_table、lock_table三张表)。 - 开发环境可以使用
file模式。
- 生产环境建议使用
- 配置中心/注册中心:
将 Seata-Server 注册到 Nacos(推荐),这样微服务可以通过 Nacos 发现 Seata-Server。
- 启动:
sh seata-server.sh -p 8091 -h 127.0.0.1 -m db
步骤 2:引入 Maven 依赖(微服务端)
以 Spring Boot 2.x 为例,在 pom.xml 中添加:
<!-- Spring Cloud Alibaba Seata -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<!-- 该依赖会自动引入 seata-all,无需额外引入 seata-spring-boot-starter -->
</dependency>
<!-- 注意:确保 spring-cloud-alibaba-seata 版本与你的 Spring Cloud Alibaba 版本匹配 -->
<!-- 2021.0.5.0 对应 Seata 1.6.1 -->
步骤 3:微服务端配置(application.yml)
-
配置事务分组(核心): 需要告诉微服务,你属于哪个事务分组,从而让客户端找到正确的 TC 节点。
seata: enabled: true application-id: ${spring.application.name} # 微服务名称 tx-service-group: my_test_tx_group # ⚠️ 事务分组名称,自定义 service: vgroup-mapping: my_test_tx_group: default # ⚠️ 将事务分组映射到 Seata 集群名 grouplist: default: 127.0.0.1:8091 # ⚠️ Seata Server 地址(如果没用注册中心) enable-degrade: false disable-global-transaction: false # 配置数据源代理(务必配) data-source-proxy-mode: AT -
数据源代理(关键步骤): Seata 必须接管数据源才能生成 UNDO_LOG,通常需要配置一个
DataSourceProxy的 Bean,如果你的项目使用了druid,配置如下:@Configuration public class SeataDataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.druid") // 你的数据源配置前缀 public DataSource druidDataSource() { return new DruidDataSource(); } @Bean("dataSource") // 必须叫 dataSource 或者使用 @Primary public DataSourceProxy dataSourceProxy(DataSource druidDataSource) { return new DataSourceProxy(druidDataSource); } }注意: 如果你使用了 MyBatis-Plus 的
@MapperScan,确保它扫描的SqlSessionFactory使用的是DataSourceProxy包裹后的数据源。
步骤 4:创建 UNDO_LOG 表
在每个参与事务的业务数据库中,必须创建 undo_log 表(AT 模式使用):
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, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
步骤 5:标记全局事务(业务代码)
在发起全局事务的入口方法上,加上 @GlobalTransactional 注解。
@Service
public class BusinessService {
@Autowired
private OrderFeignClient orderFeignClient; // 远程调用微服务A
@Autowired
private AccountFeignClient accountFeignClient; // 远程调用微服务B
// ⚠️ 核心:开启全局事务
@GlobalTransactional(name = "create-order-tx", rollbackFor = Exception.class)
public void createOrder(OrderDTO order) {
// 1. 调用订单服务创建订单(本地事务)
orderFeignClient.create(order);
// 2. 调用账户服务扣减余额(另一个微服务的本地事务)
accountFeignClient.debit(order.getUserId(), order.getAmount());
// 如果这里抛出异常,throw new RuntimeException("模拟异常");
// Seata 会回滚上面 1 和 2 两个微服务的操作。
}
}
建议: @GlobalTransactional 一定要加在最顶层的 Service 或 Controller 方法上,不能只加在 Feign 调用的内部方法上。
常见问题与最佳实践
-
全局锁冲突与性能
- 在 AT 模式下,如果多个全局事务操作同一行数据,第二个事务会尝试获取全局锁,如果获取不到(被第一个事务锁住了),会等待(默认重试30次,间隔10ms),这可能导致接口响应变慢。
- 解决方案: 对于高并发热点账户,建议使用 TCC 模式(Try-Confirm-Cancel)手动控制资源预留,或者使用乐观锁配合 Seata 的回滚。
-
Feign 调用的事务传递
- Seata 通过
RootContext中的XID来传递全局事务上下文。 - 如果你使用 Feign/RestTemplate,默认的 请求拦截器 (
SeataInterceptor) 会自动将xid放到请求头中。 - 注意: 如果使用了异步线程(
@Async)或消息队列(MQ),xid无法自动传递,需要手动传递(RootContext.bind(xid)和RootContext.unbind())。
- Seata 通过
-
与 MyBatis / MyBatis-Plus 集成
- Seata 对 MyBatis 基础 CRUD 的支持很好,但如果是存储过程、批量操作、多表 JOIN 更新,生成的 SQL 解析(前/后镜像)可能不准确,导致回滚失败,此时建议切换到 TCC 或 Saga 模式。
-
TCC 模式的应用场景
- 高并发热点数据(如秒杀库存):用 Try 冻结资源,Confirm 扣减或 Cancel 解冻。
- 非数据库资源(如发送短信、调用第三方支付):需要手动编写 Try、Confirm、Cancel 三个方法。
- 需要业务方自己处理幂等性、悬挂(Cancel 比 Try 先到)、空回滚(Try 没执行就 Cancel)。
构建一个可靠的分布式事务体系
- 优先使用 AT 模式:对于大部分 CRUD 业务,AT 模式代码侵入最小。
- 考虑性能/热点场景时使用 TCC:需要对业务有侵入,但性能最优。
- 长事务/阶梯性回滚使用 Saga:适合多服务、复杂编排、需要补偿操作的场景(如订单流程)。
- Seata-Server 必须高可用:部署多个 TC 节点,并使用 Nacos 或 Consul 做注册发现。
- 监控与运维:启用 Seata 的 metrics(对接 Prometheus + Grafana),监控全局事务失败率、分支事务耗时,设置合理的超时时间(
globalTransactionTimeout)。
Seata 的核心价值在于自动化,让开发者只需关注业务逻辑,把分布式事务的复杂性交给中间件处理。