09-分布式事务(Seata)
对应原始资料:
高级03-分布式事务、seata的部署和集成.md
一、为什么会有分布式事务
微服务架构下,一个业务操作跨多个服务、多个数据库:
下单 → 扣库存(库存服务) → 扣余额(账户服务) → 改订单状态(订单服务)任何一个失败,都需要全部回滚,否则数据不一致。这就是分布式事务问题。
二、CAP 与 BASE 理论
CAP
- C 一致性、A 可用性、P 分区容错。
- 分布式系统只能选 2 个,由于分区不可避免,实际在 CP(强一致)和 AP(高可用)间权衡。
BASE
- Basically Available 基本可用。
- Soft state 软状态。
- Eventually consistent 最终一致。
互联网业务大多追求最终一致,而非强一致。
三、分布式事务常见方案
| 方案 | 一致性 | 说明 |
|---|---|---|
| 2PC(两阶段提交) | 强 | 性能差、阻塞 |
| TCC(Try-Confirm-Cancel) | 强 | 业务侵入,需要每个操作写三套 |
| Seata AT 模式 | 弱强 | 自动补偿,业务无侵入(推荐) |
| 可靠消息最终一致 | 最终 | 本地消息表 + MQ |
| Saga | 最终 | 长事务,逐步提交/补偿 |
四、Seata 架构
三个角色
- TC(Transaction Coordinator):事务协调者,独立部署(seata-server)。
- TM(Transaction Manager):事务管理器,发起全局事务的服务。
- RM(Resource Manager):资源管理器,每个参与事务的数据库。
全局事务流程(AT 模式)
TM 调用 TC 开启全局事务(拿到 XID)
↓ XID 通过请求头传给各 RM
RM1 执行本地事务 + 记录 undo_log(用于回滚)→ 注册分支事务到 TC
RM2 执行本地事务 + 记录 undo_log → 注册分支事务到 TC
↓
TM 调用 TC:提交 or 回滚
TC 通知所有 RM:提交(异步删 undo_log)/ 回滚(根据 undo_log 反向补偿)五、Seata Server 部署
1. 准备表
每个业务库都要建 undo_log 表:
sql
CREATE TABLE undo_log (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
branch_id BIGINT, xid VARCHAR(100),
context VARCHAR(128), rollback_info LONGBLOB,
log_status INT, log_created DATETIME, log_modified DATETIME
);2. 启动 seata-server
bash
docker run -d --name seata -p 8091:8091 \
-e SEATA_IP=127.0.0.1 seataio/seata-server(资料中有详细的 seata的部署和集成.md,包括用 Nacos 作为注册中心、DB 存储模式等)
六、SpringBoot 集成(AT 模式)
1. 依赖
xml
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>2. 配置
yaml
seata:
registry:
type: nacos
nacos:
server-addr: localhost:8848
group: SEATA_GROUP
application: seata-server
tx-service-group: my_tx_group
service:
vgroup-mapping:
my_tx_group: default3. 使用
java
@Service
public class OrderService {
@Autowired private StorageClient storageClient;
@Autowired private AccountClient accountClient;
@Autowired private OrderMapper orderMapper;
@GlobalTransactional // 开启全局事务
public void createOrder(Order order) {
orderMapper.insert(order); // 本地:建订单
storageClient.deduct(order.getProductId()); // 远程:扣库存
accountClient.deduct(order.getUserId()); // 远程:扣余额
// 任何一步抛异常,全部回滚
}
}七、AT 模式的"读已提交"问题
AT 模式默认隔离级别是读未提交(全局提交前,本地事务已提交)。需要更强隔离可用 @GlobalLock + SELECT FOR UPDATE。
八、TCC 模式(了解)
需要业务自定义:
java
@LocalTCC
public interface AccountTcc {
@TwoPhaseBusinessAction(name = "deduct",
commitMethod = "confirm", rollbackMethod = "cancel")
boolean prepare(BusinessActionContext ctx, Long userId, BigDecimal money);
boolean confirm(BusinessActionContext ctx);
boolean cancel(BusinessActionContext ctx);
}性能高、对一致性要求高的场景使用。
九、可靠消息最终一致(替代方案)
适合异步的长流程业务:
订单服务 → MQ → 库存服务 → MQ → 积分服务- 本地消息表 / RocketMQ 事务消息保证「本地事务 + 发消息」原子性。
- 消费方幂等。
十、选型建议
| 场景 | 推荐 |
|---|---|
| 同公司、对一致要求高、业务短 | Seata AT |
| 业务极复杂、性能要求高 | TCC |
| 长流程、跨系统、可容忍延迟 | 可靠消息 |
| 老系统改造、不能改业务 | Saga |
练习建议
- 启动 seata-server,注册到 Nacos。
- 搭建「订单-库存-账户」三个服务,建 undo_log。
- 用
@GlobalTransactional完成下单流程,故意制造一个异常观察全局回滚。 - 对比 AT 模式和「可靠消息最终一致」的差异。