Seata、DTS、RocketMQ 事务消息到底怎么选?这篇文章以电商下单为例,把原理、用法和容易踩的坑一起讲清楚。

一、单体时代不用想的问题
单体应用里,下单、扣库存、扣余额、生成订单通常放在同一个本地数据库事务里,成功就一起成功,失败就一起回滚,基本不用额外操心。服务拆开之后,这四步落在不同的服务和数据库上,本地事务管不到那么远,原子性也就没了。
比如订单已经写进去了,库存服务却扣减失败。没有补偿机制的话,系统里就会留下“有订单、没扣库存”的脏数据。大促时这类问题会成倍放大,客服接到投诉,财务对账也对不上。
说白了,分布式事务要解决的问题是跨多个数据源、多个服务的操作在部分失败时,怎样让数据最终回到一致的状态。
二、阿里云上常见的三类能力
阿里云生态里和分布式事务相关的能力大致分三类,适用场景差别较大,不要混在一起用。
1. Seata 的 AT、TCC、Saga 三种模式
Seata 是阿里开源的分布式事务框架,EDAS、MSE 等阿里云产品里也常能看到它。它由 TC(事务协调器)、TM(事务管理器)和 RM(资源管理器)三个角色组成。
几种模式的取舍各不相同:
- AT 模式通过数据库代理自动生成反向 SQL 完成回滚,对业务代码侵入最小,大多数增删改查类的微服务场景都能直接用。
- TCC 模式由业务自己实现 Try、Confirm、Cancel 三个接口。性能和灵活性更好,代价是开发量大,还得处理空回滚、悬挂、幂等这几个老问题。
- Saga 模式给每个步骤配一个补偿动作,适合流程长、跨系统的业务,比如审批或履约流程。
2. RocketMQ 事务消息
RocketMQ 的事务消息靠“半消息、本地事务、回查”三步,保证本地数据库操作和消息发送要么都生效,要么都不生效。它适合异步解耦的场景,比如下单成功后通知积分、物流、营销等下游系统。
好处是链路短、吞吐高。不过它只保证“消息与本地事务”一致,下游有没有处理成功,还得靠消费端自己做幂等和重试。
3. DTS 数据同步
DTS(数据传输服务)主要做数据库之间的实时同步,常见于数据迁移、异地容灾和异构数据同步。它本身不是事务框架,但在“多库数据最终一致”的链路里,可以当作同步基础设施来用。
三、电商下单的一致性怎么做
假设一个电商系统拆成订单、库存、账户三个服务,下单流程大致是这样的,
- 订单服务创建订单,状态为“待支付”。
- 库存服务预扣库存。
- 账户服务冻结用户余额或优惠券。
- 三步都成功后,订单状态更新为“已确认”。
如果库存已经预扣成功,账户冻结却失败,库存就要回滚。用 Seata AT 模式的话,整个流程由 @GlobalTransactional 注解包起来,框架会记录各分支的前后镜像,失败时生成反向 SQL 回滚,业务代码基本不用动。
但如果库存服务用的是 Redis 或外部第三方接口,AT 模式就用不上了。这时可以改用 TCC:Try 阶段冻结库存,Confirm 阶段真正扣减,Cancel 阶段释放冻结量。代价是每个接口都要写三段逻辑,还要防住网络抖动带来的重复调用。
积分、营销、通知这类环节一般不需要同步返回结果,放进 RocketMQ 事务消息异步处理更合适,主链路不会被拖慢。
四、选型怎么判断
选型时可以从下面几个维度来看:
- 强一致还是最终一致要看业务能不能接受数据短暂偏差。资金、库存这类对一致性要求高的,优先考虑 TCC 或 AT;通知、积分这类能接受延迟的,走消息方案就够了。
- 数据源类型关系型数据库用 AT 最省心,遇到异构数据源或中间件,TCC 更合适。
- 性能AT 模式有全局锁和 undo 日志的开销。秒杀库存这种高并发热点行,上线前最好单独压测。
- 团队能力TCC 和 Saga 对设计和测试的要求更高,排期时要预留足够的设计和验证时间。
五、常见踩坑
实际项目里,下面几类问题出现得比较多:
- 空回滚指 Try 没执行,Cancel 却被触发了。Cancel 需要识别这种情况,直接返回成功。
- 悬挂是 Cancel 比 Try 先到。Try 之后就不该再执行,所以要记录事务状态做防护。
- 幂等问题在于 Confirm 和 Cancel 都可能被重复调用,必须基于事务 ID 去重。
- 全局锁冲突在 AT 模式下会出现,热点数据的全局锁等待会明显拉长响应时间。热点行可以拆分,或者改用 TCC。
- 超时与悬挂数据需要留意。全局事务超时后,中间状态可能长期留在库里,最好配一个兜底的巡检任务。
六、小结
分布式事务没有通用的标准答案。阿里云从 Seata 到 RocketMQ 再到 DTS 都有对应的能力,选型前先搞清楚业务对一致性的真实要求,再挑模式。落地时,还要根据技术栈和业务量级,细化超时策略、重试机制和监控告警。