Appearance
Seata 详解
Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的分布式事务解决方案,前身为 Fescar,2019 年 1 月正式开源并更名为 Seata。它为微服务架构提供高性能、易用的分布式事务服务。
目录
1. 为什么需要 Seata
1.1 分布式事务问题场景
在微服务架构中,一个业务操作往往需要跨越多个服务、多个数据库。以电商下单为例:
用户下单流程:
订单服务 库存服务 账户服务
(order_db) (storage_db) (account_db)
| | |
|--- 1. 创建订单 -------| |
| | |
|----------------------- 2. 扣减库存 -----------|
| | |
|----------------------------------------------- 3. 扣减余额
| | |问题:如果扣减库存成功,但扣减余额失败(例如余额不足),此时:
- 订单已创建(不可回滚)
- 库存已扣减(不可回滚)
- 余额未扣减
结果:数据不一致。库存少了,但钱没扣到,这是严重的业务故障。
1.2 没有分布式事务的后果
| 场景 | 订单服务 | 库存服务 | 账户服务 | 结果 |
|---|---|---|---|---|
| 正常 | 成功 | 成功 | 成功 | 数据一致 |
| 异常1 | 成功 | 成功 | 失败 | 库存扣了,钱没扣,数据不一致 |
| 异常2 | 成功 | 失败 | - | 订单已创建,但库存没扣,数据不一致 |
| 异常3 | 失败 | - | - | 无影响 |
1.3 Seata 的解决方案
Seata 提供了一套完整的分布式事务协调机制,确保跨服务、跨数据库的操作要么全部成功,要么全部回滚,保证数据最终一致性。
Seata 协调下的分布式事务:
TM(订单服务) RM(库存服务) RM(账户服务)
| | |
|-- 开启全局事务 -----| |
| | |
|-- 调用扣库存 ------->| |
| |-- 执行+UNDO_LOG --> |
| |-- 返回成功 --------->|
| | |
|-- 调用扣余额 -------|--------------------->|
| | |-- 执行+UNDO_LOG
| | |-- 返回失败!
| | |
|-- TC 检测到失败,通知所有 RM 回滚 --------|
| | |
| |<-- 根据 UNDO_LOG 回滚
| |<--------------------|-- 根据 UNDO_LOG 回滚
| | |
|-- 全局事务回滚完成 |2. 核心概念与架构
2.1 三大角色
Seata 定义了三个核心角色,各司其职:
| 角色 | 全称 | 职责 |
|---|---|---|
| TC | Transaction Coordinator(事务协调器) | 维护全局事务和分支事务的状态,驱动全局事务的提交或回滚。独立部署的 Seata Server。 |
| TM | Transaction Manager(事务管理器) | 定义全局事务的范围:开启全局事务、提交或回滚全局事务。通常由发起方服务担任。 |
| RM | Resource Manager(资源管理器) | 管理分支事务的资源,向 TC 注册分支事务、报告分支事务状态,驱动分支事务的提交或回滚。每个参与方服务都是 RM。 |
2.2 架构图
+-------------------+
| |
| TC (Seata Server)|
| 事务协调器 |
| (独立部署) |
| |
+----+---------+----+
^ ^
| |
注册分支/报告状态 | | 注册分支/报告状态
| |
+--------------------+ +--------------------+
| | | |
v | | v
+-------+--------+ | | +--------+-------+
| | | | | |
| TM (订单服务) |-----------+ +-----------| RM (账户服务) |
| 事务管理器 | RPC 调用 | 资源管理器 |
| |--------------------------------->| |
+----------------+ +----------------+
|
| RPC 调用
v
+----------------+
| |
| RM (库存服务) |
| 资源管理器 |
| |
+----------------+2.3 事务执行流程
1. TM 向 TC 申请开启一个全局事务,TC 返回一个全局事务 ID(XID)
2. XID 在微服务调用链中传播(通过 Feign 拦截器 / Dubbo Filter)
3. 每个 RM 向 TC 注册分支事务,TC 返回分支事务 ID(Branch ID)
4. 每个 RM 执行本地事务(业务 SQL),并记录 UNDO_LOG
5. 每个 RM 向 TC 报告分支事务状态(成功/失败)
6. TM 根据所有 RM 的执行结果,向 TC 发起全局提交或全局回滚
7. TC 通知所有 RM 进行分支事务的提交或回滚3. AT 模式(最常用)
3.1 概述
AT 模式(Automatic Transaction)是 Seata 默认的分布式事务模式,基于两阶段提交协议,通过自动生成反向补偿 SQL 实现回滚,对业务代码零侵入。
核心思想:在业务 SQL 执行前后,自动记录数据快照(UNDO_LOG),回滚时根据快照生成反向 SQL 进行补偿。
3.2 两阶段流程
一阶段:执行 + 记录 UNDO_LOG
一阶段流程(每个 RM 独立执行):
+------------------+
| 业务 SQL 执行前 |
| SELECT 获取前镜像 | <-- 查询当前数据作为 before_image
+--------+---------+
|
v
+------------------+
| 业务 SQL 执行 | <-- 执行 INSERT/UPDATE/DELETE
+--------+---------+
|
v
+------------------+
| 业务 SQL 执行后 |
| SELECT 获取后镜像 | <-- 查询变更后数据作为 after_image
+--------+---------+
|
v
+------------------+
| 写入 UNDO_LOG | <-- 将 before_image + after_image 写入 UNDO_LOG 表
+------------------+关键点:一阶段所有操作在一个本地事务中完成,保证原子性。
二阶段:提交或回滚
二阶段 - 提交(所有 RM 均成功):
+------------------+
| TC 通知所有 RM |
| 全局提交 |
+--------+---------+
|
v
+------------------+
| RM 异步删除 |
| UNDO_LOG 记录 | <-- 仅删除 UNDO_LOG,业务数据已持久化
+------------------+
二阶段 - 回滚(任一 RM 失败):
+------------------+
| TC 通知所有 RM |
| 全局回滚 |
+--------+---------+
|
v
+------------------+
| RM 读取 UNDO_LOG | <-- 获取 before_image 和 after_image
+--------+---------+
|
v
+------------------+
| 校验数据一致性 | <-- 对比当前数据与 after_image,防止脏写
+--------+---------+
|
v
+------------------+
| 执行反向补偿 SQL | <-- 根据 before_image 生成反向 UPDATE/DELETE/INSERT
+--------+---------+
|
v
+------------------+
| 删除 UNDO_LOG |
+------------------+3.3 完整流程图
时间线 ──────────────────────────────────────────────────────────────>
TM TC RM-A RM-B
(订单服务) (Seata Server) (库存服务) (账户服务)
| | | |
|--1. 开启全局事务 ---->| | |
|<-- 返回 XID ---------| | |
| | | |
|--2. 调用 RM-A ------>| | |
| |<--3. 注册分支事务 ---| |
| |-- 返回 BranchId --->| |
| | | |
| | |--4. 执行本地事务 |
| | | (SQL + UNDO_LOG)
| |<--5. 报告成功 -------| |
| | | |
|--6. 调用 RM-B ------>| | |
| |<--7. 注册分支事务 ---|------------------|
| |-- 返回 BranchId --->| |
| | | |
| | | --8. 执行本地事务
| | | (SQL + UNDO_LOG)
| | |<--9. 报告失败!---|
| | | |
|<--10. 感知失败 ------| | |
|--11. 发起回滚 ------>| | |
| |--12. 通知 RM-A 回滚->| |
| |--13. 通知 RM-B 回滚-|----------------->|
| | | |
| | |--14. 反向补偿 ----|
| |<--15. 回滚完成 ------| |
| | |--16. 反向补偿 ----|
| |<--17. 回滚完成 ------|------------------|
| | | |
|<--18. 全局事务结束 ---| | |3.4 隔离性保证
读隔离(Read Committed)
Seata AT 模式默认实现 读已提交(Read Committed) 隔离级别。
- SELECT FOR UPDATE 语句会申请全局锁,如果全局锁被其他事务持有,则等待直到锁释放。
- 普通 SELECT 语句直接读取本地数据,不涉及全局锁(默认读已提交)。
写隔离(全局锁防脏写)
写隔离流程:
事务 A TC 事务 B
(正在执行) (Seata) (并发执行)
| | |
|--1. 执行业务 SQL -----| |
|--2. 获取全局锁 ------->| |
|<-- 获取成功 ----------| |
| | |
| |<--3. 尝试获取全局锁 ---|
| |-- 锁已被 A 持有 |
| |-- 等待/重试... ------->|
| | |
|--4. 提交/回滚 --------| |
|--5. 释放全局锁 ------->| |
| |--6. 通知 B 获取锁 ---->|
| | |
| | |--7. 执行业务 SQL全局锁机制:在事务提交前,必须获取对应数据行的全局锁,防止其他事务并发修改同一数据。
3.5 UNDO_LOG 表结构
sql
-- 每个业务数据库都需要创建此表
CREATE TABLE `undo_log` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT '主键',
`branch_id` BIGINT(20) NOT NULL COMMENT '分支事务 ID',
`xid` VARCHAR(128) NOT NULL COMMENT '全局事务 ID',
`context` VARCHAR(128) NOT NULL COMMENT 'undo_log 上下文',
`rollback_info` LONGBLOB NOT NULL COMMENT '回滚日志(before_image + after_image 序列化 JSON)',
`log_status` INT(11) NOT NULL COMMENT '日志状态:0-正常,1-已删除',
`log_created` DATETIME NOT NULL COMMENT '创建时间',
`log_modified` DATETIME NOT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AT 模式 UNDO_LOG 表';3.6 AT 模式优缺点
| 优点 | 缺点 |
|---|---|
对业务代码零侵入,仅需添加 @GlobalTransactional 注解 | 需要每个业务数据库创建 UNDO_LOG 表 |
| 自动生成回滚 SQL,开发成本低 | 一阶段本地事务和二阶段回滚有短暂时间差,存在中间状态 |
| 性能较好,一阶段即提交,二阶段异步清理 | 依赖数据库的本地事务能力(需要支持 ACID) |
| 支持各种 SQL(INSERT/UPDATE/DELETE) | 不支持跨数据库的 JOIN 操作 |
4. TCC 模式
4.1 概述
TCC(Try-Confirm-Cancel)是一种侵入式的分布式事务模式,需要开发者手动实现三个阶段的逻辑。
核心思想:将业务操作拆分为 Try(预留资源)、Confirm(确认提交)、Cancel(取消回滚)三个阶段,每个阶段都需要开发者手动编码实现。
4.2 三阶段流程
TCC 事务执行流程:
阶段 1: Try(预留资源)
+--------------------------------------------------+
| 账户服务:冻结余额(不直接扣减) |
| 库存服务:冻结库存(不直接扣减) |
| 订单服务:创建"预订单"(状态为待确认) |
+--------------------------------------------------+
|
v
+-------------------------------+
| 所有 Try 成功? |
+----+---------------------+----+
| YES | NO
v v
+------------------------+ +------------------------+
| 阶段 2: Confirm(确认) | | 阶段 2: Cancel(取消) |
+------------------------+ +------------------------+
| 账户:实际扣减冻结余额 | | 账户:解冻余额 |
| 库存:实际扣减冻结库存 | | 库存:解冻库存 |
| 订单:状态改为已确认 | | 订单:状态改为已取消 |
+------------------------+ +------------------------+4.3 与 AT 模式的区别
| 维度 | AT 模式 | TCC 模式 |
|---|---|---|
| 回滚方式 | 自动生成反向 SQL | 手动编写 Cancel 逻辑 |
| 代码侵入性 | 无(仅需注解) | 高(需实现 Try/Confirm/Cancel) |
| 性能 | 好 | 更好(无 UNDO_LOG 开销) |
| 开发成本 | 低 | 高 |
| 灵活性 | 受限于 SQL 自动补偿 | 极高,可自定义任意补偿逻辑 |
| 资源锁定 | 全局锁 | 业务层面预留资源 |
4.4 代码示例
java
// 账户服务 - TCC 接口定义
@LocalTCC
public interface AccountTccService {
/**
* Try 阶段:冻结余额
* @TwoPhaseBusinessAction 标注 TCC 两阶段方法
* - name: TCC 资源名称,全局唯一
* - commitMethod: Confirm 方法名
* - rollbackMethod: Cancel 方法名
*/
@TwoPhaseBusinessAction(
name = "account-deduct",
commitMethod = "deductConfirm",
rollbackMethod = "deductCancel"
)
boolean deductTry(
@BusinessActionContextParameter(paramName = "userId") String userId,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount
);
/**
* Confirm 阶段:实际扣减
*/
boolean deductConfirm(BusinessActionContext context);
/**
* Cancel 阶段:解冻余额
*/
boolean deductCancel(BusinessActionContext context);
}java
// 账户服务 - TCC 接口实现
@Service
public class AccountTccServiceImpl implements AccountTccService {
@Autowired
private AccountMapper accountMapper;
@Autowired
private FreezeLogMapper freezeLogMapper;
@Override
@Transactional
public boolean deductTry(String userId, BigDecimal amount) {
// Try:冻结余额,不实际扣减
// 1. 检查可用余额是否足够
Account account = accountMapper.selectByUserId(userId);
if (account.getAvailableAmount().compareTo(amount) < 0) {
throw new BusinessException("余额不足");
}
// 2. 冻结金额(可用余额减少,冻结金额增加)
accountMapper.freezeAmount(userId, amount);
// 3. 记录冻结日志
String xid = RootContext.getXID();
freezeLogMapper.insert(new FreezeLog(xid, userId, amount));
return true;
}
@Override
@Transactional
public boolean deductConfirm(BusinessActionContext context) {
// Confirm:实际扣减冻结金额
String userId = (String) context.getActionContext("userId");
BigDecimal amount = new BigDecimal((String) context.getActionContext("amount"));
// 1. 实际扣减冻结金额
accountMapper.confirmDeduct(userId, amount);
// 2. 删除冻结日志
freezeLogMapper.deleteByXid(context.getXid());
return true;
}
@Override
@Transactional
public boolean deductCancel(BusinessActionContext context) {
// Cancel:解冻余额,恢复可用余额
String userId = (String) context.getActionContext("userId");
BigDecimal amount = new BigDecimal((String) context.getActionContext("amount"));
// 1. 解冻金额恢复
accountMapper.unfreezeAmount(userId, amount);
// 2. 删除冻结日志
freezeLogMapper.deleteByXid(context.getXid());
return true;
}
}java
// 业务调用方 - 使用 @GlobalTransactional
@Service
public class OrderService {
@Autowired
private AccountTccService accountTccService;
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public void createOrder(OrderDTO orderDTO) {
// 1. 创建订单
orderMapper.insert(orderDTO);
// 2. 扣减库存(TCC)
storageTccService.deductTry(orderDTO.getProductId(), orderDTO.getQuantity());
// 3. 扣减余额(TCC)
accountTccService.deductTry(orderDTO.getUserId(), orderDTO.getAmount());
// 任何一步抛出异常,TC 将自动调用各服务的 Cancel 方法
}
}4.5 适用场景
- 高性能场景:如账户余额扣减、积分扣减,需要高并发、低延迟
- 需要自定义补偿逻辑:如调用第三方接口,需要发送取消请求
- 资源预留型业务:如秒杀、抢购,需要先冻结资源再确认
5. Saga 模式
5.1 概述
Saga 模式是 Seata 提供的长事务解决方案。在 Saga 模式中,业务流程中的每个参与者都提交本地事务,当某个参与者失败时,则按相反顺序调用之前成功参与者的补偿操作。
核心思想:一阶段正向服务依次调用并提交,失败时反向调用补偿服务。
5.2 执行流程
Saga 事务执行流程:
正向执行:
+-------------+ +-------------+ +-------------+
| 服务 A | --> | 服务 B | --> | 服务 C |
| (正向操作) | | (正向操作) | | (正向操作) |
| 提交本地事务 | | 提交本地事务 | | 提交本地事务 |
+-------------+ +-------------+ +-------------+
|
| 失败!
v
反向补偿:
+-------------+ +-------------+ +-------------+
| 服务 A | <-- | 服务 B | <-- | 服务 C |
| (补偿操作) | | (补偿操作) | | (补偿操作) |
+-------------+ +-------------+ +-------------+5.3 代码示例
java
// Saga 服务定义(基于状态机)
// 方式一:使用 JSON 状态机 DSL 定义json
{
"Name": "create-order-saga",
"StartState": "CreateOrder",
"States": {
"CreateOrder": {
"Type": "ServiceTask",
"ServiceName": "orderService.create",
"CompensateState": "CancelOrder",
"Next": "DeductStorage"
},
"DeductStorage": {
"Type": "ServiceTask",
"ServiceName": "storageService.deduct",
"CompensateState": "RestoreStorage",
"Next": "DeductBalance"
},
"DeductBalance": {
"Type": "ServiceTask",
"ServiceName": "accountService.deduct",
"CompensateState": "RestoreBalance",
"Next": "Success"
},
"CancelOrder": {
"Type": "ServiceTask",
"ServiceName": "orderService.cancel",
"Next": "Fail"
},
"RestoreStorage": {
"Type": "ServiceTask",
"ServiceName": "storageService.restore",
"Next": "CancelOrder"
},
"RestoreBalance": {
"Type": "ServiceTask",
"ServiceName": "accountService.restore",
"Next": "RestoreStorage"
},
"Success": {
"Type": "Succeed"
},
"Fail": {
"Type": "Fail"
}
}
}java
// 方式二:使用 @GlobalTransactional 注解(需要配合状态机引擎)
@Service
public class OrderSagaService {
@GlobalTransactional
public void createOrder(OrderDTO orderDTO) {
// 正向操作:每个服务独立提交本地事务
orderService.create(orderDTO); // 补偿:orderService.cancel(orderId)
storageService.deduct(orderDTO); // 补偿:storageService.restore(orderDTO)
accountService.deduct(orderDTO); // 补偿:accountService.restore(orderDTO)
}
}5.4 适用场景
- 老系统改造:无法修改原有代码,只能通过补偿接口实现回滚
- 长事务场景:如出行订票(机票 + 酒店 + 租车),事务可能持续数分钟甚至数小时
- 不支持 ACID 的数据库:如 MongoDB、Redis 等 NoSQL 数据库
- 调用第三方服务:无法控制第三方服务的回滚,只能通过补偿接口处理
5.5 Saga 模式特点
| 优点 | 缺点 |
|---|---|
| 一阶段直接提交,无锁等待,性能高 | 需手动编写补偿逻辑 |
| 适合长事务,不会长时间占用资源 | 可能出现中间状态(数据暂时不一致) |
| 适合异构数据库环境 | 补偿操作必须实现幂等 |
| 适合调用第三方服务 | 无法保证隔离性 |
6. XA 模式
6.1 概述
XA 模式基于 X/Open DTP 模型,使用 XA 协议实现强一致性的分布式事务。Seata 对数据库的 XA 接口进行了封装,使用上与 AT 模式类似。
核心思想:利用数据库自身的 XA 事务支持,通过两阶段提交(2PC)协议保证强一致性。
6.2 两阶段流程
XA 模式两阶段提交:
一阶段 - Prepare(预提交):
+--------------------------------------------------+
| TM 向所有 RM 发送 Prepare 指令 |
| RM 执行 SQL,但不提交(数据被锁定) |
| RM 返回 Prepare 结果(Ready / Fail) |
+--------------------------------------------------+
|
v
+-------------------------------+
| 所有 RM 返回 Ready? |
+----+---------------------+----+
| YES | NO
v v
+------------------------+ +------------------------+
| 二阶段 - Commit(提交) | | 二阶段 - Rollback(回滚)|
+------------------------+ +------------------------+
| TM 通知所有 RM 提交 | | TM 通知所有 RM 回滚 |
| RM 执行 COMMIT | | RM 执行 ROLLBACK |
| 释放锁资源 | | 释放锁资源 |
+------------------------+ +------------------------+6.3 代码示例
java
// XA 模式配置
@Configuration
public class SeataXaConfig {
@Bean
public DataSourceProxy dataSource(DruidDataSource druidDataSource) {
// XA 模式使用 DataSourceProxyXA 代理数据源
return new DataSourceProxyXA(druidDataSource);
}
}yaml
# application.yml - XA 模式配置
seata:
data-source-proxy-mode: XA # 开启 XA 模式
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: defaultjava
// 业务代码与 AT 模式完全相同,仅需 @GlobalTransactional
@Service
public class OrderService {
@GlobalTransactional
public void createOrder(OrderDTO orderDTO) {
orderMapper.insert(orderDTO);
storageService.deduct(orderDTO.getProductId(), orderDTO.getQuantity());
accountService.deduct(orderDTO.getUserId(), orderDTO.getAmount());
}
}6.4 XA 模式 vs AT 模式
| 维度 | XA 模式 | AT 模式 |
|---|---|---|
| 一致性 | 强一致性(Prepare 期间数据锁定) | 最终一致性(一阶段提交后释放锁) |
| 性能 | 差(Prepare 期间锁定资源,事务持续时间长) | 好(一阶段即提交,锁持有时间短) |
| 数据库支持 | 需要数据库支持 XA 协议 | 需要支持 ACID 事务的数据库 |
| 回滚方式 | 数据库原生 ROLLBACK | 根据 UNDO_LOG 反向补偿 |
| 代码侵入 | 无(注解驱动) | 无(注解驱动) |
| 适用场景 | 对一致性要求极高的金融场景 | 大部分互联网业务场景 |
6.5 适用场景
- 金融核心系统:资金转账、账务处理,不允许任何中间不一致状态
- 对一致性要求极高的场景:强一致性 > 性能
- 数据库天然支持 XA:MySQL、Oracle、PostgreSQL 等
7. 四种模式对比
7.1 综合对比表
| 维度 | AT 模式 | TCC 模式 | Saga 模式 | XA 模式 |
|---|---|---|---|---|
| 一致性 | 最终一致性 | 最终一致性 | 最终一致性 | 强一致性 |
| 隔离性 | 读已提交 + 全局锁写隔离 | 业务层隔离 | 无隔离保证 | 持久隔离(Prepare 锁定) |
| 性能 | 好 | 最好 | 好 | 差 |
| 开发成本 | 低(零侵入) | 高(需手写 Try/Confirm/Cancel) | 中(需手写补偿接口) | 低(零侵入) |
| 代码侵入性 | 无 | 高 | 中 | 无 |
| 回滚方式 | 自动生成反向 SQL | 手动 Cancel 逻辑 | 手动补偿操作 | 数据库原生 ROLLBACK |
| 数据库要求 | 支持 ACID 的关系型数据库 | 无特殊要求 | 无特殊要求 | 支持 XA 协议的关系型数据库 |
| UNDO_LOG 表 | 需要 | 不需要 | 不需要 | 不需要 |
| 资源锁定 | 全局锁 | 业务层预留资源 | 无锁 | 数据库行锁(Prepare 期间) |
| 适用场景 | 大部分互联网业务 | 高性能、高并发场景 | 长事务、老系统改造 | 强一致性金融场景 |
| 幂等要求 | 框架自动处理 | Confirm/Cancel 需幂等 | 补偿操作需幂等 | 框架自动处理 |
| 空回滚处理 | 框架自动处理 | 需手动处理 Cancel | 需手动处理 | 框架自动处理 |
7.2 选型建议
选型决策树:
需要强一致性?
├── YES → XA 模式
└── NO
├── 长事务(> 分钟级)?
│ ├── YES → Saga 模式
│ └── NO
│ ├── 需要极致性能或自定义补偿?
│ │ ├── YES → TCC 模式
│ │ └── NO → AT 模式(默认推荐)8. 核心配置
8.1 Seata Client 完整配置
yaml
# application.yml - Seata Client 配置
seata:
# 是否开启 Seata 自动配置
enabled: true
# 应用名称(对应 Seata Server 中的事务分组映射)
application-id: ${spring.application.name}
# 事务分组(重要!用于匹配 TC 集群)
tx-service-group: my_tx_group
# 服务配置:事务分组 → 集群名称映射
service:
vgroup-mapping:
# key: 事务分组名称, value: TC 集群名称(Seata Server 的 registry 集群名)
my_tx_group: default
# 禁用全局事务分组配置
disable-global-transaction: false
# 全局事务超时(毫秒)默认 60 秒
grouplist:
default: 127.0.0.1:8091
# 注册中心配置
registry:
# 注册中心类型:nacos / eureka / redis / zk / consul / etcd3 / sofa
type: nacos
nacos:
application: seata-server
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: "" # Nacos 命名空间 ID
cluster: default
username: nacos
password: nacos
# 配置中心配置
config:
# 配置中心类型:nacos / consul / apollo / zk / etcd3
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: ""
data-id: seata.properties
username: nacos
password: nacos
# 存储模式(仅 file 模式时有效,db/redis 模式在 Server 端配置)
store:
# file / db / redis
mode: file
file:
dir: sessionStore
max-branch-session-size: 16384
max-global-session-size: 512
file-write-buffer-cache-size: 16384
session-reload-read-size: 100
flush-disk-mode: async
# 传输配置
transport:
type: TCP
server: NIO
heartbeat: true
enable-client-batch-send-request: true
thread-factory:
boss-thread-prefix: NettyBoss
worker-thread-prefix: NettyServerNIOWorker
server-executor-thread-prefix: NettyServerBizHandler
share-boss-worker: false
client-selector-thread-prefix: NettyClientSelector
client-selector-thread-size: 1
client-worker-thread-prefix: NettyClientWorkerThread
boss-thread-size: 1
worker-thread-size: default
shutdown:
wait: 3
# 客户端参数
client:
rm:
# 异步提交缓存队列大小
async-commit-buffer-limit: 10000
# 一阶段结果上报重试次数
report-retry-count: 5
# 表元数据校验
table-meta-check-enable: false
# 上报重试间隔(毫秒)
report-success-enable: false
# Saga 分支注册重试次数
saga-branch-register-enable: false
# SQL 解析器类型
sql-parser-type: druid
# 锁重试间隔(毫秒)
lock:
retry-interval: 10
retry-times: 30
retry-policy-branch-rollback-on-conflict: true
tm:
# 全局事务提交重试次数
commit-retry-count: 5
# 全局事务回滚重试次数
rollback-retry-count: 5
# 一阶段方法是否降级
degrade-check: false
degrade-check-period: 2000
degrade-check-allow-times: 10
undo:
# UNDO_LOG 存储方式
data-validation: true
# 日志序列化方式
log-serialization: jackson
# UNDO_LOG 表名
log-table: undo_log
# 仅当 UPDATE 前后镜像不同时才记录 UNDO_LOG
only-care-update-columns: true
load-balance:
type: XID
# 日志配置
log:
exception-rate: 1008.2 数据源代理配置
java
// 方式一:手动配置数据源代理(推荐,更灵活)
@Configuration
public class SeataDataSourceConfig {
/**
* 使用 Seata 包装原始数据源
* Seata 通过 DataSourceProxy 拦截 SQL 执行,自动记录 UNDO_LOG
*/
@Bean
@Primary
public DataSource dataSource(DruidDataSource druidDataSource) {
// AT 模式使用 DataSourceProxy
return new DataSourceProxy(druidDataSource);
// XA 模式使用 DataSourceProxyXA
// return new DataSourceProxyXA(druidDataSource);
}
}yaml
# 方式二:使用自动代理(Spring Boot 自动配置)
seata:
data-source-proxy-mode: AT # AT / XA8.3 XID 传播配置
java
// Feign 拦截器 - 自动传播 XID
@Component
public class SeataFeignInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
String xid = RootContext.getXID();
if (StringUtils.hasText(xid)) {
template.header(RootContext.KEY_XID, xid);
}
}
}java
// Filter 接收 XID
@Component
public class SeataFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
String xid = httpRequest.getHeader(RootContext.KEY_XID);
if (StringUtils.hasText(xid)) {
RootContext.bind(xid);
}
try {
chain.doFilter(request, response);
} finally {
RootContext.unbind();
}
}
}9. 实战配置
9.1 环境准备
依赖版本:
- Spring Boot: 2.3.x+
- Spring Cloud: Hoxton.SR9+
- Spring Cloud Alibaba: 2.2.6.RELEASE+
- Seata: 1.4.2+
- Nacos: 1.4.2+
- MySQL: 5.7+9.2 Maven 依赖
xml
<!-- Seata 依赖 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>
<!-- 排除自带的低版本 Seata -->
<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>
<!-- 引入指定版本的 Seata -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>9.3 创建 UNDO_LOG 表
sql
-- 在每个业务数据库执行以下 SQL
-- 订单库 (order_db)
CREATE TABLE `undo_log` (
`id` BIGINT(20) NOT NULL AUTO_INCREMENT,
`branch_id` BIGINT(20) NOT NULL,
`xid` VARCHAR(128) 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 DEFAULT CHARSET=utf8mb4;
-- 库存库 (storage_db)
-- (同上 SQL)
-- 账户库 (account_db)
-- (同上 SQL)9.4 配置 registry.conf
conf
# registry.conf - Seata 注册中心配置
# 此文件放在 resources 目录下
registry {
type = "nacos"
nacos {
application = "seata-server"
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = ""
cluster = "default"
username = "nacos"
password = "nacos"
}
}
config {
type = "nacos"
nacos {
serverAddr = "127.0.0.1:8848"
group = "SEATA_GROUP"
namespace = ""
dataId = "seata.properties"
username = "nacos"
password = "nacos"
}
}9.5 配置 application.yml
yaml
# application.yml - 订单服务
spring:
application:
name: order-service
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/order_db?useUnicode=true&characterEncoding=utf-8&useSSL=false
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
seata:
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
application: seata-server
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
data-source-proxy-mode: AT9.6 数据源代理 Bean 配置
java
package com.example.order.config;
import com.alibaba.druid.pool.DruidDataSource;
import io.seata.rm.datasource.DataSourceProxy;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration
public class SeataDataSourceConfig {
/**
* 原始 Druid 数据源
* 注意:此 Bean 不需要 @Primary,SeataDataSourceProxy 设置为 @Primary
*/
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DruidDataSource druidDataSource() {
return new DruidDataSource();
}
/**
* Seata 数据源代理
* 设置为 @Primary,所有 MyBatis/ JPA 将使用此代理数据源
*/
@Bean
@Primary
public DataSource dataSource(DruidDataSource druidDataSource) {
return new DataSourceProxy(druidDataSource);
}
}9.7 @GlobalTransactional 注解使用
java
package com.example.order.service;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private StorageFeignClient storageFeignClient;
@Autowired
private AccountFeignClient accountFeignClient;
/**
* @GlobalTransactional 开启全局事务
*
* 属性说明:
* - name: 全局事务名称(可选)
* - timeoutMills: 超时时间(毫秒),默认 60 秒
* - rollbackFor: 触发回滚的异常类型
* - noRollbackFor: 不触发回滚的异常类型
* - propagation: 事务传播行为(默认 REQUIRED)
*/
@GlobalTransactional(
name = "create-order",
timeoutMills = 300000, // 5 分钟
rollbackFor = Exception.class
)
public void createOrder(OrderDTO orderDTO) {
// 1. 本地事务:创建订单
Order order = new Order();
order.setUserId(orderDTO.getUserId());
order.setProductId(orderDTO.getProductId());
order.setQuantity(orderDTO.getQuantity());
order.setAmount(orderDTO.getAmount());
order.setStatus("PENDING");
orderMapper.insert(order);
// 2. 远程调用:扣减库存
storageFeignClient.deduct(orderDTO.getProductId(), orderDTO.getQuantity());
// 3. 远程调用:扣减余额
accountFeignClient.deduct(orderDTO.getUserId(), orderDTO.getAmount());
// 4. 更新订单状态
order.setStatus("SUCCESS");
orderMapper.updateById(order);
// 任何步骤抛出异常,Seata 会自动回滚所有操作
}
}9.8 全局事务回滚示例
java
@Service
public class OrderService {
@GlobalTransactional
public void createOrderWithRollback(OrderDTO orderDTO) {
// 步骤1:创建订单 - 成功
orderMapper.insert(order);
log.info("订单创建成功,订单ID: {}", order.getId());
// 步骤2:扣减库存 - 成功
storageFeignClient.deduct(orderDTO.getProductId(), orderDTO.getQuantity());
log.info("库存扣减成功");
// 步骤3:扣减余额 - 失败!余额不足
try {
accountFeignClient.deduct(orderDTO.getUserId(), orderDTO.getAmount());
} catch (Exception e) {
log.error("余额扣减失败,触发全局回滚: {}", e.getMessage());
// 抛出异常,Seata 自动回滚步骤1和步骤2
throw new BusinessException("余额不足,订单创建失败");
}
}
}
/*
* 执行结果:
*
* 1. 订单表:插入的记录被自动回滚删除
* 2. 库存表:扣减的库存被自动恢复(UNDO_LOG 反向补偿)
* 3. 账户表:无变化(调用失败,未执行本地事务)
*
* 最终:三个数据库数据一致,相当于什么都没发生
*/9.9 Seata 执行日志示例
# 正常流程日志
2024-01-01 10:00:00.001 INFO Begin new global transaction [192.168.1.1:8091:1234567890]
2024-01-01 10:00:00.010 INFO Register branch transaction [192.168.1.1:8091:1234567890:1234567891]
2024-01-01 10:00:00.100 INFO Branch commit success [192.168.1.1:8091:1234567890:1234567891]
2024-01-01 10:00:00.200 INFO Global transaction commit [192.168.1.1:8091:1234567890]
# 回滚流程日志
2024-01-01 10:00:00.001 INFO Begin new global transaction [192.168.1.1:8091:1234567890]
2024-01-01 10:00:00.010 INFO Register branch transaction [192.168.1.1:8091:1234567890:1234567891]
2024-01-01 10:00:00.100 INFO Branch commit success [192.168.1.1:8091:1234567890:1234567891]
2024-01-01 10:00:00.200 INFO Register branch transaction [192.168.1.1:8091:1234567890:1234567892]
2024-01-01 10:00:00.300 ERROR Branch commit failed [192.168.1.1:8091:1234567890:1234567892]
2024-01-01 10:00:00.301 INFO Global transaction rollback [192.168.1.1:8091:1234567890]
2024-01-01 10:00:00.310 INFO Branch rollback success [192.168.1.1:8091:1234567890:1234567891]
2024-01-01 10:00:00.320 INFO Branch rollback success [192.168.1.1:8091:1234567890:1234567892]10. Seata Server 部署
10.1 下载与安装
bash
# 1. 下载 Seata Server
wget https://github.com/seata/seata/releases/download/v1.4.2/seata-server-1.4.2.tar.gz
# 2. 解压
tar -zxvf seata-server-1.4.2.tar.gz
# 3. 目录结构
cd seata
# ├── bin/ # 启动脚本
# │ ├── seata-server.sh
# │ └── seata-server.bat
# ├── conf/ # 配置文件
# │ ├── registry.conf # 注册中心配置
# │ ├── file.conf # file 模式配置(不推荐)
# │ └── logback.xml # 日志配置
# ├── lib/ # 依赖包
# └── target/ # 启动 JAR10.2 三种存储模式
模式一:file 模式(默认,不推荐生产)
conf
# conf/file.conf
store {
mode = "file"
file {
dir = "sessionStore"
maxBranchSessionSize = 16384
maxGlobalSessionSize = 512
fileWriteBufferCacheSize = 16384
sessionReloadReadSize = 100
flushDiskMode = async
}
}特点:单机模式,无高可用,数据存储在本地文件,重启数据丢失。仅适合开发测试。
模式二:db 模式(推荐生产)
sql
-- 1. 创建 Seata Server 数据库
CREATE DATABASE seata;
USE seata;
-- 2. 创建全局事务表
CREATE TABLE `global_table` (
`xid` VARCHAR(128) NOT NULL,
`transaction_id` BIGINT,
`status` TINYINT NOT NULL,
`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_status_gmt_modified` (`status`, `gmt_modified`),
KEY `idx_transaction_id` (`transaction_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 3. 创建分支事务表
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;
-- 4. 创建全局锁表
CREATE TABLE `lock_table` (
`row_key` VARCHAR(128) NOT NULL,
`xid` VARCHAR(128),
`transaction_id` BIGINT,
`branch_id` BIGINT NOT NULL,
`resource_id` VARCHAR(256),
`table_name` VARCHAR(32),
`pk` VARCHAR(36),
`status` TINYINT NOT NULL DEFAULT '0',
`gmt_create` DATETIME,
`gmt_modified` DATETIME,
PRIMARY KEY (`row_key`),
KEY `idx_branch_id` (`branch_id`),
KEY `idx_xid_and_branch_id` (`xid`, `branch_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;conf
# conf/registry.conf - db 模式配置
store {
mode = "db"
db {
datasource = "druid"
dbType = "mysql"
driverClassName = "com.mysql.cj.jdbc.Driver"
url = "jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true"
user = "root"
password = "root"
minConn = 5
maxConn = 100
globalTable = "global_table"
branchTable = "branch_table"
lockTable = "lock_table"
queryLimit = 100
maxWait = 5000
}
}模式三:Nacos 模式(推荐)
在 Nacos 中创建 seata.properties 配置项(属于 SEATA_GROUP 分组),内容如下:
properties
# seata.properties - 存储在 Nacos 配置中心
# 存储模式
store.mode=db
store.db.datasource=druid
store.db.dbType=mysql
store.db.driverClassName=com.mysql.cj.jdbc.Driver
store.db.url=jdbc:mysql://127.0.0.1:3306/seata?useUnicode=true&rewriteBatchedStatements=true
store.db.user=root
store.db.password=root
store.db.minConn=5
store.db.maxConn=100
store.db.globalTable=global_table
store.db.branchTable=branch_table
store.db.lockTable=lock_table
store.db.queryLimit=100
# 服务配置
service.vgroupMapping.my_tx_group=default
service.default.grouplist=127.0.0.1:8091
service.enableDegrade=false
service.disableGlobalTransaction=false
# 传输配置
transport.type=TCP
transport.server=NIO
transport.heartbeat=true
transport.enableClientBatchSendRequest=true
transport.threadFactory.bossThreadPrefix=NettyBoss
transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker
transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler
transport.threadFactory.shareBossWorker=false
transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector
transport.threadFactory.clientSelectorThreadSize=1
transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread
transport.threadFactory.bossThreadSize=1
transport.threadFactory.workerThreadSize=default
transport.shutdown.wait=310.3 启动 Seata Server
bash
# Linux / Mac
cd seata/bin
./seata-server.sh -p 8091 -h 127.0.0.1 -m db
# Windows
cd seata\bin
seata-server.bat -p 8091 -h 127.0.0.1 -m db
# 参数说明:
# -p: 端口号(默认 8091)
# -h: 绑定 IP(默认 0.0.0.0)
# -m: 存储模式(file / db / redis)
# -n: Server 节点 ID(多节点部署时区分)10.4 高可用部署
Seata 高可用架构:
+------------------+
| Nacos 集群 |
| (注册中心+配置中心)|
+----+--------+----+
^ ^
| |
+--------------+ +--------------+
| |
v v
+-------+--------+ +--------+-------+
| Seata Server 1 | | Seata Server 2 |
| 192.168.1.1 | | 192.168.1.2 |
| Port: 8091 | | Port: 8091 |
+-------+--------+ +--------+-------+
| |
+------------------+-------------------+
|
v
+------------------+
| MySQL 主从 |
| (seata 库) |
+------------------+高可用部署步骤:
bash
# 1. 在 Nacos 中配置集群信息
# 确保 seata.properties 中 store.mode=db,指向同一个 MySQL
# 2. 启动多个 Seata Server 节点
# 节点 1
./seata-server.sh -p 8091 -h 192.168.1.1 -m db -n 1
# 节点 2
./seata-server.sh -p 8091 -h 192.168.1.2 -m db -n 2
# 节点 3
./seata-server.sh -p 8091 -h 192.168.1.3 -m db -n 3
# 3. 客户端配置
# seata.service.vgroup-mapping.my_tx_group=default
# 客户端从 Nacos 获取 default 集群下所有 Seata Server 节点
# 通过 XID 负载均衡,同一事务的请求路由到同一 Server11. 面试要点
11.1 高频面试题
Q1: 说一下 Seata AT 模式的两阶段原理?
答:
AT 模式基于两阶段提交协议,但与传统 2PC 不同:
- 一阶段:执行业务 SQL,同时记录 UNDO_LOG(前镜像 + 后镜像),然后直接提交本地事务,释放数据库锁。UNDO_LOG 和业务 SQL 在同一个本地事务中,保证原子性。
- 二阶段:
- 如果全局事务成功,TC 通知各 RM 异步删除 UNDO_LOG 即可。
- 如果全局事务失败,TC 通知各 RM 根据 UNDO_LOG 中的前镜像生成反向 SQL 进行补偿,然后删除 UNDO_LOG。
关键点:一阶段直接提交(不像传统 2PC 的 Prepare 阶段锁定资源),因此性能更好。但不保证强一致性,属于最终一致性方案。
Q2: TCC 模式和 AT 模式有什么区别?如何选择?
答:
| 区别 | AT 模式 | TCC 模式 |
|---|---|---|
| 补偿方式 | 框架自动生成反向 SQL | 开发者手动编写 Cancel 逻辑 |
| 代码侵入 | 无(仅需注解) | 高(需实现 Try/Confirm/Cancel) |
| 性能 | 好(有 UNDO_LOG 开销) | 更好(无 UNDO_LOG,直接操作) |
| 资源锁定 | 全局锁 | 业务层预留资源 |
| 灵活性 | 受限于 SQL 反向生成 | 极高,任意自定义补偿 |
选择:
- 大部分业务用 AT 模式,开发成本低,开箱即用。
- 高性能、高并发场景(如秒杀、账户扣减)用 TCC,避免全局锁竞争。
- 需要调用第三方接口、发送消息等复杂补偿逻辑用 TCC。
Q3: Seata 的全局锁机制是什么?为什么需要全局锁?
答:
全局锁是 Seata AT 模式保证写隔离的核心机制。
为什么需要:AT 模式一阶段直接提交本地事务,释放数据库锁。如果两个全局事务同时修改同一行数据,可能出现脏写。
机制:在事务提交前,RM 必须向 TC 申请全局锁(锁定资源 ID + 表名 + 主键)。如果全局锁被其他事务持有,则等待或重试,直到超时后回滚。
流程:先获取本地锁(数据库行锁),执行 SQL,提交本地事务,释放本地锁,然后获取全局锁(由 TC 管理)。只有获取全局锁成功后,一阶段才算完成。这避免了事务 A 提交后、事务 B 修改同一数据而事务 A 又需要回滚的冲突。
Q4: Seata 如何保证数据一致性?
答:
Seata 通过以下机制保证数据一致性:
- 两阶段提交:一阶段执行 + 记录 UNDO_LOG,二阶段根据全局结果决定提交或回滚。
- UNDO_LOG:记录数据变更前后的快照,回滚时根据前镜像生成反向 SQL。
- 全局锁:防止并发事务间的脏写,保证写隔离。
- TC 协调:TC 维护全局事务状态,确保所有分支事务最终一致(全部提交或全部回滚)。
- 重试机制:TM 提交/回滚失败会重试(默认 5 次),RM 报告状态失败会重试。
- 定时任务:TC 对超时未完成的事务进行定时清理(定时回滚或提交)。
注意:AT 模式是最终一致性,不是强一致性。一阶段提交后到二阶段完成之间,其他事务可以读到已提交的数据(存在短暂的中间状态)。
Q5: Seata 的优缺点有哪些?
答:
优点:
- 零侵入:AT 模式仅需
@GlobalTransactional注解,业务代码无需修改。 - 高性能:一阶段直接提交,不长时间锁定资源,性能远优于传统 2PC。
- 多模式支持:AT、TCC、Saga、XA 四种模式覆盖不同场景。
- 生态完善:支持 Dubbo、Spring Cloud、gRPC 等主流框架。
- 高可用:TC 支持集群部署,存储支持 db/redis 模式。
缺点:
- 最终一致性:AT 模式不保证强一致性,有短暂中间状态,不适合强一致性要求场景。
- UNDO_LOG 膨胀:每个业务数据库都需要 UNDO_LOG 表,大数据量时可能膨胀。
- 全局锁开销:高并发下全局锁可能成为瓶颈。
- SQL 限制:AT 模式不支持跨数据库 JOIN、不支持存储过程等复杂 SQL。
- 运维成本:需要独立部署 TC Server,增加了运维复杂度。
Q6: 如果 Seata Server(TC)挂了怎么办?
答:
- 高可用部署:TC 应部署为集群,通过 Nacos/Eureka 实现服务发现和负载均衡。
- 存储外置:使用 db 或 redis 模式存储事务会话,节点故障时其他节点可接管。
- 客户端容错:客户端 RM 本地事务已提交,数据已持久化;TC 恢复后可继续处理二阶段。
- 定时任务恢复:TC 恢复后,定时任务会扫描未完成的事务,重新驱动提交或回滚。
- 超时机制:如果 TM 长时间无法连接 TC,全局事务会超时,各 RM 可根据本地策略处理。
附录
A. 关键文件清单
| 文件 | 路径 | 说明 |
|---|---|---|
| registry.conf | src/main/resources/registry.conf | Seata 注册中心配置 |
| file.conf | seata-server/conf/file.conf | Seata Server 存储配置 |
| application.yml | src/main/resources/application.yml | 业务服务 Seata 配置 |
| undo_log 表 | 各业务数据库 | AT 模式回滚日志表 |
| global_table / branch_table / lock_table | Seata Server 数据库 | TC 事务会话表 |
