文章目录
-
- 一、开篇:第29课留下的"一致性缺口"
- 二、分布式事务理论基础
-
- [2.1 CAP理论:分布式系统的"不可能三角"](#2.1 CAP理论:分布式系统的“不可能三角”)
- [2.2 BASE理论:AP路线的落地指南](#2.2 BASE理论:AP路线的落地指南)
- 三、Seata架构深度剖析
-
- [3.1 三大核心组件:TC、TM、RM](#3.1 三大核心组件:TC、TM、RM)
- [3.2 XID的传递机制](#3.2 XID的传递机制)
- 四、AT模式深度剖析:两阶段提交的"零侵入"实现
-
- [4.1 AT模式的整体机制](#4.1 AT模式的整体机制)
- [4.2 数据源代理技术](#4.2 数据源代理技术)
- [4.3 全局锁与隔离性](#4.3 全局锁与隔离性)
- [五、Seata Server部署实战](#五、Seata Server部署实战)
-
- [5.1 Seata 2.5.0新特性](#5.1 Seata 2.5.0新特性)
- [5.2 存储模式选择](#5.2 存储模式选择)
- [5.3 数据库初始化](#5.3 数据库初始化)
- [5.4 Seata Server配置](#5.4 Seata Server配置)
- [5.5 启动Seata Server](#5.5 启动Seata Server)
- 六、AT模式完整实战:订单扣库存
-
- [6.1 依赖配置](#6.1 依赖配置)
- [6.2 应用配置](#6.2 应用配置)
- [6.3 订单服务:发起全局事务](#6.3 订单服务:发起全局事务)
- [6.4 库存服务:分支事务](#6.4 库存服务:分支事务)
- [6.5 验证分布式事务](#6.5 验证分布式事务)
- 七、AT模式与其他模式的对比
-
- [7.1 四种模式全景对比](#7.1 四种模式全景对比)
- [7.2 模式选型决策](#7.2 模式选型决策)
- 八、生产级性能调优与踩坑方案
-
- [8.1 全局锁与本地锁的死锁问题](#8.1 全局锁与本地锁的死锁问题)
- [8.2 Feign超时与Seata事务超时的配合](#8.2 Feign超时与Seata事务超时的配合)
- [8.3 空回滚与悬挂问题](#8.3 空回滚与悬挂问题)
- [8.4 性能优化参数](#8.4 性能优化参数)
- [8.5 生产环境监控指标](#8.5 生产环境监控指标)
- 九、踩坑指南
-
- 坑一:undo_log表不存在导致事务失败
- 坑二:Gateway链路中XID丢失
- 坑三:@GlobalTransactional注解不生效
- 坑四:Feign重试导致分支事务重复注册
- 坑五:生产环境使用file存储模式
- 坑六:AT模式读到脏数据
- [坑七:Seata 2.5.0启动失败------未初始化账户](#坑七:Seata 2.5.0启动失败——未初始化账户)
- 十、课后作业
- 十一、阶段总结与下节预告
- [🔗《最新版 SpringCloud 2025 从入门到实战》系列课程导航](#🔗《最新版 SpringCloud 2025 从入门到实战》系列课程导航)

适配版本 :Seata 2.5.0、Spring Cloud Alibaba 2025.1.0.0、Spring Cloud 2025.1.3(Oakwood)、Spring Boot 4.0.8、Nacos Server 3.1.1、JDK 21
课程定位:微服务高阶阶段核心课,从分布式事务理论到Seata AT模式实战,构建跨服务数据一致性保障能力
一、开篇:第29课留下的"一致性缺口"
第29课我们完成了微服务异步调用、线程池优化和事件驱动开发。通过@Async和Spring事件,我们将非核心流程从主流程中剥离,大幅提升了接口响应速度。但异步也带来了一个新的问题:数据一致性。
考虑一个场景:订单服务创建订单后,异步发送消息通知库存服务扣减库存。如果订单创建成功,但异步消息发送失败,库存没有被扣减,就会出现"订单已创建但库存未扣减"的数据不一致。更严重的是,@Async方法的事务与主方法的事务是独立的------主方法的事务回滚了,异步方法的事务可能已经提交,数据就"飘"了。
在第16课我们讨论过Feign调用的一致性,但当时的方案(本地消息表、Seata AT模式简介)只是浅尝辄止。现在必须正面回答一个核心问题:当一次业务操作需要跨多个服务、多个数据库时,如何保证数据要么全部成功,要么全部回滚?
这就是分布式事务 要解决的问题。Seata是Spring Cloud Alibaba生态中分布式事务的事实标准方案,它提供了AT、TCC、SAGA、XA四种模式,其中AT模式以零侵入 著称------只需要一个@GlobalTransactional注解,就能将本地事务升级为分布式事务。Spring Cloud Alibaba 2025.1.0.0正式适配了Seata 2.5.0,并新增了对WebFlux的支持。
本课将从CAP/BASE理论讲起,深入Seata的三组件架构(TC/TM/RM)和AT模式的两阶段提交原理,完成Seata Server部署和订单扣库存的完整实战,最后给出生产环境的性能调优和踩坑方案。
二、分布式事务理论基础
2.1 CAP理论:分布式系统的"不可能三角"
CAP理论指出,一个分布式系统无法同时满足以下三个特性:
| 特性 | 含义 | 取舍 |
|---|---|---|
| Consistency(一致性) | 所有节点在同一时刻看到相同的数据 | 与A冲突 |
| Availability(可用性) | 每个请求都能在有限时间内得到响应 | 与C冲突 |
| Partition Tolerance(分区容错性) | 系统在节点间通信中断时仍能继续运行 | 分布式系统必须满足 |
核心结论 :由于网络分区(P)在分布式系统中不可避免 ,系统实际上只能在一致性(CP) 和可用性(AP) 之间二选一。分布式事务框架的选择本质上是在这个权衡中做出决策。
- CP型方案(如XA、2PC):强一致性优先,但协调者宕机时参与者会阻塞,可用性降低
- AP型方案(如AT、TCC、SAGA、本地消息表):最终一致性优先,性能更好,但存在短暂的数据不一致窗口
2.2 BASE理论:AP路线的落地指南
BASE理论是对CAP中AP方案的延伸,包含三个核心要素:
| 要素 | 含义 | 在微服务中的体现 |
|---|---|---|
| Basically Available(基本可用) | 允许损失部分可用性,保证核心功能可用 | 大促期间降级非核心功能 |
| Soft State(软状态) | 允许系统存在中间状态,不影响整体可用性 | 订单"支付中"状态 |
| Eventually Consistent(最终一致) | 经过一段时间后,所有节点数据最终一致 | 异步补偿、定期对账 |
BASE理论的核心思想 :放弃强一致性,通过补偿机制在事后达成一致。Seata的AT模式、TCC模式、SAGA模式都遵循BASE理论,属于最终一致性方案。XA模式则属于CP路线。
三、Seata架构深度剖析
3.1 三大核心组件:TC、TM、RM
Seata的全称是 Simple Extensible Autonomous Transaction Architecture,它通过三个核心组件协同工作:
| 组件 | 全称 | 职责 | 部署位置 |
|---|---|---|---|
| TC | Transaction Coordinator | 事务协调者,维护全局事务和分支事务的状态,驱动全局提交或回滚 | 独立部署的Seata Server |
| TM | Transaction Manager | 事务管理器,定义全局事务的范围,发起全局提交或回滚 | 业务应用(发起方) |
| RM | Resource Manager | 资源管理器,管理分支事务处理的资源,向TC注册分支事务 | 业务应用(参与方) |
一次全局事务的完整生命周期:
TM 向 TC 申请开启全局事务
│ TC 生成全局唯一 XID,返回给 TM
▼
TM 将 XID 绑定到当前线程上下文
│ 通过 Feign / RPC 调用时将 XID 透传到下游服务
▼
RM 拦截业务 SQL,向 TC 注册分支事务
│ 获取 Branch ID
│ 执行 SQL,记录 undo_log,提交本地事务
▼
所有分支事务执行完毕,TM 向 TC 发起全局提交 / 回滚
│
▼
TC 驱动所有 RM 完成 Phase 2 操作
│ 提交 → 异步删除 undo_log
│ 回滚 → 根据 undo_log 反向补偿
3.2 XID的传递机制
XID是全局事务的唯一标识,格式为{ip}:{port}:{transactionId},例如192.168.1.100:8091:1234567890。XID的传递是链路串联的核心:
| 传递场景 | 传递方式 | 是否需要手动处理 |
|---|---|---|
| Spring Cloud / OpenFeign | 通过HTTP Header自动透传 | ✅ 自动 |
| Dubbo | 通过RPC Context自动透传 | ✅ 自动 |
| 消息队列(RocketMQ/Kafka) | 需手动在消息体中绑定XID | ⚠️ 手动 |
| 自定义线程池 | 需手动RootContext.bind(xid) |
⚠️ 手动 |
| Spring Cloud Gateway | 需添加XID到白名单,避免被过滤 |
⚠️ 手动 |
Spring Cloud Alibaba 2025.1.0.0新增了对WebFlux的支持,这意味着Gateway(基于WebFlux)也能正确透传XID,之前版本中Gateway链路断裂的问题得到了解决。
四、AT模式深度剖析:两阶段提交的"零侵入"实现
4.1 AT模式的整体机制
AT(Automatic Transaction)模式是Seata默认 的分布式事务模式,也是Spring Cloud Alibaba生态中使用最广的方案。它的核心创新在于:通过数据源代理自动拦截SQL,在不修改业务代码的前提下实现分布式事务。
AT模式采用改进的两阶段提交(2PC) 模型:
阶段一(Phase 1):执行并记录
① 拦截业务 SQL,解析 SQL 语义(SQL类型、表名、条件)
② 查询数据前镜像(Before Image)------ SELECT ... FOR UPDATE
③ 执行业务 SQL(UPDATE / INSERT / DELETE)
④ 查询数据后镜像(After Image)
⑤ 保存 undo_log 回滚日志(记录前后镜像)
⑥ 提交本地事务(立即释放本地锁和连接资源)
⑦ 向 TC 汇报执行结果
关键设计 :业务数据和undo_log在同一个本地事务中提交,保证了"要么都成功,要么都失败"。
阶段二(Phase 2):提交或回滚
成功提交:
TC 异步通知 RM 删除 undo_log(无需同步等待,性能极高)
失败回滚:
TC 通知 RM 根据 undo_log 中的前镜像恢复数据
回滚完成后删除 undo_log
4.2 数据源代理技术
AT模式的"零侵入"依赖于数据源代理(DataSourceProxy) 。Seata通过代理真实数据源,在SQL执行层插入事务逻辑:
| 代理层级 | 代理对象 | 拦截的调用 |
|---|---|---|
| DataSourceProxy | 数据源代理 | getConnection() |
| ConnectionProxy | 连接代理 | prepareStatement()、commit() |
| StatementProxy | 语句代理 | executeXXX() |
自动注册流程:
业务代码执行 SQL → mapper.insert()
│
▼
DataSourceProxy 拦截 → 检查 RootContext 中是否有 XID
│
├─ 有 XID → 向 TC 注册分支事务,获取 Branch ID
│
└─ 无 XID → 普通本地事务,直接执行
│
▼
查询前镜像 → 执行 SQL → 查询后镜像 → 保存 undo_log → 提交本地事务
4.3 全局锁与隔离性
AT模式的默认全局隔离级别是读未提交(Read Uncommitted) 。这意味着在全局事务提交之前,其他事务可能看到未提交的数据修改。
为什么不做读已提交? 因为AT模式Phase 1就提交了本地事务,如果要求全局读已提交,所有读操作都需要加全局锁,性能会急剧下降。
需要读已提交时的解决方案 :在查询SQL上使用SELECT ... FOR UPDATE。Seata通过SelectForUpdateExecutor代理该语句,在获取数据库本地锁后,再检查全局锁是否存在。如果全局锁被其他事务持有,则回滚本地事务并重试,直到获取全局锁。
全局锁的工作机制:
| 操作 | 全局锁行为 |
|---|---|
| 写操作 | Phase 1 注册分支事务时获取全局锁;Phase 2 完成后释放 |
| 读操作(SELECT) | 默认不加全局锁,可能读到未提交数据 |
| 读操作(SELECT FOR UPDATE) | 代理后先检查全局锁,若存在则等待或重试 |
五、Seata Server部署实战
5.1 Seata 2.5.0新特性
Seata 2.5.0于2025年7月21日发布,关键变更包括:
| 特性 | 说明 |
|---|---|
| 强制账户初始化 | 禁止使用默认凭证,提升安全性 |
| HTTP/2支持 | Seata Server支持HTTP/2协议,通信性能提升 |
| OceanBase Oracle支持 | 新增对OceanBase Oracle模式的支持 |
| Tomcat替换为Netty-HTTP | 服务端Web容器从Tomcat切换为Netty,降低资源消耗 |
5.2 存储模式选择
Seata Server支持三种事务日志存储模式:
| 模式 | 说明 | 适用场景 |
|---|---|---|
| file | 文件存储,事务日志写入本地文件 | 开发/测试环境 |
| db | 数据库存储,事务日志持久化到MySQL等数据库 | 生产环境推荐 |
| redis | Redis存储,事务日志持久化到Redis | 高性能场景 |
生产环境必须使用db或redis模式,file模式在服务重启后事务日志会丢失,导致无法恢复未完成的事务。
5.3 数据库初始化
使用db模式需要先创建Seata的数据库表:
sql
-- 全局事务表
CREATE TABLE global_table (
xid VARCHAR(128) NOT NULL,
transaction_id BIGINT,
status TINYINT,
application_id VARCHAR(32),
transaction_service_group VARCHAR(32),
transaction_name VARCHAR(128),
timeout INT,
begin_time BIGINT,
application_data VARCHAR(2000),
gmt_create DATETIME,
gmt_modified DATETIME,
PRIMARY KEY (xid),
KEY idx_gmt_modified_status (gmt_modified, status),
KEY idx_transaction_id (transaction_id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- 分支事务表
CREATE TABLE branch_table (
branch_id BIGINT NOT NULL,
xid VARCHAR(128) NOT NULL,
transaction_id BIGINT,
resource_group_id VARCHAR(32),
resource_id VARCHAR(256),
branch_type VARCHAR(8),
status TINYINT,
client_id VARCHAR(64),
application_data VARCHAR(2000),
gmt_create DATETIME(6),
gmt_modified DATETIME(6),
PRIMARY KEY (branch_id),
KEY idx_xid (xid)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
-- 全局锁表
CREATE TABLE lock_table (
row_key VARCHAR(128) NOT NULL,
xid VARCHAR(128),
transaction_id BIGINT,
branch_id BIGINT,
resource_id VARCHAR(256),
table_name VARCHAR(32),
pk VARCHAR(36),
gmt_create DATETIME,
gmt_modified DATETIME,
PRIMARY KEY (row_key),
KEY idx_branch_id (branch_id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
业务库需要额外创建undo_log表(每个参与分布式事务的业务数据库都需要):
sql
CREATE TABLE undo_log (
branch_id BIGINT NOT NULL,
xid VARCHAR(128) NOT NULL,
context VARCHAR(128) NOT NULL,
rollback_info LONGBLOB NOT NULL,
log_status INT NOT NULL,
log_created DATETIME NOT NULL,
log_modified DATETIME NOT NULL,
UNIQUE KEY ux_undo_log (xid, branch_id)
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
5.4 Seata Server配置
编辑seata-server/conf/application.yml:
yaml
server:
port: 7091 # 控制台端口
seata:
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: seata
group: SEATA_GROUP
data-id: seataServer.properties
registry:
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
namespace: seata
group: SEATA_GROUP
store:
mode: db
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seata?useSSL=false&serverTimezone=UTC
user: seata
password: Seata@2026!
min-conn: 5
max-conn: 100
security:
secret-key: SeataSecretKey0c382ef121d778043159209298fd40bf3850a017
token-validity-in-seconds: 1800
Seata 2.5.0的安全变更 :2.5.0强制要求初始化账户并禁用默认凭证。必须在application.yml中配置security.secret-key,否则启动会失败。
5.5 启动Seata Server
bash
# Linux/macOS
sh bin/seata-server.sh -p 8091 -m db
# Windows
bin\seata-server.bat -p 8091 -m db
启动后访问http://localhost:7091,使用初始化时设置的账户登录控制台。
六、AT模式完整实战:订单扣库存
6.1 依赖配置
在service-order、service-product(库存服务)的POM中添加Seata依赖:
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
版本由Spring Cloud Alibaba BOM(2025.1.0.0)统一管理,对应Seata 2.5.0。
6.2 应用配置
在两个服务的application.yml中添加:
yaml
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: default
grouplist:
default: 127.0.0.1:8091
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: seata
group: SEATA_GROUP
application: seata-server
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: seata
group: SEATA_GROUP
data-id: seataServer.properties
enable-auto-data-source-proxy: true # 自动代理数据源
data-source-proxy-mode: AT # 使用AT模式
enable-auto-data-source-proxy: true 是AT模式的核心配置。开启后,Seata会自动将Spring管理的DataSource包装为DataSourceProxy,业务代码无需任何修改。
6.3 订单服务:发起全局事务
java
@Service
@Slf4j
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private ProductFeignClient productFeignClient;
@GlobalTransactional(name = "create-order", timeoutMills = 30000)
public OrderVO createOrder(OrderCreateRequest request) {
log.info("开始创建订单: productId={}, quantity={}",
request.getProductId(), request.getQuantity());
// 1. 本地事务:创建订单
Order order = new Order();
order.setOrderNo(IdUtils.generateOrderNo());
order.setUserId(request.getUserId());
order.setProductId(request.getProductId());
order.setQuantity(request.getQuantity());
order.setStatus(OrderStatus.CREATED);
orderMapper.insert(order);
// 2. 远程调用:扣减库存(通过Feign,XID自动透传)
Result<Void> stockResult = productFeignClient.decreaseStock(
new StockDecreaseRequest(request.getProductId(), request.getQuantity()));
if (!stockResult.isSuccess()) {
throw new BusinessException("库存不足");
}
// 3. 更新订单状态
orderMapper.updateStatus(order.getOrderNo(), OrderStatus.CONFIRMED);
log.info("订单创建成功: orderNo={}", order.getOrderNo());
return OrderVO.from(order);
}
}
@GlobalTransactional的关键参数:
| 参数 | 含义 | 建议值 |
|---|---|---|
name |
全局事务名称,用于监控和日志 | 业务含义明确的名称 |
timeoutMills |
全局事务超时时间(毫秒) | 30000(30秒) |
rollbackFor |
触发回滚的异常类型 | Throwable.class |
6.4 库存服务:分支事务
java
@Service
@Slf4j
public class StockService {
@Autowired
private StockMapper stockMapper;
public void decreaseStock(Long productId, Integer quantity) {
log.info("扣减库存: productId={}, quantity={}", productId, quantity);
// 查询当前库存
Stock stock = stockMapper.selectByProductId(productId);
if (stock == null || stock.getCount() < quantity) {
throw new BusinessException("库存不足");
}
// 扣减库存(Seata自动拦截SQL,记录undo_log)
int affected = stockMapper.decreaseStock(productId, quantity);
if (affected == 0) {
throw new BusinessException("库存扣减失败");
}
log.info("库存扣减成功: productId={}", productId);
}
}
关键理解 :库存服务的decreaseStock方法不需要 添加@GlobalTransactional。Seata的数据源代理会自动拦截SQL,从RootContext中获取XID,自动注册分支事务。
6.5 验证分布式事务
场景一:正常下单(全部成功)
bash
curl -X POST http://localhost:8082/api/order/create \
-H "Content-Type: application/json" \
-d '{"userId":1,"productId":100,"quantity":2}'
# 预期:订单创建成功,库存扣减成功
场景二:库存不足(全局回滚)
bash
curl -X POST http://localhost:8082/api/order/create \
-H "Content-Type: application/json" \
-d '{"userId":1,"productId":999,"quantity":1000}'
# 预期:返回"库存不足",订单记录被回滚删除,库存未变化
验证回滚 :查询订单表和库存表,确认订单记录已被删除,库存数量未变化。查看业务库的undo_log表,应能看到回滚记录。
七、AT模式与其他模式的对比
7.1 四种模式全景对比
Seata支持四种分布式事务模式,各有适用场景:
| 维度 | AT | TCC | SAGA | XA |
|---|---|---|---|---|
| 一致性 | 最终一致 | 最终一致 | 最终一致 | 强一致 |
| 业务侵入 | 零侵入 | 高侵入 | 高侵入 | 零侵入 |
| 隔离性 | 读未提交(可配置) | 取决于实现 | 无隔离 | 读已提交 |
| 性能 | 高 | 最高 | 高 | 低 |
| 适用场景 | 通用CRUD | 复杂定制化业务 | 长事务 | 强一致要求场景 |
| 实现难度 | 低 | 高 | 中 | 低 |
| 补偿方式 | undo_log自动反向补偿 | 手写Cancel方法 | 状态机补偿 | 数据库回滚 |
7.2 模式选型决策
| 场景 | 推荐模式 | 原因 |
|---|---|---|
| 通用CRUD(订单、库存、账户) | AT | 零侵入,开箱即用 |
| 复杂业务(需要精细控制补偿逻辑) | TCC | 可自定义Try/Confirm/Cancel |
| 长事务(涉及多个系统、长时间流程) | SAGA | 状态机驱动,适合编排复杂流程 |
| 强一致要求(金融核心账务) | XA | 唯一强一致方案 |
| 性能敏感(高并发交易) | TCC | 无全局锁,性能最高 |
选型建议 :90%的场景使用AT模式即可。AT模式在CRUD场景下性能足够、零侵入、维护成本低。只有在AT模式遇到明确的性能瓶颈或业务逻辑复杂度超出AT模式能力时,才考虑切换到TCC或SAGA。
八、生产级性能调优与踩坑方案
8.1 全局锁与本地锁的死锁问题
AT模式最常见的生产问题:Lock wait timeout exceeded异常。根本原因是全局锁(Global Lock)与本地锁(Local Lock)的加锁顺序不当,形成死锁环。
死锁典型场景:
事务1:持有 t_order 的 RECORD LOCK,等待 Seata global_lock
事务2:持有 Seata global_lock,等待 t_order 的 RECORD LOCK
根本原因 :@Transactional(本地事务)和@GlobalTransactional(全局事务)的嵌套使用。如果Feign远程调用被包裹在Spring的@Transactional中,本地数据库连接会先开启隐式锁,然后才去获取全局锁,形成了"先拿本地锁、再等全局锁"的顺序。
解决方案:
方案一:调整本地事务传播属性。 将内部本地事务的传播级别改为Propagation.REQUIRES_NEW,让本地锁在子事务中快速释放:
java
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void createOrderLocal(Order order) {
orderMapper.insert(order);
}
方案二:减少事务粒度(推荐)。 不要将Feign远程调用包裹在Spring事务中:
java
@Service
public class OrderService {
@GlobalTransactional(name = "create-order", timeoutMills = 30000)
public void createOrder(OrderDTO dto) {
// 1. 本地事务单独控制(使用编程式事务或脱离大事务)
orderMapper.insert(order);
// 2. 远程调用(剥离出Spring事务上下文)
productFeignClient.decreaseStock(dto.getProductId(), dto.getQuantity());
}
}
调优效果:调整后锁等待超时异常可降低98%,RT从2.3秒降至650ms。
8.2 Feign超时与Seata事务超时的配合
Seata的全局事务超时时间(timeoutMills)和Feign的超时时间需要协调配置:
| 超时类型 | 建议值 | 说明 |
|---|---|---|
| Feign连接超时 | 3秒 | 快速失败 |
| Feign读取超时 | 5秒 | 与分支事务超时同步 |
| Seata全局事务超时 | 30秒 | 大于所有分支超时之和 |
关键规则 :Feign读取超时应小于Seata全局事务超时。如果Feign超时了,Seata会触发回滚;如果Seata超时了但Feign还在等待,就会导致分支事务状态不一致。
禁用Feign重试 :Seata参与的服务必须禁用Feign重试 (Retryer.NEVER_RETRY),否则重试会导致分支事务重复注册。
8.3 空回滚与悬挂问题
空回滚:TC通知RM回滚,但RM的Phase 1根本没有执行(因网络问题未收到请求),回滚操作被"空执行"。
悬挂:空回滚发生后,Phase 1的请求才到达RM并执行成功,导致数据被修改但全局事务已经回滚,数据无法恢复。
AT模式的防护 :AT模式通过undo_log的xid + branch_id唯一约束来防止重复回滚,但对于"先回滚后执行"的悬挂场景,需要额外的防护机制(TCC模式中需要业务代码手动处理)。
生产建议 :确保undo_log表的(xid, branch_id)唯一索引存在;监控undo_log表中长期未删除的记录(可能表示悬挂事务)。
8.4 性能优化参数
| 参数 | 默认值 | 生产建议 | 说明 |
|---|---|---|---|
client.rm.report.success.enable |
true | false | 关闭成功上报,减少TC压力(失败时才上报) |
client.tm.commit.retry.count |
5 | 3 | 全局提交重试次数 |
client.tm.rollback.retry.count |
5 | 3 | 全局回滚重试次数 |
transport.threadFactory.bossThreadSize |
1 | 1 | Netty Boss线程数 |
transport.threadFactory.workerThreadSize |
8 | CPU核数×2 | Netty Worker线程数 |
8.5 生产环境监控指标
通过Seata控制台或Prometheus监控以下指标:
| 指标 | 含义 | 告警阈值 |
|---|---|---|
seata_transaction_total |
全局事务总数 | 突增50%告警 |
seata_transaction_committed_total |
已提交事务数 | 用于计算提交率 |
seata_transaction_rollback_total |
已回滚事务数 | 回滚率>5%告警 |
seata_branch_transaction_total |
分支事务总数 | 突增告警 |
seata_lock_conflict_total |
全局锁冲突次数 | 持续上升告警 |
九、踩坑指南
坑一:undo_log表不存在导致事务失败
现象 :启动时报Table 'xxx.undo_log' doesn't exist。
原因 :业务数据库未创建undo_log表。
解决 :每个参与分布式事务的业务数据库都必须创建undo_log表(第5.3节的建表语句)。
坑二:Gateway链路中XID丢失
现象:Gateway转发后的请求,Seata的XID丢失,下游服务无法加入全局事务。
原因 :Gateway的GlobalFilter修改了请求头,覆盖了Seata注入的TX_XID头。或Gateway本身是基于WebFlux的,旧版本Seata不兼容。
解决 :确保使用Spring Cloud Alibaba 2025.1.0.0和Seata 2.5.0,已支持WebFlux。在自定义过滤器中,将TX_XID头加入白名单。
坑三:@GlobalTransactional注解不生效
现象 :添加了@GlobalTransactional,但事务没有回滚。
原因 :@GlobalTransactional方法必须由Spring代理调用 (与@Transactional相同)。如果同一个类中直接调用,注解不会生效。
解决:将全局事务方法抽取到独立的Service中,或注入自身调用。
坑四:Feign重试导致分支事务重复注册
现象:Seata日志中出现大量重复的Branch ID。
原因:Feign配置了重试,超时后重试导致分支事务被重复注册。
解决 :Seata参与的服务禁用Feign重试(Retryer.NEVER_RETRY)。
坑五:生产环境使用file存储模式
现象:Seata Server重启后,未完成的事务无法恢复。
原因:file模式下事务日志存储在本地文件,重启后可能丢失。
解决 :生产环境必须使用db或redis存储模式(第5.2节)。
坑六:AT模式读到脏数据
现象:全局事务未提交时,其他事务读到了未提交的数据。
原因 :AT模式默认的全局隔离级别是读未提交。
解决 :如果需要读已提交,使用SELECT ... FOR UPDATE语句,Seata会代理该语句检查全局锁。
坑七:Seata 2.5.0启动失败------未初始化账户
现象:Seata Server启动报错,提示账户未初始化。
原因:Seata 2.5.0强制要求初始化账户并禁用默认凭证。
解决 :在application.yml中配置security.secret-key,且所有节点保持一致。
十、课后作业
作业一:使用Docker或本地部署Seata Server 2.5.0(db存储模式),配置Nacos注册和配置中心。验证控制台可正常访问。
作业二 :为service-order和service-product添加Seata依赖和配置。在undo_log表建好后,实现@GlobalTransactional的订单创建方法。
作业三:验证正常下单和库存不足两种场景。库存不足时,确认订单记录被回滚删除,库存未变化。
作业四 :模拟Feign超时,验证Seata全局回滚。在库存服务中添加Thread.sleep(10000),Feign读取超时设为5秒,确认Feign超时后Seata触发全局回滚。
作业五(进阶) :调整本地事务传播属性为Propagation.REQUIRES_NEW,对比调整前后的锁等待超时次数和RT变化。
十一、阶段总结与下节预告
至此,微服务高阶特性 & 分布式核心能力阶段(第29-31课) 已完成前两课:
| 课程 | 核心能力 | 关键产出 |
|---|---|---|
| 第29课 | 异步调用与事件驱动 | @Async、线程池调优、MDC透传、Spring事件 |
| 第30课 | 分布式事务与Seata | AT模式、全局锁、@GlobalTransactional、性能调优 |
两课的核心逻辑 :第29课解决了单服务内的异步与解耦 ,第30课解决了跨服务的分布式事务一致性 。第31课将进入微服务消息驱动(RabbitMQ/RocketMQ)适配与解耦实战,内容包括消息异步解耦、可靠消息投递、消费幂等、死信队列和流量削峰。消息队列与分布式事务是互补的------Seata的AT模式适合强一致场景(如支付扣款),消息队列的最终一致性适合异步解耦场景(如订单通知)。两者结合,构成微服务数据一致性的完整方案。
🔗《最新版 SpringCloud 2025 从入门到实战》系列课程导航
第一部分:微服务前置基础 & 新版环境搭建(第1-5课)
第二部分:注册中心核心(Nacos 最新版)(第6-9课)
第三部分:配置中心核心(Nacos配置中心)(第10-12课)
第四部分:服务通信核心(OpenFeign + LoadBalancer)(第13-16课)
第五部分:网关核心(SpringCloud Gateway 新版)(第17-20课)
第六部分:熔断、限流、降级(Sentinel 新版)(第21-24课)
第七部分:微服务监控、链路追踪、日志体系(第25-28课)
第八部分:微服务高阶特性 & 分布式核心能力(第29-31课)
第九部分:企业级完整项目实战 & 架构复盘(第32-35课)