Appearance
Seata 极简教程
一、基础理论
1. 定位
- 分布式事务解决方案:解决微服务架构下跨多个数据库/服务的数据一致性问题。
- 阿里开源,Spring Cloud Alibaba 官方推荐,对标阿里内部 DTX。
- 核心目标:高性能 + 对业务侵入低(尤其是 AT 模式)。
2. 核心角色
| 角色 | 全称 | 职责 |
|---|---|---|
| TC | Transaction Coordinator 事务协调者 | 独立进程,维护全局事务状态,驱动全局提交或回滚。 |
| TM | Transaction Manager 事务管理器 | 标注 @GlobalTransactional 的一方,负责开启、提交、回滚全局事务。 |
| RM | Resource Manager 资源管理器 | 各分支服务,管理本地事务资源,向 TC 注册并执行分支事务提交/回滚。 |
3. 四种事务模式
| 模式 | 原理 | 侵入性 | 适用场景 |
|---|---|---|---|
| AT | 自动补偿:一阶段拦截 SQL 生成 undo_log;二阶段根据全局状态决定提交或回滚。 | 低(仅需加注解) | 80% 场景首选,CRUD 操作无缝兼容 |
| TCC | 手动实现 Try / Confirm / Cancel 三阶段接口。 | 高 | 复杂业务、需精细控制资源预留 |
| Saga | 长事务拆分,每阶段正向操作 + 补偿操作;事务链顺序执行,失败时反向补偿。 | 中 | 业务流程长、需异步化、不要求强隔离 |
| XA | 基于 XA 协议的 2PC,数据库原生支持,一阶段不提交,持锁到二阶段。 | 低 | 强一致性、兼容传统 2PC、可接受锁性能损耗 |
4. AT 模式核心原理(最常用)
AT 模式是 Seata 的默认模式,对业务零侵入,只需一个
@GlobalTransactional注解。
一阶段(提交):
- TM 调用
@GlobalTransactional方法,向 TC 申请生成 全局事务 XID。 - XID 通过 RPC 透传到各分支服务。
- RM 拦截业务 SQL,解析生成 前后镜像(修改前数据 + 修改后数据)。
- RM 在本地事务中执行业务 SQL + 插入
undo_log(镜像数据),一并提交。 - RM 向 TC 注册分支事务,报告执行成功。
二阶段(全局提交/回滚):
- 全局提交:TC 通知各 RM 异步删除
undo_log。 - 全局回滚:TC 通知各 RM 用
undo_log中的前镜像生成反向 SQL,补偿回滚数据,再删除undo_log。
undo_log 拆解:
- 存储在业务库的
undo_log表中(Seata 自动维护)。- 包含:前镜像
beforeImage、后镜像afterImage、SQL 类型、表名、主键等。- 回滚时通过前镜像生成
UPDATE/DELETE/INSERT反向 SQL,恢复数据到修改前状态。
5. 隔离级别与脏读问题
AT 模式默认读未提交(一阶段本地事务已提交),存在中间态脏读。
解决方案:
- **
@GlobalTransactional+@Transactional**:本地事务保证原子性,但全局视角仍可能读到未提交数据。 - FOR UPDATE 代理:Seata 代理
SELECT FOR UPDATE,遇到未提交全局事务时申请全局锁等待,达到读已提交效果。
6. 与经典分布式事务方案对比
| 维度 | 2PC/XA | TCC | Saga | Seata AT |
|---|---|---|---|---|
| 一致性 | 强 | 最终一致 | 最终一致 | 最终一致 |
| 性能 | 低(锁资源久) | 高 | 高 | 较高(一阶段即提交) |
| 侵入性 | 低 | 高(需写3个接口) | 中(需写补偿) | 低(仅需注解) |
| 隔离性 | 全局锁 | 业务层控制 | 无 | 默认读未提交,可用全局锁 |
| 回滚机制 | 数据库驱动 | 业务实现 Cancel | 业务实现补偿 | 自动生成 undo_log 回滚 |
| 适用场景 | 金融强一致 | 库存/支付预留 | 审批/物流长流程 | 通用 CRUD 微服务 |
二、工作实践(复制即用)
1. 服务端部署(TC)
Docker 单机部署
bash
docker run --name seata-server \
-p 8091:8091 \
-p 7091:7091 \
-e SEATA_PORT=8091 \
-v /path/to/application.yml:/seata-server/resources/application.yml \
-d seataio/seata-server:1.8.08091:TC 业务端口(RM/TM 连接)。7091:TC 控制台端口(Web UI)。
TC 配置文件要点(application.yml)
yaml
server:
port: 7091
seata:
config:
type: nacos # 支持 nacos / file / etcd / consul
nacos:
server-addr: 127.0.0.1:8848
namespace: seata
group: SEATA_GROUP
data-id: seataServer.properties
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: seata
group: SEATA_GROUP
store:
mode: db # 事务日志存储方式:db / file / redis
db:
datasource: druid
db-type: mysql
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/seata
user: root
password: root生产环境建议用 Nacos 作为配置中心,统一管理 Seata 的 registry/file 等配置。
2. 业务端依赖(RM / TM)
pom.xml
xml
<!-- Seata Spring Boot Starter -->
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.8.0</version>
</dependency>
<!-- 若用 Nacos 作注册/配置中心 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>3. 业务端配置(application.yml)
yaml
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: my_tx_group # 事务分组,对应 TC 集群
service:
vgroup-mapping:
my_tx_group: default # 分组映射到 TC 集群名
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
namespace: seata
group: SEATA_GROUP
client:
rm:
async-commit-buffer-limit: 100004. 数据库准备(AT 模式)
每个业务库都需要创建 undo_log 表:
sql
CREATE TABLE IF NOT EXISTS `undo_log`
(
`branch_id` BIGINT NOT NULL COMMENT '分支事务 ID',
`xid` VARCHAR(128) NOT NULL COMMENT '全局事务 XID',
`context` VARCHAR(128) NOT NULL COMMENT '上下文',
`rollback_info` LONGBLOB NOT NULL COMMENT '回滚信息(前/后镜像)',
`log_status` INT NOT NULL COMMENT '0:正常 1:防御性保留',
`log_created` DATETIME(6) NOT NULL COMMENT '创建时间',
`log_modified` DATETIME(6) NOT NULL COMMENT '修改时间',
UNIQUE INDEX `ux_undo_log` (`xid`, `branch_id`)
) ENGINE = InnoDB
DEFAULT CHARSET = utf8mb4 COMMENT ='AT 模式 undo_log 表';5. 全局事务使用(AT 模式)
业务入口(TM)
java
@Service
@Slf4j
public class OrderService {
@Autowired
private StorageClient storageClient;
@Autowired
private AccountClient accountClient;
@Autowired
private OrderMapper orderMapper;
/**
* 下单:创建订单 + 扣库存 + 扣余额
* 只要任一环节异常,全局回滚
*/
@GlobalTransactional(name = "create-order", rollbackFor = Exception.class)
public void createOrder(OrderCreateReq req) {
// 1. 创建订单
Order order = new Order();
order.setUserId(req.getUserId());
order.setCommodityCode(req.getCommodityCode());
order.setCount(req.getCount());
order.setMoney(req.getMoney());
orderMapper.insert(order);
// 2. 扣库存(RPC 调用 storage-service)
storageClient.deduct(req.getCommodityCode(), req.getCount());
// 3. 扣余额(RPC 调用 account-service)
accountClient.debit(req.getUserId(), req.getMoney());
// 任意步骤抛异常,Seata 驱动全局回滚,各服务自动通过 undo_log 恢复
}
}分支服务(RM)——以库存服务为例
java
@RestController
public class StorageController {
@Autowired
private StorageMapper storageMapper;
@PostMapping("/storage/deduct")
public void deduct(@RequestParam String commodityCode, @RequestParam Integer count) {
// 普通业务方法即可,无需额外注解
// Seata 代理数据源后自动拦截 SQL 生成 undo_log
storageMapper.deduct(commodityCode, count);
}
}6. TCC 模式示例(高侵入但精细控制)
适用于需要预留资源的场景,如下单时冻结库存。
java
@LocalTCC
public interface StorageTccService {
@TwoPhaseBusinessAction(name = "storageTccAction",
commitMethod = "commit",
rollbackMethod = "rollback")
boolean tryDeduct(@BusinessActionContextParameter(paramName = "commodityCode") String commodityCode,
@BusinessActionContextParameter(paramName = "count") int count);
boolean commit(BusinessActionContext context);
boolean rollback(BusinessActionContext context);
}java
@Service
@Slf4j
public class StorageTccServiceImpl implements StorageTccService {
@Autowired
private StorageMapper storageMapper;
@Override
public boolean tryDeduct(String commodityCode, int count) {
// 检查库存并冻结(可用库存 - count,冻结库存 + count)
return storageMapper.freezeStock(commodityCode, count) > 0;
}
@Override
public boolean commit(BusinessActionContext context) {
String commodityCode = context.getActionContext("commodityCode").toString();
int count = Integer.parseInt(context.getActionContext("count").toString());
// 真正扣减:冻结库存 - count,总库存 - count
storageMapper.commitDeduct(commodityCode, count);
return true;
}
@Override
public boolean rollback(BusinessActionContext context) {
String commodityCode = context.getActionContext("commodityCode").toString();
int count = Integer.parseInt(context.getActionContext("count").toString());
// 释放冻结库存
storageMapper.unfreezeStock(commodityCode, count);
return true;
}
}7. Saga 模式简介
适用于业务流程长、需异步化或遗留系统改造。
- 状态机引擎(State Machine Engine):基于 JSON/DSL 定义状态流转图。
- 编排式(Orchestration):由 Saga 编排器统一调用各服务正向操作 + 补偿操作。
- 不保证隔离性,可能存在脏读,需业务层容忍或做幂等设计。
Saga 模式配置相对复杂,需引入 seata-saga-engine,此处不展开完整示例。
8. 数据源代理(AT 模式必须)
Seata AT 模式需要代理数据源才能拦截 SQL 生成 undo_log。
手动配置(若未自动生效)
java
@Configuration
public class SeataDataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
@Primary
@Bean("dataSource")
public DataSource dataSource(DataSource druidDataSource) {
// Seata 代理数据源,开启 SQL 拦截
return new DataSourceProxy(druidDataSource);
}
}Spring Cloud Alibaba Seata 通常自动代理,若发现 undo_log 未生成,检查是否手动多数据源未走代理。
三、核心速查表
| 概念 | 说明 |
|---|---|
@GlobalTransactional | TM 开启全局事务;name 标识,rollbackFor 指定回滚异常 |
XID | 全局事务唯一标识,由 TC 生成,经 RPC 透传各服务 |
undo_log | AT 模式回滚日志表,存前后镜像,业务库必须创建 |
TC | 事务协调者,独立部署,维护全局事务状态 |
TM | 事务管理器,发起方加 @GlobalTransactional |
RM | 资源管理器,各分支服务,向 TC 注册并执行本地分支 |
tx-service-group | 事务分组,对应一套 TC 集群,业务端与 TC 配置需对齐 |
AT | 自动补偿模式,低侵入,80% 场景首选 |
TCC | Try-Confirm-Cancel,高侵入,适合预留资源场景 |
Saga | 长事务 + 补偿,适合流程长、异步化场景 |
XA | 基于数据库 XA 协议的 2PC,强一致但性能低 |
DataSourceProxy | AT 模式必须的数据源代理,拦截业务 SQL |
四、排错速查
| 现象 | 解决 |
|---|---|
@GlobalTransactional 不生效 | 1. 检查是否引入了 seata-spring-boot-starter2. 检查 tx-service-group 与 TC 的 vgroup-mapping 是否匹配3. 检查 TC 是否正常运行,RM/TM 能否连接到 TC 的 8091 端口 |
| undo_log 未生成 / 回滚失败 | 1. 确认业务库已创建 undo_log 表2. 确认数据源被 Seata 代理( DataSourceProxy)3. 检查数据库是否 InnoDB 引擎 |
| 全局事务提交但数据不一致 | 1. 分支服务抛异常后被吞掉(如异步调用未等待结果) 2. RPC 框架未透传 XID(Feign 需确保请求头携带 TX_XID) |
| TC 连接不上 / 注册失败 | 1. 检查 seata.registry 配置(Nacos 地址、namespace、group)2. 检查 TC 服务端 8091 端口是否暴露3. 检查 Nacos 中 service.vgroup-mapping.my_tx_group 是否指向正确的 TC 集群名 |
| TCC try 成功但 commit/rollback 不执行 | 1. 检查 @TwoPhaseBusinessAction 的 commitMethod / rollbackMethod 方法名是否写对2. 检查 commit/rollback 方法是否为 public 且参数为 BusinessActionContext3. 检查是否被 Spring 管理( @Service / @Component) |
| 性能下降明显 | 1. AT 模式下 SQL 拦截解析有开销,大表/批量操作建议拆分或改用 TCC 2. 检查是否 SELECT FOR UPDATE 频繁等待全局锁3. 调整 client.rm.async-commit-buffer-limit 等参数 |
| 脏读问题 | AT 模式默认读未提交;如需读已提交,对关键查询加 FOR UPDATE,Seata 会自动申请全局锁 |
| Spring Cloud Alibaba 版本冲突 | 确认 spring-cloud-alibaba-dependencies 与 seata.version 兼容,参考官方版本说明 |