Seata分布式事务深度解析笔记
一、核心概念澄清
1.1 本地事务 vs 分布式事务
本地事务:
- 范围:单个数据库连接内
- 特性:ACID(原子性、一致性、隔离性、持久性)
- 支持数据库:几乎所有现代关系型数据库(MySQL/InnoDB、PostgreSQL、Oracle、SQL Server等)
- Spring事务管理的就是本地事务
分布式事务:
- 范围:跨多个服务/数据库
- 问题:多个本地事务如何作为一个整体保证原子性
- 解决方案:Seata等分布式事务框架
1.2 Seata AT模式 vs Spring事务
关键区别:
// Spring事务 - 只能管理单个服务
@Transactional
public void createOrder(Order order) {orderMapper.insert(order); // ✅ 能回滚inventoryService.deductStock(...); // ❌ 远程调用,无法回滚
}// Seata AT模式 - 跨服务事务管理
@GlobalTransactional
public void createOrder(Order order) {orderMapper.insert(order); // ✅ Seata生成UNDO_LOGinventoryFeignClient.deductStock(...); // ✅ 通过XID传递实现分布式事务
}
AT模式魔法:
- XID传递:通过拦截器在微服务间传递全局事务ID
- DataSource代理:自动生成和应用UNDO_LOG
- TC协调:Seata Server统一协调所有参与者
二、Seata四种模式深度对比
2.1 AT模式(自动模式)
核心机制:一阶段提交+UNDO_LOG,二阶段异步删除/回滚
-- 业务操作
UPDATE products SET stock = stock - 10 WHERE id = 1;-- Seata自动生成UNDO_LOG
INSERT INTO undo_log VALUES ('before', 100, 'after', 90);
适用场景:跨微服务的CRUD操作(前提是其他微服务也接入了Seata)
优点:无代码入侵、开发效率高
缺点:有全局锁、性能有损耗
2.2 TCC模式(手动模式)
核心机制:Try-Confirm-Cancel三阶段
public interface CouponTccAction {boolean tryGrant(Long userId, String couponType); // 预占资源boolean confirmGrant(BusinessActionContext context); // 确认操作boolean cancelGrant(BusinessActionContext context); // 取消操作
}
适用场景:
- 高并发核心业务
- 需要调用外部系统(如无法接入seata的应用、服务,或第三方厂商提供api等)
- 复杂业务逻辑控制
优点:性能好、无锁、可处理非数据库操作
缺点:代码入侵强、需要手动实现补偿逻辑
2.3 Saga模式
核心机制:一阶段直接提交,失败时按相反顺序执行补偿
{"Steps": [{"Name": "bookFlight","ServiceName": "flightService","ServiceMethod": "book","CompensateService": "flightService", "CompensateMethod": "cancelBook"}]
}
适用场景:长事务、最终一致性可接受的业务流程
优点:适合长时间运行流程、吞吐量高
缺点:隔离性弱、可能读到中间状态
2.4 XA模式
核心机制:标准XA协议,一阶段不提交,二阶段统一提交/回滚
适用场景:需要与XA协议集成的传统系统
优点:强一致性、标准协议
缺点:性能差、资源锁定时间长
三、关键问题深度解析
3.1 AT模式为什么不能处理外部调用?
根本原因:AT的回滚基于数据库UNDO_LOG
AT能回滚的:
- ✅ UPDATE/INSERT/DELETE等SQL操作
- ✅ 单个数据库的事务
AT不能回滚的:
- ❌ HTTP API调用
- ❌ 消息队列发送
- ❌ 文件操作
- ❌ 外部系统调用
- ❌ 短信/邮件发送
3.2 TCC中手动回滚逻辑出错怎么办?
严重性:补偿逻辑BUG会导致数据永久不一致
防御措施:
- 严格的单元测试
- 补偿逻辑的幂等性设计
- 业务状态机验证
- 完善的监控告警
3.3 能否混合使用AT和TCC模式?
答案:可以且推荐!
混合架构示例:
@GlobalTransactional
public void createComplexOrder(Order order) {// AT模式:简单的数据库操作orderMapper.insert(order);inventoryMapper.deductStock(...);// TCC模式:复杂业务/外部调用couponTccService.tryReserve(...);pointsTccService.tryDeduct(...);
}
3.4 Saga vs TCC 核心区别
方面 | TCC模式 | Saga模式 |
---|---|---|
一阶段 | 不提交,预占资源 | 直接提交事务 |
隔离性 | 强(业务预留) | 弱(可能读到中间状态) |
适用场景 | 短事务、强一致性 | 长事务、最终一致性 |
3.5 为什么不能所有服务都用Seata AT?
现实约束:
- 技术可行性:外部系统、第三方服务无法引入Seata
- 业务可行性:短信、邮件等操作本质上无法回滚
- 组织可行性:跨团队系统无法统一技术栈
- 架构合理性:长事务不适合AT模式
四、选型决策指南
4.1 模式选择矩阵
场景特征 | 推荐模式 | 理由 |
---|---|---|
简单的跨服务CRUD | AT模式 | 开发效率高,无代码入侵 |
高并发核心业务 | TCC模式 | 性能好,无锁竞争 |
调用外部系统 | TCC模式 | AT无法处理非数据库操作 |
业务流程很长 | Saga模式 | 适合长时间运行流程 |
需要强一致性 | TCC模式 | 业务预留保证隔离性 |
可接受最终一致性 | Saga模式 | 补偿机制处理异常 |
4.2 实际应用建议
- 默认选择AT模式:适用于大部分跨微服务CRUD场景
- 复杂业务用TCC:当涉及外部调用或需要精细控制时
- 长流程用Saga:当业务流程可能持续较长时间时
- 混合使用:根据业务特性在同一个应用中混合使用不同模式
五、常见误区澄清
误区1:Seata AT可以替代Spring事务
纠正:Seata AT是Spring事务的分布式扩展,不是替代
误区2:TCC中可以在Try阶段提交事务
纠正:这违背TCC设计哲学,会失去原子性保证
误区3:Saga模式可以不提交事务
纠正:这会让Saga退化成TCC,失去其应对长事务的核心价值
误区4:所有服务都用Seata AT就能解决所有问题
纠正:外部系统调用、非数据库操作等场景必须使用TCC
六、最佳实践总结
- 理解业务需求:根据业务特性选择合适的事务模式
- 设计事务边界:合理划分分布式事务的范围
- 保证幂等性:特别是TCC的Confirm/Cancel方法
- 完善监控:分布式事务的可见性很重要
- 混合架构:根据场景灵活使用AT+TCC组合