Appearance
Seata 学习指南
导航目录
- 一、Seata 概述
- 二、为什么需要分布式事务
- 三、Seata 核心角色与工作原理
- 四、Seata 部署与环境搭建
- 五、AT 模式
- 六、TCC、SAGA、XA 模式
- 七、Spring Cloud Alibaba 整合 Seata
- 八、常见问题与故障排查
- 九、面试重点
- 十、最佳实践
一、Seata 概述
1.1 什么是 Seata
Seata 是一套开源的分布式事务解决方案,主要用于解决微服务架构下多个服务、多个数据库之间的数据一致性问题。
- 支持 AT 模式
- 支持 TCC 模式
- 支持 SAGA 模式
- 支持 XA 模式
重点:Seata 解决的不是单库事务,而是“跨服务、跨库”的分布式事务问题。
1.2 Seata 能解决什么问题
例如下单场景:
- 订单服务创建订单
- 库存服务扣减库存
- 账户服务扣减余额
如果第二步或第三步失败,就需要回滚前面的操作,否则数据会不一致。
1.3 Seata 在微服务中的位置
text
客户端 -> Gateway -> order-service
|-> stock-service
|-> account-service
|-> Seata 协调全局事务二、为什么需要分布式事务
2.1 本地事务和分布式事务的区别
2.1.1 本地事务
- 一般只针对单个数据库连接
- 例如 Spring 的
@Transactional
2.1.2 分布式事务
- 涉及多个服务
- 涉及多个数据库
- 一个服务失败时,需要整体回滚或补偿
2.2 微服务为什么会引入事务一致性问题
- 服务拆分后数据分散
- 各服务独立数据库
- 调用链变长
- 网络不可靠
2.3 常见分布式事务方案
| 方案 | 特点 |
|---|---|
| 2PC / XA | 强一致,但性能压力大 |
| TCC | 侵入性较强,但控制精细 |
| SAGA | 长事务补偿,适合长流程 |
| AT | 开发成本低,适合常见业务 |
三、Seata 核心角色与工作原理
3.1 TC、TM、RM 是什么
3.1.1 TC
Transaction Coordinator- 事务协调者
- 负责维护全局事务状态,驱动提交或回滚
3.1.2 TM
Transaction Manager- 事务管理器
- 负责开启、提交、回滚全局事务
3.1.3 RM
Resource Manager- 资源管理器
- 管理分支事务资源,执行本地提交或回滚
3.2 全局事务和分支事务
- 全局事务:一个完整业务链路的事务
- 分支事务:每个微服务内部对应的本地事务
3.3 Seata 工作流程
text
TM 开启全局事务
-> TC 生成全局事务 XID
-> XID 在服务间传播
-> 各服务以 RM 身份注册分支事务
-> 所有分支成功则提交
-> 任一分支失败则全局回滚3.4 XID 是什么
- 全局事务唯一标识
- 会在服务调用链路中持续传播
重点:Seata 能否生效,很大程度上取决于 XID 是否正确传播。
3.5 XID 传播示意
text
order-service 开启全局事务
-> Seata 生成 XID
-> OpenFeign / HTTP 调用自动透传 XID
-> stock-service / account-service 获取 XID
-> 注册分支事务3.6 Seata 模式总览
| 模式 | 特点 | 侵入性 | 适用场景 |
|---|---|---|---|
| AT | 自动补偿,接入简单 | 低 | 常规数据库更新 |
| TCC | Try / Confirm / Cancel | 高 | 资金、库存冻结 |
| SAGA | 长事务补偿 | 高 | 长链路流程 |
| XA | 标准 2PC | 中 | 强一致数据库事务 |
四、Seata 部署与环境搭建
4.1 常见部署组成
- Seata Server
- 业务服务
- 注册中心
- 配置中心
- 事务日志库
4.2 Seata Server 启动
bash
# 启动 Seata Server
seata-server.batLinux:
bash
sh seata-server.sh4.3 Seata 与 Nacos 集成思路
通常会这样用:
- Nacos 作为注册中心
- Nacos 作为配置中心
- Seata Server 注册到 Nacos
4.4 Nacos 注册中心配置示例
properties
seata.registry.type=nacos
seata.registry.nacos.server-addr=127.0.0.1:8848
seata.registry.nacos.group=SEATA_GROUP
seata.registry.nacos.namespace=dev-namespace-id
seata.registry.nacos.application=seata-server4.5 配置中心示例
properties
seata.config.type=nacos
seata.config.nacos.server-addr=127.0.0.1:8848
seata.config.nacos.group=SEATA_GROUP
seata.config.nacos.namespace=dev-namespace-id4.6 事务日志表说明
AT 模式通常会依赖这些表:
undo_log
undo_log 用于记录回滚快照。
sql
CREATE TABLE undo_log (
id BIGINT NOT NULL AUTO_INCREMENT,
branch_id BIGINT NOT NULL,
xid VARCHAR(100) 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,
PRIMARY KEY (id),
UNIQUE KEY ux_undo_log (xid, branch_id)
);4.7 Seata Server 常见配置项
properties
# Seata Server 监听端口
server.port=7091
# 存储模式,可选 file / db / redis
seata.store.mode=db
# 事务日志库驱动
store.db.driver-class-name=com.mysql.cj.jdbc.Driver
# 事务日志库地址
store.db.url=jdbc:mysql://127.0.0.1:3306/seata
# 事务日志库用户名
store.db.user=root
# 事务日志库密码
store.db.password=1234564.8 数据库表准备说明
在实际项目中通常需要准备两类表:
- 业务库中的
undo_log - Seata Server 自己使用的事务日志表
五、AT 模式
5.1 什么是 AT 模式
AT 是 Seata 默认、最常见的模式。
特点:
- 开发成本低
- 对业务代码侵入较小
- 适合常见单库更新场景
5.2 AT 模式原理
AT 模式核心思路:
- 执行业务 SQL 前记录
before image - 执行业务 SQL 后记录
after image - 正常时提交本地事务
- 回滚时根据
undo_log生成反向 SQL
5.3 AT 模式执行流程
text
开启全局事务
-> 执行业务 SQL
-> 记录 undo_log
-> 分支事务注册
-> 全局提交 / 全局回滚5.4 AT 模式代码示例
java
@Service
public class OrderService {
@Resource
private StockFeignClient stockFeignClient;
@Resource
private AccountFeignClient accountFeignClient;
@GlobalTransactional(name = "create-order-tx", rollbackFor = Exception.class)
public void createOrder(Long userId, Long productId, Integer count, BigDecimal amount) {
// 1. 创建订单
saveOrder(userId, productId, count, amount);
// 2. 扣减库存
stockFeignClient.decrease(productId, count);
// 3. 扣减余额
accountFeignClient.decrease(userId, amount);
// 如果这里抛异常,前面的分支事务会被全局回滚
if (amount.compareTo(new BigDecimal("10000")) > 0) {
throw new RuntimeException("订单金额过大,模拟全局事务回滚");
}
}
private void saveOrder(Long userId, Long productId, Integer count, BigDecimal amount) {
// 这里只演示事务主流程,实际项目中可调用 mapper 持久化订单
}
}5.5 Feign 接口示例
java
@FeignClient(name = "stock-service")
public interface StockFeignClient {
@PostMapping("/stock/decrease")
void decrease(@RequestParam("productId") Long productId,
@RequestParam("count") Integer count);
}java
@FeignClient(name = "account-service")
public interface AccountFeignClient {
@PostMapping("/account/decrease")
void decrease(@RequestParam("userId") Long userId,
@RequestParam("amount") BigDecimal amount);
}5.6 下游库存服务示例
java
@RestController
@RequestMapping("/stock")
public class StockController {
@Resource
private StockService stockService;
@PostMapping("/decrease")
public String decrease(@RequestParam("productId") Long productId,
@RequestParam("count") Integer count) {
stockService.decrease(productId, count);
return "ok";
}
}java
@Service
public class StockService {
@Transactional(rollbackFor = Exception.class)
public void decrease(Long productId, Integer count) {
// 这里演示本地事务,实际项目中应执行 update stock set count = count - ? where product_id = ?
if (count <= 0) {
throw new RuntimeException("库存扣减数量非法");
}
}
}5.7 下游账户服务示例
java
@RestController
@RequestMapping("/account")
public class AccountController {
@Resource
private AccountService accountService;
@PostMapping("/decrease")
public String decrease(@RequestParam("userId") Long userId,
@RequestParam("amount") BigDecimal amount) {
accountService.decrease(userId, amount);
return "ok";
}
}java
@Service
public class AccountService {
@Transactional(rollbackFor = Exception.class)
public void decrease(Long userId, BigDecimal amount) {
// 这里演示本地事务,实际项目中应执行余额扣减 SQL
if (amount.compareTo(BigDecimal.ZERO) <= 0) {
throw new RuntimeException("扣减金额非法");
}
}
}5.8 DataSource 代理配置示例
AT 模式中,Seata 需要代理数据源,才能拦截 SQL 并记录 undo_log。
java
@Configuration
public class DataSourceProxyConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource dataSource() {
return new DruidDataSource();
}
@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
// 用 Seata 的 DataSourceProxy 包装原始数据源
return new DataSourceProxy(dataSource);
}
}5.9 AT 模式常见限制
AT 模式并不是万能的,常见限制包括:
- 不适合长事务
- 不适合复杂存储过程
- 不适合对同一行长期持锁的场景
- 批量复杂 SQL 需要格外小心
5.10 AT 模式优缺点
优点:
- 使用简单
- 接入成本低
- 适合大多数 CRUD 型业务
缺点:
- 依赖本地数据库支持
- 对复杂 SQL、批量更新、长事务不友好
5.11 AT 模式适用场景
- 电商下单
- 库存扣减
- 支付记账
- 常规后台管理系统
六、TCC、SAGA、XA 模式
6.1 TCC 模式
TCC 是:
TryConfirmCancel
6.1.1 TCC 特点
- 侵入性强
- 控制粒度细
- 一致性强
6.1.2 TCC 示例思路
text
Try: 预留库存 / 预冻结余额
Confirm: 正式扣减
Cancel: 回滚预留资源6.1.3 TCC 代码示例
java
@LocalTCC
public interface AccountTccAction {
@TwoPhaseBusinessAction(name = "decreaseAccount", commitMethod = "commit", rollbackMethod = "rollback")
boolean prepare(BusinessActionContext actionContext,
@BusinessActionContextParameter(paramName = "userId") Long userId,
@BusinessActionContextParameter(paramName = "amount") BigDecimal amount);
boolean commit(BusinessActionContext actionContext);
boolean rollback(BusinessActionContext actionContext);
}java
@Component
public class AccountTccActionImpl implements AccountTccAction {
@Override
public boolean prepare(BusinessActionContext actionContext, Long userId, BigDecimal amount) {
// Try 阶段:预冻结余额
return true;
}
@Override
public boolean commit(BusinessActionContext actionContext) {
// Confirm 阶段:正式扣减余额
return true;
}
@Override
public boolean rollback(BusinessActionContext actionContext) {
// Cancel 阶段:释放冻结余额
return true;
}
}6.2 XA 模式
XA 是标准 2PC 模式。
特点:
- 强一致性
- 数据库原生事务支持较强
- 性能和锁资源占用较重
6.3 SAGA 模式
SAGA 更适合长事务和状态机编排场景。
特点:
- 通过补偿事务回滚
- 更适合长链路业务
6.3.1 SAGA 思路示意
text
创建订单 -> 扣减库存 -> 扣减余额
如果余额扣减失败 -> 补偿库存 -> 补偿订单状态6.4 四种模式对比
| 模式 | 一致性 | 开发成本 | 性能 | 适用场景 |
|---|---|---|---|---|
| AT | 较高 | 低 | 较好 | 常规业务 |
| TCC | 高 | 高 | 好 | 核心资金类业务 |
| SAGA | 最终一致 | 高 | 好 | 长事务、复杂编排 |
| XA | 强一致 | 中 | 一般 | 强一致数据库事务 |
6.5 模式选择建议
- 常规业务优先 AT
- 资金类高一致场景可考虑 TCC
- 长流程业务可考虑 SAGA
- 数据库强一致诉求高时可考虑 XA
七、Spring Cloud Alibaba 整合 Seata
7.1 引入依赖
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<!-- Seata 集成依赖 -->
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>7.2 应用配置示例
yaml
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: order_tx_group
service:
vgroup-mapping:
# 事务组映射到 Seata 默认事务分组
order_tx_group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: dev-namespace-id
application: seata-server
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: dev-namespace-id7.3 Spring Boot 完整配置示例
yaml
server:
port: 8088
spring:
application:
name: order-service
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/order_db?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
username: root
password: 123456
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: order_tx_group
data-source-proxy-mode: AT
service:
vgroup-mapping:
order_tx_group: default
registry:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: dev-namespace-id
application: seata-server
config:
type: nacos
nacos:
server-addr: 127.0.0.1:8848
group: SEATA_GROUP
namespace: dev-namespace-id7.4 数据源代理说明
Seata AT 模式通常需要代理数据源。
yaml
seata:
data-source-proxy-mode: AT7.5 全局事务注解说明
java
@GlobalTransactional(name = "order-create-tx", rollbackFor = Exception.class)说明:
name:全局事务名rollbackFor:指定哪些异常触发回滚
7.6 XID 查询示例
java
@RestController
@RequestMapping("/xid")
public class XidController {
@GetMapping("/current")
public String currentXid() {
// 查看当前线程中的全局事务 XID
return RootContext.getXID();
}
}7.7 一套完整业务链路
text
order-service 开启 @GlobalTransactional
-> stock-service 扣减库存
-> account-service 扣减余额
-> 任一步失败则 Seata 全局回滚重点:Spring Cloud Alibaba 项目里,Seata 一般会和 Nacos、OpenFeign 一起使用。
八、常见问题与故障排查
8.1 全局事务不回滚怎么办
重点排查:
- 是否加了
@GlobalTransactional rollbackFor是否覆盖异常类型- 下游异常是否被吃掉
- XID 是否传播成功
undo_log表是否存在
8.2 Feign 调用后事务不生效怎么办
- 检查 Feign 是否正确传播 XID
- 检查下游服务是否接入 Seata
- 检查事务组配置是否一致
8.3 TCC 模式为什么更复杂
- 需要自己实现 Try / Confirm / Cancel
- 需要保证幂等
- 需要处理悬挂、空回滚等问题
8.4 undo_log 有什么作用
- 记录回滚快照
- 帮助 AT 模式生成反向 SQL
8.5 Seata 和本地事务怎么配合
- 局部服务内部依然使用本地事务
- Seata 管理的是全局事务协调
8.6 常见故障点
- 注册中心配置错误
- 配置中心配置错误
- 事务组映射错误
- 数据源没有代理
- 数据库表缺失
- XID 没有透传
- 异常被业务代码吞掉
九、面试重点
9.1 高频问题
- 什么是分布式事务
- Seata 的 TC、TM、RM 分别是什么
- AT 模式原理是什么
- 为什么需要 undo_log
- AT、TCC、SAGA、XA 的区别
- Seata 和 Spring Cloud Alibaba 怎么整合
- XID 是什么,如何传播
- 为什么 AT 模式需要数据源代理
9.2 高频结论
- AT 模式最常用
- TCC 控制最精细,但开发成本高
- SAGA 更适合长事务
- XA 更偏强一致,但性能压力更大
十、最佳实践
10.1 模式选择建议
- 普通业务优先
AT - 强一致资金场景优先
TCC - 长事务场景考虑
SAGA
10.2 开发建议
- 异常不要随意吞掉
- 全局事务范围不要过大
- 避免长事务
- 关键链路做好幂等
10.3 生产建议
- Seata Server 做高可用部署
- 注册中心和配置中心统一接入 Nacos
- 监控事务失败率和回滚率
- 定期检查
undo_log - 控制全局事务粒度,避免长事务
- 下游服务异常不要吞掉
10.4 总结
Seata 是 Spring Cloud Alibaba 体系里分布式事务的重要组件。
- 入门阶段要掌握分布式事务基本概念
- 进阶阶段要掌握 AT 模式与 Spring Cloud Alibaba 整合
- 高阶阶段要掌握 TCC、SAGA、XA 与生产实践