在 Spring Cloud 微服务架构中,业务系统被拆分为多个独立部署的微服务,每个服务对应独立的数据库实例,原本单体应用内的本地事务,彻底演变为跨服务、跨数据源的分布式事务场景。最典型的电商下单场景:用户下单需依次执行「扣减商品库存」「扣减用户余额」「生成订单记录」三个操作,这三个操作分属库存服务、账户服务、订单服务,对应三个独立数据库,任何一个环节失败,都需要所有已执行的操作全部回滚,否则会出现数据不一致,这就是分布式事务要解决的核心问题。
分布式事务的核心目标,是保证跨服务、跨数据源的多个操作「要么全部成功,要么全部失败」,即满足事务的原子性。而 Spring Cloud 生态中,Seata(Simple Extensible Autonomous Transaction Architecture)作为阿里开源的轻量级分布式事务框架,凭借低侵入、高性能、易接入、多模式适配的特性,成为解决分布式事务的首选方案。
Seata 无缝集成 Spring Cloud、Spring Boot,提供了 AT、TCC、SAGA、XA 四种事务模式,分别适配不同的业务场景,从简单的常规业务到复杂的长事务、金融级强一致业务,都能完美覆盖。

一、前置认知:Spring Cloud 分布式事务痛点与 Seata 核心定位
1.1 Spring Cloud 分布式事务的核心痛点
Spring Cloud 微服务中,服务间通过 OpenFeign、Dubbo 等方式调用,每个服务独立管理自己的数据库,分布式事务的痛点主要集中在 3 点,也是我们选择 Seata 的核心原因:
-
数据一致性难以保证:服务间调用存在网络超时、断连等问题,可能出现"部分操作成功、部分失败"(如订单生成成功,但库存未扣减),导致数据不一致;
-
本地事务无法全局回滚:每个服务的操作都由本地事务管理,一个服务的本地事务提交后,无法被其他服务的失败操作触发回滚;
-
开发与维护成本高:手动实现分布式事务(如基于消息队列的最终一致性),需编写大量补偿代码,适配复杂场景,开发效率低、维护难度大。
1.2 Seata 核心定位与 Spring Cloud 集成优势
Seata 是一款专为微服务设计的分布式事务解决方案,核心定位是「轻量级、无侵入、多模式适配」,与 Spring Cloud 生态无缝集成,无需修改大量业务代码,就能快速实现分布式事务控制,其核心优势如下:
-
无缝集成 Spring Cloud:支持 Spring Cloud Alibaba、Spring Cloud Netflix 等主流 Spring Cloud 生态,适配 Nacos、Eureka 等注册中心,配置简单、接入成本低;
-
多模式适配:提供 AT、TCC、SAGA、XA 四种事务模式,覆盖 99% 的 Spring Cloud 分布式场景;
-
低侵入性:AT、XA 模式几乎无代码侵入,只需添加注解和简单配置,就能实现分布式事务;TCC、SAGA 模式虽需编写补偿逻辑,但有明确的规范和框架支持;
-
高性能:AT 模式基于本地事务+undo log 实现,性能接近本地事务,远超传统的 2PC 方案;
-
易运维:提供 Seata Server 作为事务协调中心,支持集群部署,可通过控制台监控事务状态、排查问题。
1.3 Seata 核心架构(Spring Cloud 集成场景)
Seata 遵循「TC、TM、RM」三大核心角色设计,这三个角色协同工作,实现分布式事务的全局协调,在 Spring Cloud 微服务中,角色分工清晰,无需手动实现协调逻辑:
-
TM(Transaction Manager,事务管理器) : - 部署位置:Spring Cloud 微服务中的「发起分布式事务的服务」(如订单服务,发起下单、扣库存、扣余额的全局事务); - 核心职责:通过
@GlobalTransactional注解开启全局事务,向 TC 注册全局事务,发起全局提交或回滚请求。 -
RM(Resource Manager,资源管理器): - 部署位置:Spring Cloud 微服务中的「参与分布式事务的所有服务」(如订单服务、库存服务、账户服务); - 核心职责:管理本地数据库资源,向 TC 注册分支事务,执行本地事务,并响应 TC 的提交/回滚指令。
-
TC(Transaction Coordinator,事务协调器): - 部署位置:独立部署的 Seata Server 服务(可集群部署,保证高可用); - 核心职责:接收 TM 的全局事务请求,协调所有 RM 的分支事务,记录事务状态,最终决定全局事务提交或回滚,是 Seata 分布式事务的"大脑"。
Spring Cloud 集成 Seata 后,事务执行流程简化为 3 步:① TM 开启全局事务,向 TC 注册;② 各 RM 执行本地事务,向 TC 注册分支事务;③ 所有分支事务执行完成后,TM 向 TC 发起提交/回滚请求,TC 协调所有 RM 完成全局提交/回滚。
1.4 Spring Cloud 集成 Seata 前置准备(通用步骤)
无论使用 Seata 的哪种事务模式,Spring Cloud 微服务集成 Seata 的前置准备步骤一致,主要包括「部署 Seata Server」「客户端配置」两步,以下是基于 Spring Cloud Alibaba + Nacos 的完整准备流程(最主流方案):
1.4.1 部署 Seata Server(事务协调器)
- 下载 Seata Server 安装包(推荐稳定版本 2.x),解压后修改配置文件
conf/application.yml,配置 Nacos 注册中心和数据存储模式(生产环境推荐 DB 模式,避免内存模式丢失数据):
XML
server:
port: 8091 # Seata Server 默认端口
spring:
application:
name: seata-server
cloud:
nacos:
discovery:
server-addr: localhost:8848 # Nacos 地址
group: SEATA_GROUP # 注册分组,默认即可
namespace: public # Nacos 命名空间
# 数据存储配置(DB模式,需提前创建seata数据库)
store:
mode: db # 存储模式:db(数据库)、file(文件,测试用)、redis(Redis)
db:
datasource: druid
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/seata?useSSL=false&serverTimezone=UTC
username: root
password: 123456
min-conn: 5
max-conn: 100
global-table: global_table # 全局事务表
branch-table: branch_table # 分支事务表
lock-table: lock_table # 锁表
distributed-lock-table: distributed_lock # 分布式锁表
-
在 MySQL 中创建 seata 数据库,并执行 Seata 官方提供的建表语句(全局事务表、分支事务表等),建表语句可在 Seata 安装包的
conf/db_undo_log.sql中获取; -
启动 Seata Server:执行解压目录下的**
bin/seata-server.sh** (Linux/Mac)或bin/seata-server.bat(Windows),启动后可在 Nacos 控制台看到 seata-server 实例。
1.4.2 Spring Cloud 客户端配置(所有参与事务的微服务)
- 引入依赖(Spring Cloud Alibaba 环境,对应 Seata 版本):
XML
<!-- Spring Cloud Alibaba Seata 依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
<!-- Seata 数据源代理依赖(AT/XA模式必需) -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
- 配置 application.yml,核心配置 Seata 注册中心、事务组、数据源代理(AT/XA 模式必需):
XML
spring:
cloud:
alibaba:
seata:
tx-service-group: my_seata_group # 事务组名称,所有参与事务的服务必须一致
service:
vgroup-mapping:
my_seata_group: default # 事务组与Seata Server集群的映射关系
grouplist:
default: localhost:8091 # Seata Server 地址(集群用逗号分隔)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=UTC
username: root
password: 123456
# Seata 配置(AT/XA模式需配置数据源代理)
seata:
data-source-proxy-mode: AT # 数据源代理模式,AT/XA模式需指定,TCC/SAGA模式无需配置
registry:
type: nacos # 注册中心类型
nacos:
server-addr: localhost:8848
group: SEATA_GROUP
namespace: public
config:
type: nacos # 配置中心类型
nacos:
server-addr: localhost:8848
group: SEATA_GROUP
namespace: public
- 配置数据源代理(AT/XA 模式必需):Seata 需要通过数据源代理拦截 SQL 执行,生成 undo log(回滚日志),因此需创建数据源代理配置类:
java
import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import javax.sql.DataSource;
@Configuration
public class SeataDataSourceConfig {
// 配置数据源代理(Seata 拦截 SQL 执行)
@Bean
public DataSource dataSourceProxy(DataSource dataSource) {
return new DataSourceProxy((DruidDataSource) dataSource);
}
// 配置事务管理器
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSourceProxy) {
return new DataSourceTransactionManager(dataSourceProxy);
}
}
前置准备完成后,即可根据业务场景,选择 Seata 的四种事务模式之一,实现分布式事务控制。以下将逐一拆解每种模式的原理、Spring Cloud 实操、优缺点及适用场景。
二、Seata AT 模式(首选!Spring Cloud 常规业务首选)
AT 模式是 Seata 最常用、最推荐的模式,也是 Spring Cloud 微服务常规业务的首选方案。它基于「本地事务+undo log 回滚日志」实现,无需编写补偿逻辑,几乎无代码侵入,性能接近本地事务,完美适配大多数常规业务场景(如电商下单、支付回调等),覆盖 80% 以上的分布式事务需求。
2.1 AT 模式核心原理(两阶段提交,自动补偿)
AT 模式采用「两阶段提交」机制,核心是"一阶段执行本地事务并记录回滚日志,二阶段根据全局事务状态,自动执行提交或回滚",全程无需手动编写补偿逻辑,Seata 自动完成回滚操作,原理拆解如下:
第一阶段(执行本地事务)
-
TM 开启全局事务(添加
@GlobalTransactional注解),向 TC 注册全局事务,获取全局事务 ID(XID); -
TM 调用其他微服务(如订单服务调用库存服务、账户服务),XID 会通过服务调用传递到各个 RM;
-
每个 RM 接收到请求后,执行本地事务(如扣减库存、扣减余额),同时 Seata 数据源代理拦截 SQL 执行,记录「undo log」(回滚日志)------ 即事务执行前的数据快照(如库存扣减前的数量、余额扣减前的金额);
-
本地事务提交,释放数据库锁(这是 AT 模式高性能的核心,避免长期锁表);
-
RM 向 TC 汇报分支事务执行状态(成功/失败)。
第二阶段(全局提交/回滚)
-
全局提交:若所有分支事务都执行成功,TC 向所有 RM 发送提交指令,RM 收到指令后,删除之前记录的 undo log(无需回滚,清理日志),全局事务完成;
-
全局回滚:若有任意一个分支事务失败,TC 向所有 RM 发送回滚指令,RM 收到指令后,根据 undo log 中的数据快照,执行回滚操作(将数据恢复到事务执行前的状态),全局事务回滚完成。
关键说明:AT 模式的回滚是「自动补偿」,无需开发者编写任何回滚代码,Seata 通过 undo log 自动完成数据恢复,这也是其低侵入性的核心原因。同时,由于一阶段本地事务已提交、释放锁,AT 模式的性能远优于传统 2PC 方案。
2.2 Spring Cloud + Seata AT 模式实操(电商下单案例)
以电商下单场景为例,涉及 3 个微服务:订单服务(TM,发起全局事务)、库存服务(RM)、账户服务(RM),实现"下单→扣库存→扣余额"的分布式事务控制,实操步骤如下:
2.2.1 业务准备
-
订单服务:创建订单表(order),提供下单接口,调用库存服务扣减库存、账户服务扣减余额;
-
库存服务:创建库存表(inventory),提供扣减库存接口;
-
账户服务:创建账户表(account),提供扣减余额接口;
-
三个服务均完成 Seata 前置配置(依赖、application.yml、数据源代理),事务组名称一致。
2.2.2 库存服务(RM)代码实现
java
// 库存 mapper
@Mapper
public interface InventoryMapper {
// 扣减库存
@Update("update inventory set stock = stock - #{num} where product_id = #{productId} and stock >= #{num}")
int deductStock(@Param("productId") Long productId, @Param("num") Integer num);
}
// 库存 service
@Service
public class InventoryService {
@Autowired
private InventoryMapper inventoryMapper;
// 扣减库存接口(RM 分支事务,无需额外注解,Seata 自动拦截)
public boolean deductStock(Long productId, Integer num) {
// 执行本地事务:扣减库存
int rows = inventoryMapper.deductStock(productId, num);
// 若影响行数为0,说明库存不足,抛出异常,触发全局回滚
if (rows == 0) {
throw new RuntimeException("库存不足");
}
return true;
}
}
2.2.3 账户服务(RM)代码实现
java
// 账户 mapper
@Mapper
public interface AccountMapper {
// 扣减余额
@Update("update account set balance = balance - #{amount} where user_id = #{userId} and balance >= #{amount}")
int deductBalance(@Param("userId") Long userId, @Param("amount") BigDecimal amount);
}
// 账户 service
@Service
public class AccountService {
@Autowired
private AccountMapper accountMapper;
// 扣减余额接口(RM 分支事务,无需额外注解)
public boolean deductBalance(Long userId, BigDecimal amount) {
int rows = accountMapper.deductBalance(userId, amount);
if (rows == 0) {
throw new RuntimeException("余额不足");
}
return true;
}
}
2.2.4 订单服务(TM)代码实现(核心,开启全局事务)
java
// 订单 mapper
@Mapper
public interface OrderMapper {
// 创建订单
@Insert("insert into `order`(user_id, product_id, amount, status) values(#{userId}, #{productId}, #{amount}, 0)")
@Options(useGeneratedKeys = true, keyProperty = "id")
void createOrder(Order order);
}
// 订单 service(TM,发起全局事务)
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private InventoryFeignClient inventoryFeignClient; // OpenFeign 调用库存服务
@Autowired
private AccountFeignClient accountFeignClient; // OpenFeign 调用账户服务
// 开启全局事务:@GlobalTransactional 注解是核心,标记该方法为全局事务入口
@GlobalTransactional(rollbackFor = Exception.class)
public void createOrder(Long userId, Long productId, Integer num, BigDecimal amount) {
try {
// 1. 本地事务:创建订单(订单服务 RM 分支事务)
Order order = new Order();
order.setUserId(userId);
order.setProductId(productId);
order.setAmount(amount);
orderMapper.createOrder(order);
// 2. 远程调用:扣减库存(库存服务 RM 分支事务)
inventoryFeignClient.deductStock(productId, num);
// 3. 远程调用:扣减余额(账户服务 RM 分支事务)
accountFeignClient.deductBalance(userId, amount);
// 4. 本地事务:更新订单状态为"已完成"
// (若前面步骤无异常,全局事务提交后,该操作生效)
} catch (Exception e) {
// 若任意一步抛出异常,触发全局回滚,所有已执行的操作(创建订单、扣库存、扣余额)都会回滚
throw new RuntimeException("下单失败,触发全局回滚", e);
}
}
}
// 订单 controller
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/create")
public Result createOrder(@RequestParam Long userId,
@RequestParam Long productId,
@RequestParam Integer num,
@RequestParam BigDecimal amount) {
try {
orderService.createOrder(userId, productId, num, amount);
return Result.success("下单成功");
} catch (Exception e) {
return Result.fail(e.getMessage());
}
}
}
2.2.5 测试验证
-
正常场景:库存充足、余额充足,三个服务的本地事务均执行成功,全局事务提交,订单创建成功、库存和余额正常扣减;
-
异常场景:库存充足,但余额不足,账户服务抛出异常,触发全局回滚,订单创建、库存扣减操作全部回滚,数据恢复到初始状态。
2.3 AT 模式优缺点
优点
-
无代码侵入:只需添加
@GlobalTransactional注解,无需编写补偿逻辑,开发成本极低; -
性能优异:一阶段本地事务提交,释放数据库锁,避免长期锁表,性能接近本地事务;
-
自动回滚:基于 undo log 自动完成回滚,无需手动干预;
-
适配广泛:支持所有关系型数据库(MySQL、Oracle 等),完美适配 Spring Cloud 常规业务。
缺点
-
仅支持关系型数据库:不支持非关系型数据库(如 Redis)、消息队列等非 DB 资源;
-
隔离级别较低:默认隔离级别为「读未提交」,若有高隔离级别需求(如读已提交、可重复读),需额外处理;
-
依赖 SQL 解析:Seata 需拦截 SQL 生成 undo log,若 SQL 过于复杂(如复杂子查询),可能解析失败。
2.4 适用场景
Spring Cloud 微服务中的常规业务场景,尤其是:① 所有操作都基于关系型数据库;② 对性能要求高;③ 不想编写补偿逻辑;④ 对隔离级别要求不高(读未提交可接受)。例如:电商下单、支付回调、订单状态更新等场景,是 Seata 最主流的使用场景。
三、Seata TCC 模式(金融级强一致,自定义补偿)
TCC 模式是 Seata 提供的「强一致性」分布式事务模式,核心是"手动定义三个操作:Try(资源预留)、Confirm(确认提交)、Cancel(取消回滚)",不依赖数据库事务,可适配非关系型数据库、消息队列等非 DB 资源,是金融级业务(如转账、支付)的首选方案,能够实现强一致性保障。
与 AT 模式的"自动补偿"不同,TCC 模式的补偿逻辑需要开发者手动编写,侵入性较高,但灵活性极强,能够满足高一致性、高定制化的业务需求。
3.1 TCC 模式核心原理(三阶段操作,手动补偿)
TCC 模式基于「Try-Confirm-Cancel」三阶段操作,核心是"先预留资源,再确认提交,失败则取消预留",全程由开发者控制,原理拆解如下:
1. Try 阶段(资源检查与预留)
所有参与分布式事务的服务,执行资源检查和预留操作,确保后续操作能够成功执行,预留的资源是"不可用"状态,避免并发问题。例如:转账场景中,Try 阶段检查余额是否充足,并将需要转账的金额冻结(预留资源),此时余额不可用。
2. Confirm 阶段(确认提交)
若所有服务的 Try 阶段都执行成功,TM 发起全局提交,各服务执行 Confirm 操作,将 Try 阶段预留的资源"确认生效",且 Confirm 操作必须是「幂等」的(多次执行结果一致)。例如:转账场景中,Confirm 阶段将冻结的金额转入目标账户,释放预留资源。
3. Cancel 阶段(取消回滚)
若有任意一个服务的 Try 阶段失败,TM 发起全局回滚,各服务执行 Cancel 操作,释放 Try 阶段预留的资源,恢复到初始状态,Cancel 操作也必须是「幂等」的。例如:转账场景中,Cancel 阶段解冻冻结的金额,恢复到转账前的余额。
关键说明:TCC 模式不依赖数据库事务,Try、Confirm、Cancel 三个操作均由开发者手动实现,因此可以适配非关系型数据库(如 Redis)、消息队列等非 DB 资源;同时,由于 Try 阶段已预留资源,Confirm/Cancel 阶段执行速度快,能够实现强一致性。
3.2 Spring Cloud + Seata TCC 模式实操(转账案例)
以金融转账场景为例,涉及 2 个微服务:转账服务(TM,发起全局事务)、账户服务(RM,包含两个账户,执行转账操作),实现"账户A转账给账户B"的分布式事务控制,实操步骤如下:
3.2.1 业务准备
-
账户服务:创建账户表(account),增加「冻结金额」字段(freeze_balance),用于 Try 阶段的资源预留;
-
转账服务:调用账户服务的 Try、Confirm、Cancel 接口,发起全局事务;
-
两个服务均完成 Seata 前置配置(无需配置数据源代理,TCC 模式不依赖 DB 事务)。
3.2.2 账户服务(RM):定义 TCC 接口(Try/Confirm/Cancel)
Seata TCC 模式要求,每个 RM 需定义 TCC 接口,通过 @TwoPhaseBusinessAction 注解标记 Try 方法,并指定 Confirm 和 Cancel 方法名称,Seata 会自动拦截调用,协调三个阶段的执行。
java
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
/**
* 账户服务 TCC 接口(核心:定义 Try/Confirm/Cancel 三阶段方法)
*/
public interface AccountTccService {
/**
* Try 阶段:资源检查与预留(冻结转账金额)
* @param actionContext 事务上下文(用于传递全局事务ID、参数等)
* @param userId 账户ID
* @param amount 转账金额
* @return 预留是否成功
*/
@TwoPhaseBusinessAction(name = "transferTcc", commitMethod = "confirm", rollbackMethod = "cancel")
boolean tryFreeze(BusinessActionContext actionContext,
@BusinessActionContextParameter(paramName = "userId") Long userId,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount);
/**
* Confirm 阶段:确认提交(扣除冻结金额,完成转账)
* @param actionContext 事务上下文
* @return 提交是否成功
*/
boolean confirm(BusinessActionContext actionContext);
/**
* Cancel 阶段:取消回滚(解冻冻结金额,恢复初始状态)
* @param actionContext 事务上下文
* @return 回滚是否成功
*/
boolean cancel(BusinessActionContext actionContext);
}
// TCC 接口实现类
@Service
public class AccountTccServiceImpl implements AccountTccService {
@Autowired
private AccountMapper accountMapper;
/**
* Try 阶段:检查余额是否充足,冻结转账金额
*/
@Override
public boolean tryFreeze(BusinessActionContext actionContext, Long userId, BigDecimal amount) {
// 1. 检查余额是否充足(可用余额 = 余额 - 冻结金额)
Account account = accountMapper.selectById(userId);
if (account == null || account.getBalance().subtract(account.getFreezeBalance()).compareTo(amount) < 0) {
return false; // 余额不足,预留失败
}
// 2. 冻结金额(预留资源):freeze_balance = freeze_balance + amount
int rows = accountMapper.freezeBalance(userId, amount);
return rows > 0;
}
/**
* Confirm 阶段:扣除冻结金额,完成转账(幂等实现:避免重复扣除)
*/
@Override
public boolean confirm(BusinessActionContext actionContext) {
// 从上下文获取参数(Try 阶段传递的参数)
Long userId = Long.parseLong(actionContext.getActionContext("userId").toString());
BigDecimal amount = new BigDecimal(actionContext.getActionContext("amount").toString());
// 扣除冻结金额:balance = balance - amount,freeze_balance = freeze_balance - amount
// 幂等处理:添加版本号或判断冻结金额是否大于等于转账金额
int rows = accountMapper.confirmFreeze(userId, amount);
return rows > 0;
}
/**
* Cancel 阶段:解冻冻结金额(幂等实现:避免重复解冻)
*/
@Override
public boolean cancel(BusinessActionContext actionContext) {
Long userId = Long.parseLong(actionContext.getActionContext("userId").toString());
BigDecimal amount = new BigDecimal(actionContext.getActionContext("amount").toString());
// 解冻金额:freeze_balance = freeze_balance - amount
int rows = accountMapper.cancelFreeze(userId, amount);
return rows > 0;
}
}
3.2.3 转账服务(TM):发起全局事务,调用 TCC 接口
java
@Service
public class TransferService {
@Autowired
private AccountFeignClient accountFeignClient; // OpenFeign 调用账户服务 TCC 接口
// 开启全局事务,调用两个账户的 Try 方法,Seata 自动协调 Confirm/Cancel
@GlobalTransactional(rollbackFor = Exception.class)
public boolean transfer(Long fromUserId, Long toUserId, BigDecimal amount) {
// 1. 调用账户服务:冻结转出账户的金额(Try 阶段)
boolean fromSuccess = accountFeignClient.tryFreeze(fromUserId, amount);
// 2. 调用账户服务:冻结转入账户的金额(Try 阶段,转入账户需预留"接收金额"的资源,实际可简化)
boolean toSuccess = accountFeignClient.tryFreeze(toUserId, amount.negate()); // 负数表示转入预留
// 若任意一个 Try 阶段失败,Seata 自动调用两个账户的 Cancel 方法,触发回滚
if (!fromSuccess || !toSuccess) {
throw new RuntimeException("转账预留资源失败,触发回滚");
}
// 所有 Try 阶段成功,Seata 自动调用两个账户的 Confirm 方法,完成全局提交
return true;
}
}
3.3 TCC 模式优缺点
优点
-
强一致性:Try 阶段预留资源,Confirm/Cancel 阶段快速执行,能够实现分布式事务的强一致性,满足 ACID 原则;
-
灵活性高:不依赖数据库事务,可适配非关系型数据库(Redis)、消息队列、第三方 API 等非 DB 资源;
-
性能较好:Try 阶段预留资源,Confirm/Cancel 阶段无锁操作,执行速度快;
-
隔离级别可自定义:开发者可根据业务需求,自定义隔离级别,避免脏读、不可重复读等问题。
缺点
-
侵入性高:需手动编写 Try、Confirm、Cancel 三个方法,开发成本高,对开发者要求高;
-
需处理幂等性:Confirm 和 Cancel 方法可能被多次调用(如网络超时重试),必须实现幂等性,否则会出现数据异常;
-
开发复杂度高:需考虑资源预留、补偿逻辑、幂等性等细节,容易出现 bugs。
3.4 适用场景
Spring Cloud 微服务中对一致性要求高、需适配非 DB 资源的场景,尤其是:① 金融级业务(转账、支付、对账);② 涉及非关系型数据库(Redis)、第三方 API(如支付接口);③ 对隔离级别有自定义需求。例如:银行转账、电商支付、跨境结算等场景。
四、Seata SAGA 模式(长事务,复杂流程,最终一致)
SAGA 模式是 Seata 提供的「长事务解决方案」,核心是"将长事务拆分为多个短事务,每个短事务执行本地事务提交,失败时通过补偿事务回滚",适用于事务流程长、涉及多个服务、无法实现强一致性的场景,最终实现数据的最终一致性,是复杂业务流程的首选方案。
与 AT、TCC 模式不同,SAGA 模式不依赖锁机制,每个短事务执行后立即提交,释放资源,适合执行时间长、涉及多个服务的复杂场景(如旅行预订、供应链管理)。
4.1 SAGA 模式核心原理(长事务拆分+补偿回滚)
SAGA 模式的核心思想是「拆分长事务」,将一个复杂的长事务,拆分为多个相互独立的短事务(每个短事务对应一个服务的操作),每个短事务执行本地事务并提交,同时为每个短事务编写对应的「补偿事务」(用于回滚),原理拆解如下:
-
拆分长事务:将复杂的业务流程,拆分为 N 个短事务(T1、T2、T3、...、Tn),每个短事务由一个微服务执行,执行完成后立即提交本地事务,释放资源;
-
执行正向流程:按顺序执行 T1、T2、T3、...、Tn,若所有短事务都执行成功,全局事务完成;
-
执行补偿流程:若某个短事务 Tk 执行失败,触发补偿流程,按逆序执行补偿事务(Ck-1、Ck-2、...、C1),每个补偿事务用于回滚对应短事务的操作,最终实现数据的最终一致性。
关键说明:SAGA 模式的每个短事务都直接提交,无需等待其他事务,因此不存在锁表问题,适合长事务场景;补偿事务由开发者手动编写,且需保证幂等性;SAGA 模式不保证隔离性,可能出现脏读、脏写,需在业务层面处理。
Seata 提供两种 SAGA 模式实现:① 简单 SAGA 模式(手动编写补偿逻辑,适用于简单长事务);② 状态机 SAGA 模式(基于状态机定义业务流程和补偿逻辑,适用于复杂长事务,支持流程编排、分支判断等),其中状态机模式是企业实战的首选。
4.2 Spring Cloud + Seata SAGA 模式实操(旅行预订案例)
以旅行预订场景为例,涉及 3 个微服务:预订服务(TM,发起长事务)、机票服务(RM)、酒店服务(RM)、景点门票服务(RM),业务流程为"预订机票→预订酒店→预订门票",若任意一步失败,需回滚前面所有已完成的预订,实操步骤如下(采用简单 SAGA 模式):
4.2.1 业务准备
-
机票服务:提供"预订机票"接口(正向事务)和"取消机票"接口(补偿事务);
-
酒店服务:提供"预订酒店"接口(正向事务)和"取消酒店"接口(补偿事务);
-
门票服务:提供"预订门票"接口(正向事务)和"取消门票"接口(补偿事务);
-
预订服务:按顺序调用三个服务的正向接口,失败时调用对应补偿接口,发起全局事务。
4.2.2 各 RM 服务:实现正向事务与补偿事务
java
// 机票服务 service(正向事务+补偿事务)
@Service
public class FlightService {
@Autowired
private FlightMapper flightMapper;
// 正向事务:预订机票
public Long bookFlight(Long userId, String flightNo, Date date) {
// 执行本地事务:创建机票预订记录
FlightOrder flightOrder = new FlightOrder();
flightOrder.setUserId(userId);
flightOrder.setFlightNo(flightNo);
flightOrder.setDate(date);
flightOrder.setStatus(1); // 1-已预订
flightMapper.insert(flightOrder);
return flightOrder.getId(); // 返回预订ID,用于补偿
}
// 补偿事务:取消机票(幂等实现)
public boolean cancelFlight(Long flightOrderId) {
// 幂等处理:判断订单状态,避免重复取消
FlightOrder flightOrder = flightMapper.selectById(flightOrderId);
if (flightOrder == null || flightOrder.getStatus() == 0) {
return true; // 已取消或不存在,视为成功
}
// 执行补偿:更新订单状态为"已取消"
flightOrder.setStatus(0);
flightMapper.updateById(flightOrder);
return true;
}
}
// 酒店服务、门票服务类似,均提供正向预订接口和补偿取消接口,此处省略...
4.2.3 预订服务(TM):发起 SAGA 长事务,协调正向与补偿流程
java
@Service
public class BookingService {
@Autowired
private FlightFeignClient flightFeignClient;
@Autowired
private HotelFeignClient hotelFeignClient;
@Autowired
private TicketFeignClient ticketFeignClient;
// 开启全局事务,Seata 协调 SAGA 流程(失败时自动触发补偿)
@GlobalTransactional(rollbackFor = Exception.class)
public boolean bookTravel(Long userId, String flightNo, Date flightDate, String hotelName, String ticketNo) {
// 存储各服务的预订ID,用于补偿
Long flightOrderId = null;
Long hotelOrderId = null;
Long ticketOrderId = null;
try {
// 1. 正向流程:预订机票(短事务1)
flightOrderId = flightFeignClient.bookFlight(userId, flightNo, flightDate);
// 2. 正向流程:预订酒店(短事务2)
hotelOrderId = hotelFeignClient.bookHotel(userId, hotelName, flightDate);
// 3. 正向流程:预订门票(短事务3)
ticketOrderId = ticketFeignClient.bookTicket(userId, ticketNo, flightDate);
// 所有短事务执行成功,全局事务完成
return true;
} catch (Exception e) {
// 执行补偿流程:按逆序取消已预订的服务
if (ticketOrderId != null) {
ticketFeignClient.cancelTicket(ticketOrderId); // 补偿3
}
if (hotelOrderId != null) {
hotelFeignClient.cancelHotel(hotelOrderId); // 补偿2
}
if (flightOrderId != null) {
flightFeignClient.cancelFlight(flightOrderId); // 补偿1
}
throw new RuntimeException("旅行预订失败,已触发补偿回滚", e);
}
}
}
4.2.4 状态机 SAGA 模式说明(企业实战推荐)
对于复杂的长事务(如包含分支判断、并发执行、子流程),推荐使用 Seata 状态机 SAGA 模式,通过可视化状态机设计器定义业务流程和补偿逻辑,无需手动编写补偿流程的调用顺序,Seata 状态机引擎会自动驱动流程执行和补偿回滚,降低开发复杂度。
核心步骤:① 通过 Seata 状态机设计器,绘制业务流程(正向流程+补偿流程),生成 JSON 配置文件;② 在 Spring Cloud 微服务中配置状态机,指定流程配置文件;③ 调用状态机接口,发起长事务,Seata 自动协调流程执行和补偿。
4.3 SAGA 模式优缺点
优点
-
适合长事务:每个短事务立即提交,无锁表问题,可处理执行时间长、涉及多个服务的复杂业务流程;
-
性能优异:无需等待其他事务,无锁操作,执行速度快,支持高并发;
-
灵活性高:可适配复杂业务流程(分支、并发、子流程),支持非 DB 资源;
-
补偿逻辑简单:补偿事务只需回滚对应短事务的操作,无需考虑全局状态。
缺点
-
仅保证最终一致性:不保证强一致性,在补偿流程执行前,可能出现数据不一致(如预订机票成功,但酒店预订失败,补偿前机票已预订);
-
无隔离性:多个 SAGA 事务同时操作同一数据,可能出现脏读、脏写,需在业务层面处理(如添加版本号、乐观锁);
-
需处理幂等性和空补偿:补偿事务可能被多次调用,需实现幂等性;同时需允许空补偿(原服务未执行,补偿服务执行),避免悬挂问题。
4.4 适用场景
Spring Cloud 微服务中长事务、复杂业务流程场景,尤其是:① 事务执行时间长(如旅行预订、供应链管理、审批流程);② 涉及多个服务,无法实现强一致性;③ 对性能要求高,不允许锁表。例如:旅行套餐预订、电商供应链履约、企业审批流程等场景。
五、Seata XA 模式(传统两阶段,强一致,兼容传统数据库)
XA 模式是 Seata 提供的「基于 XA 协议的传统两阶段提交模式」,核心是"依赖数据库原生的 XA 协议,实现分布式事务的强一致性",几乎无代码侵入,兼容所有支持 XA 协议的关系型数据库(MySQL、Oracle 等),适合传统数据库迁移到微服务的场景,或对强一致性要求高、可接受性能损耗的场景。
XA 协议是 X/Open 组织定义的分布式事务标准,大多数主流关系型数据库都原生支持,Seata XA 模式只是对 XA 协议的封装,适配 Spring Cloud 微服务架构,简化开发流程。
5.1 XA 模式核心原理(数据库原生两阶段提交)
XA 模式依赖数据库原生的 XA 协议,采用「两阶段提交」机制,核心是"一阶段准备,二阶段提交/回滚",全程由数据库和 Seata TC 协同完成,原理拆解如下:
第一阶段(准备阶段)
-
TM 开启全局事务,向 TC 注册全局事务,获取 XID;
-
各 RM 执行本地事务,但不提交,而是向数据库发送 XA prepare 指令,数据库将事务状态标记为"准备就绪",并锁定相关资源;
-
RM 向 TC 汇报分支事务状态(准备就绪/失败)。
第二阶段(提交/回滚阶段)
-
全局提交:若所有 RM 都准备就绪,TC 向所有 RM 发送 XA commit 指令,数据库执行提交操作,释放资源,全局事务完成;
-
全局回滚:若有任意一个 RM 准备失败,TC 向所有 RM 发送 XA rollback 指令,数据库执行回滚操作,释放资源,全局事务回滚完成。
关键说明:XA 模式的核心依赖数据库原生的 XA 协议,Seata 仅负责协调 TC 和 RM 的通信,无需编写补偿逻辑,几乎无代码侵入;但由于一阶段需要锁定资源,直到二阶段完成才释放,因此性能较差,适合对性能要求不高、对强一致性要求高的场景。
5.2 Spring Cloud + Seata XA 模式实操(简单订单场景)
XA 模式的实操与 AT 模式非常相似,核心区别是「数据源代理模式不同」,无需编写任何额外代码,只需修改配置,即可切换为 XA 模式,步骤如下:
5.2.1 修改客户端配置(所有参与事务的微服务)
将 Seata 数据源代理模式改为 XA,其余配置与 AT 模式一致:
bash
seata:
data-source-proxy-mode: XA # 改为 XA 模式(AT 模式为 AT)
registry:
type: nacos
nacos:
server-addr: localhost:8848
group: SEATA_GROUP
namespace: public
config:
type: nacos
nacos:
server-addr: localhost:8848
group: SEATA_GROUP
namespace: public
tx-service-group: my_seata_group # 需与 Seata Server 事务组一致
spring:
cloud:
alibaba:
seata:
tx-service-group: my_seata_group
service:
vgroup-mapping:
my_seata_group: default
grouplist:
default: localhost:8091
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/order_db?useSSL=false&serverTimezone=UTC
username: root
password: 123456
关键说明:XA 模式同样需要数据源代理,因此无需删除 AT 模式的数据源代理配置类(SeataDataSourceConfig),仅修改 data-source-proxy-mode 即可,Seata 会自动适配 XA 模式的数据源代理逻辑。
5.2.2 业务代码实现(与 AT 模式完全一致)
XA 模式的业务代码与 AT 模式完全相同,无需修改任何业务逻辑,仅需保证 TM 服务添加 @GlobalTransactional注解,RM 服务正常提供业务接口即可。以下以订单服务(TM)、库存服务(RM)为例,简化展示核心代码(与 AT 模式复用,无需额外开发):
库存服务(RM)核心代码
java
// 库存 mapper(与 AT 模式一致)
@Mapper
public interface InventoryMapper {
@Update("update inventory set stock = stock - #{num} where product_id = #{productId} and stock >= #{num}")
int deductStock(@Param("productId") Long productId, @Param("num") Integer num);
}
// 库存 service(无需额外注解,Seata 自动拦截 XA 事务)
@Service
public class InventoryService {
@Autowired
private InventoryMapper inventoryMapper;
public boolean deductStock(Long productId, Integer num) {
int rows = inventoryMapper.deductStock(productId, num);
if (rows == 0) {
throw new RuntimeException("库存不足");
}
return true;
}
}
订单服务(TM)核心代码
java
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private InventoryFeignClient inventoryFeignClient;
@Autowired
private AccountFeignClient accountFeignClient;
// 全局事务注解,与 AT 模式一致,Seata 自动适配 XA 模式
@GlobalTransactional(rollbackFor = Exception.class)
public void createOrder(Long userId, Long productId, Integer num, BigDecimal amount) {
try {
// 1. 本地事务:创建订单
Order order = new Order();
order.setUserId(userId);
order.setProductId(productId);
order.setAmount(amount);
orderMapper.createOrder(order);
// 2. 远程调用:扣减库存
inventoryFeignClient.deductStock(productId, num);
// 3. 远程调用:扣减余额
accountFeignClient.deductBalance(userId, amount);
} catch (Exception e) {
throw new RuntimeException("下单失败,触发全局回滚", e);
}
}
}
5.2.3 测试验证(与 AT 模式逻辑一致)
-
正常场景:库存充足、余额充足,所有 RM 执行本地事务并完成 XA 准备,TC 发起全局提交,所有数据库执行提交操作,数据一致;
-
异常场景:任意一个 RM 执行失败(如余额不足),该 RM 向 TC 汇报准备失败,TC 向所有 RM 发送回滚指令,所有数据库执行回滚,数据恢复初始状态;
-
特殊验证:测试过程中可中断 Seata Server 或网络,观察数据库状态------此时 RM 处于"准备就绪"状态,资源被锁定,直至 Seata Server 恢复后,TC 重新协调提交或回滚,验证 XA 模式的强一致性。
5.3 XA 模式优缺点
优点
-
几乎无代码侵入:与 AT 模式一致,只需修改配置,无需编写补偿逻辑,开发成本低,适配 Spring Cloud 快速集成需求;
-
强一致性:依赖数据库原生 XA 协议,严格遵循两阶段提交,保证分布式事务的强一致性,满足 ACID 原则;
-
兼容性强:兼容所有支持 XA 协议的关系型数据库(MySQL、Oracle、SQL Server 等),适合传统数据库迁移到微服务的场景;
-
实现简单:Seata 封装了 XA 协议的协调逻辑,无需开发者手动处理两阶段提交的细节,运维成本低。
缺点
-
性能较差:一阶段需锁定数据库资源,直至二阶段提交/回滚后才释放,容易出现锁表、阻塞问题,并发量高的场景下性能损耗明显;
-
仅支持关系型数据库:与 AT 模式一样,不支持非关系型数据库(Redis)、消息队列等非 DB 资源;
-
数据库依赖:依赖数据库对 XA 协议的支持,部分轻量级数据库(如 SQLite)不支持 XA 协议,无法使用;
-
无降级方案:若数据库出现异常,XA 事务无法降级,可能导致整个分布式事务阻塞,影响业务可用性。
5.4 适用场景
Spring Cloud 微服务中对强一致性要求高、性能要求不高,且基于传统关系型数据库的场景,尤其是:① 传统单体应用迁移到微服务,原有业务基于 XA 事务;② 对数据一致性要求极高(如金融对账、账务统计),可接受性能损耗;③ 业务场景简单,并发量低,无锁表风险。例如:传统金融系统的账务处理、企业内部的财务统计等场景。
六、Seata 四大模式对比总结与生产环境选型建议
Seata 的 AT、TCC、SAGA、XA 四种模式,分别适配不同的业务场景,无绝对优劣之分,核心是"贴合业务需求选择"。以下通过对比表格清晰呈现四大模式的核心差异,同时给出生产环境的选型指南,帮助开发者快速决策。
6.1 四大模式核心对比
| 对比维度 | AT 模式 | TCC 模式 | SAGA 模式 | XA 模式 |
|---|---|---|---|---|
| 一致性 | 最终一致(默认),可优化为强一致 | 强一致 | 最终一致 | 强一致 |
| 代码侵入性 | 极低(仅需 @GlobalTransactional) | 高(手动编写 Try/Confirm/Cancel) | 中(手动编写补偿事务) | 极低(仅修改配置) |
| 性能 | 优异(接近本地事务) | 较好(无锁操作) | 优异(无锁,短事务提交) | 较差(锁表阻塞) |
| 支持资源 | 仅关系型数据库 | 所有资源(DB、Redis、第三方 API) | 所有资源(DB、Redis、第三方 API) | 仅关系型数据库 |
| 隔离级别 | 默认读未提交(可优化) | 自定义(开发者控制) | 无(需业务层面处理) | 依赖数据库(通常为可重复读) |
| 幂等性要求 | 低(Seata 自动处理) | 高(Confirm/Cancel 需手动实现) | 高(补偿事务需手动实现) | 低(数据库自动处理) |
| 适用场景 | 常规业务、电商下单、支付回调 | 金融级业务、转账、支付对账 | 长事务、复杂流程、旅行预订 | 传统数据库迁移、高一致性低并发 |
6.2 生产环境选型指南
选型核心原则:优先匹配业务场景,平衡一致性、性能、开发成本,具体建议如下:
-
首选 AT 模式:若业务是 Spring Cloud 常规微服务场景,所有操作基于关系型数据库,对性能要求高、不想编写补偿逻辑,优先选择 AT 模式(覆盖 80% 以上的生产场景);
-
选择 TCC 模式:若业务是金融级场景(转账、支付),对强一致性要求极高,涉及非关系型数据库或第三方 API,选择 TCC 模式,需重点关注幂等性和补偿逻辑的编写;
-
选择 SAGA 模式:若业务是长事务、复杂流程(如旅行预订、供应链履约),执行时间长、涉及多个服务,无法实现强一致性,选择 SAGA 模式,推荐使用状态机模式降低开发复杂度;
-
选择 XA 模式:若业务是传统单体应用迁移到微服务,原有系统基于 XA 事务,或对强一致性要求极高、可接受性能损耗,选择 XA 模式,避免大量修改原有代码。
6.3 生产环境注意事项
-
Seata Server 高可用:生产环境必须集群部署 Seata Server,避免单点故障,同时配置 DB 模式存储事务状态,防止数据丢失;
-
事务组配置一致:所有参与分布式事务的微服务,事务组名称(tx-service-group)必须一致,否则无法被 TC 协调;
-
幂等性处理:TCC、SAGA 模式必须实现幂等性,避免网络超时重试导致的数据异常;
-
性能优化:AT 模式可优化 SQL 解析效率,避免复杂子查询;XA 模式需控制并发量,减少锁表风险;
-
监控与排查:启用 Seata 控制台,实时监控事务状态,针对失败事务,通过 undo log、事务日志排查问题。
七、总结
Seata 作为 Spring Cloud 生态中最主流的分布式事务解决方案,通过 AT、TCC、SAGA、XA 四种模式,覆盖了从常规业务到金融级业务、从短事务到长事务的所有场景。本文从核心原理、Spring Cloud 实操、优缺点、适用场景四个维度,完整拆解了四种模式的核心知识点,同时给出了生产环境选型建议,助力开发者快速上手 Seata,灵活解决微服务分布式事务难题。