分布式事务TCC/Seata/Saga到底选哪个?
用户下单场景,涉及三个服务,订单服务、积分服务、优惠券服务,假设积分扣了,优惠券核销了,但是最后一步订单创建失败,此时,积分和优惠券怎么回滚?
你项目里用了什么方案,为什么选他?
场景复现:微服务下的“数据分裂”
订单服务->连接order_db
积分服务->连接point_db
优惠券服务->连接Coupon_db
积分扣减成功(提交了)
优惠券核销成功(提交了)
订单插入失败(回滚了)
这就是典型的分布式问题
方案A:Seata(AT模式)---开发者的“后悔药”
Seata是阿里开源的分布式事务框架
其中AT(Auto Transaction)模式是最受欢迎的
1、代码示例:傻瓜式开发
只要在发起全局事务的方法上,加一个@GlobalTransactional注解
2、致命死穴:脏读与全局锁
Seata AT为了性能 在一阶段就释放了本地数据库锁
写隔离:Seata通过全局锁(Global Lock)防止脏写(高并发下竞争激烈,性能差)
读隔离:因为本地锁已释放,其他没有加Seata注解的普通事务,会读到一阶段已经修改但全局并未提交的数据(脏读)!
方案B:TCC--高并发下的“特种兵”
如果你的业务是电商核心交易链路,必须请出TCC(Try-Confirm-Cancel)
1、核心原理与代码:资源预留
TCC不依赖数据库事务,而是把业务拆分成三个阶段,需要再接口定义上明确这三个方法;
2、隐形成本:Schema侵入
最大的痛点是DBA很麻烦,必须改造业务表,积分表加frozen字段,库存表加frozen_stock字段,如果是老系统,该表结构风险极大。
方案C:Saga模式---第三方服务的“无奈之选”
"如果积分服务是第三方SaaS”,或者银行接口,你根本改不了数据库,怎么搞TCC?"
A:TCC搞不定,只能用Saga
原理:没有“预留(try)”阶段,直接“干(Do)”
正向操作:直接调用银行扣款接口
补偿操作:如果后续失败,调用银行退款接口进行补偿。
幂等性:Saga模式下,由于网络可能超时,正向操作(扣款)和补偿操作(退款)都必须支持幂等。
否则重试时可能导致多次扣款或多次退款。
终极对比:一张图看懂选型
| 维度 | Seata AT | TCC | Saga | MQ 事务消息 |
|---|---|---|---|---|
| 一致性 | 弱一致性(存在中间状态) | 最终一致性 | 最终一致性 | 最终一致性 |
| 并发性能 | 低(依赖全局锁) | 极高(锁粒度在行级) | 高 | 最高(异步) |
| 业务侵入 | 无侵入(注解即可) | 强侵入(写3个方法) | 中等(写补偿方法) | 弱侵入 |
| 表结构改造 | 需要Undo_Log表 | 需要增加冻结字段 | 无需 | 无需 |
| 适用场景 | 后台管理、B端业务 | 核心交易、秒杀扣减 | 老系统、第三方接口 | 充值、发货、通知 |
1、资产增加(如返积分):选MQ.允许延迟,异步性能最好。
2、资产扣减(内部核心系统):选TCC。强一致性,抗并发。虽然要改表结构,但是为了资金安全值得。
3、资产扣减(第三方/老系统):选Saga.无法改表结构,只能靠“补偿”;
4、后台/非核心业务:选SeataAT,开发效率优。
TCC落地的三大“地狱坑”
1、空回滚(Null Rollback)现象:Try请求丢包没执行,TC就发起了Cancel.
解法:事务控制表,Cancel执行前,查不到Try记录,直接返回成功。
2、悬挂(Hanging)现象:网络拥堵,Cancel比Try先到,Try到了后,如果不拦截,资金就被永久冻结了;
解法:Try执行时,查事务表,发现已有Cancel记录,直接拒绝;
3、幂等(Idempotency)现象:网络抖动,Confirm/Cancel被重试多次。
解法:基于全局事务ID做唯一索引校验。

浙公网安备 33010602011771号