1. 为什么我们需要分布式事务?
在单体应用时代,事务管理相对简单。我们熟悉的ACID特性(原子性、一致性、隔离性、持久性)通过数据库本身就能很好实现。但随着微服务架构的普及,一个业务操作往往需要跨多个服务完成,这就带来了分布式事务的挑战。
想象一个电商场景:用户下单后,需要同时扣减库存、生成订单、增加积分。这三个操作可能分别属于库存服务、订单服务和会员服务。如果其中某个步骤失败,如何保证数据的一致性?这就是分布式事务要解决的核心问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Seata是什么?
Seata(Simple Extensible Autonomous Transaction Architecture)是阿里巴巴开源的分布式事务解决方案。它提供了AT(自动补偿)、TCC(Try-Confirm-Cancel)、SAGA和XA四种模式,能够满足不同场景下的分布式事务需求。
Seata的架构包含三个核心组件:
- TC(Transaction Coordinator):事务协调器,维护全局事务的运行状态
- TM(Transaction Manager):事务管理器,定义全局事务的边界
- RM(Resource Manager):资源管理器,管理分支事务
3. Seata的AT模式详解
AT模式是Seata最常用的模式,它的工作原理可以分为两个阶段:
3.1 第一阶段:业务执行+本地事务提交
- TM向TC发起全局事务
- 业务SQL执行前,Seata会拦截SQL,解析语义,生成前置镜像(before image)
- 执行业务SQL
- 生成后置镜像(after image)
- 向TC注册分支事务
- 本地事务提交
3.2 第二阶段:全局提交或回滚
- 如果所有分支事务都成功,TC会通知各RM异步删除undo_log
- 如果有分支事务失败,TC会通知各RM根据undo_log进行补偿
注意:AT模式依赖于数据库的本地事务能力,因此只支持支持本地事务的关系型数据库。
4. 搭建Seata开发环境
4.1 服务端安装
- 下载Seata Server(建议1.4.2版本)
- 解压后修改conf/file.conf和conf/registry.conf
- 初始化数据库(需要创建undo_log表)
- 启动服务端:bin/seata-server.sh
4.2 客户端配置
在Spring Boot项目中添加依赖:
xml复制<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.4.2</version>
</dependency>
配置application.yml:
yaml复制seata:
enabled: true
application-id: ${spring.application.name}
tx-service-group: my_test_tx_group
service:
vgroup-mapping:
my_test_tx_group: default
grouplist:
default: 127.0.0.1:8091
5. 实战:订单-库存分布式事务Demo
5.1 场景设计
我们模拟一个简单的下单场景:
- 订单服务创建订单
- 库存服务扣减库存
- 如果任一服务失败,整个事务回滚
5.2 代码实现
在订单服务中:
java复制@GlobalTransactional
public void createOrder(OrderDTO orderDTO) {
// 1. 创建订单
orderMapper.create(orderDTO);
// 2. 扣减库存(远程调用)
storageFeignClient.deduct(orderDTO.getCommodityCode(), orderDTO.getCount());
// 3. 模拟异常
if (orderDTO.getCount() > 100) {
throw new RuntimeException("库存不足");
}
}
在库存服务中:
java复制public void deduct(String commodityCode, int count) {
// 检查库存
Storage storage = storageMapper.selectByCommodityCode(commodityCode);
if (storage.getCount() < count) {
throw new RuntimeException("库存不足");
}
// 扣减库存
storageMapper.deduct(commodityCode, count);
}
5.3 关键点说明
- @GlobalTransactional注解标记了全局事务的边界
- 每个服务都需要配置Seata数据源代理
- 数据库表需要有主键,Seata依赖主键生成undo_log
6. 常见问题与解决方案
6.1 事务失效问题
可能原因:
- 异常被捕获未抛出
- 方法不是public
- 同类方法调用(AOP失效)
解决方案:
- 确保异常能传播到全局事务管理器
- 使用public方法
- 避免同类调用,通过其他Bean调用
6.2 性能优化建议
- 减少全局事务范围,只包含必要的操作
- 合理设置事务超时时间
- 在高并发场景考虑使用TCC模式
- 适当调整Seata server的线程池大小
7. Seata与其他分布式事务方案对比
| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 |
|---|---|---|---|---|
| Seata AT | 最终一致 | 高 | 低 | 大部分业务场景 |
| Seata TCC | 强一致 | 中 | 高 | 资金、金融类业务 |
| SAGA | 最终一致 | 高 | 中 | 长事务、跨系统集成 |
| XA | 强一致 | 低 | 低 | 传统数据库事务 |
8. 生产环境部署建议
- 高可用部署:Seata Server建议至少3节点集群
- 存储模式:生产环境建议使用DB模式而非file模式
- 监控:集成Prometheus监控事务成功率等指标
- 日志:配置合理的日志级别,避免日志量过大
我在实际项目中使用Seata的经验是,对于大部分业务场景,AT模式已经足够。但在金融等高一致性要求的场景,TCC模式更为可靠。关键是要根据业务特点选择合适的模式,而不是盲目追求技术先进性。
