先明确一个核心前提
分布式事务要解决的是:一个业务操作跨了多个数据库或服务,如何保证数据一致性?
解决方案分为两大阵营:
-
刚性事务(强一致性):Seata 的 AT、TCC、XA 模式。追求数据实时一致,所有服务要么一起成功,要么一起失败。
-
柔性事务(最终一致性):本地消息表、MQ 事务消息。允许短暂不一致,通过重试保证数据最终一定对齐。
本地消息表
核心思想 :在本地业务数据库里建一张 outbox 表,把"写业务数据"和"写待发送消息"放在同一个本地事务里。业务成功后,用一个定时任务去扫 outbox 表,把消息发给 MQ,发送成功后再删除或标记这条消息。
特点:
优点:实现简单,不依赖特殊 MQ,和业务代码耦合低。
缺点:需要自己写定时任务和异常处理;消息可能重复发送,消费者必须幂等;有延迟,定时任务扫描间隔决定了消息的延迟时间。
适用场景:公司没有 RocketMQ 这类支持事务消息的 MQ,只能用 RabbitMQ 或 Kafka,但又需要保证消息一定发出去。

MQ 事务消息
核心思想:利用 RocketMQ 原生的事务消息机制,把"发送消息"和"执行本地事务"绑定在一起。先发半消息(消费者不可见),再执行本地事务,根据本地事务结果决定半消息是提交(消费者可见)还是回滚(丢弃)。
特点:
优点:消息延迟低(实时性远高于本地消息表),不需要自己写定时任务和 outbox 表,RocketMQ 自带回查机制保证最终一致性。
缺点:必须用 RocketMQ(RabbitMQ 和 Kafka 原生不支持);消费者必须幂等。
适用场景:公司有 RocketMQ,且对数据一致性的实时性要求高------比如下单后要立刻扣库存,不能等几秒后才发消息。

Seata AT 模式
核心思想:Seata 介入你的 SQL 执行过程,在事务提交前自动记录回滚日志(undo_log 表)。如果需要回滚,Seata 根据 undo_log 自动生成反向 SQL,把数据恢复原状。你的代码不需要写任何回滚逻辑。
特点:
优点 :对业务代码零侵入(只需一个
@GlobalTransactional注解),自动生成回滚日志,自动回滚。缺点 :依赖 Seata Server(需要额外部署);性能损耗比本地事务高;不支持 Redis、MongoDB 等非关系型数据库;不支持
SELECT ... FOR UPDATE这类显式加锁的 SQL。

适用场景:Java 微服务项目,数据库全部是 MySQL(或其他关系型数据库),不想侵入业务代码,想用注解自动管理分布式事务。
Seata TCC 模式
核心思想:TCC 把每个服务需要做的事情拆成三步:Try(锁定资源)、Confirm(正式执行)、Cancel(释放资源)。所有 Try 都成功后,统一 Confirm;任何一个 Try 失败,统一 Cancel。这是业务层面的补偿,不是数据库层面的回滚。
特点:
优点:性能更高(没有全局行锁),支持非数据库资源(Redis、文件系统等),业务可控性强。
缺点:代码侵入性极高,每个接口都需要实现 Try、Confirm、Cancel 三个方法;开发工作量大,容易出错。
适用场景:对性能要求极高的场景(比如秒杀扣库存),或者涉及非数据库资源(需要同时操作 Redis 和 MySQL),或者公司已经有 TCC 框架沉淀。

Seata XA 模式
核心思想:利用数据库自身的 XA 协议(两阶段提交)。Seata 不在 SQL 层面做拦截,而是把分布式事务完全交给数据库去管理。第一阶段各数据库执行 SQL 但不提交(Prepare),第二阶段 Seata 通知所有数据库统一提交或回滚(Commit/Rollback)。
特点:
优点:对业务代码几乎零侵入;强一致性,数据绝不脏读。
缺点:性能最差(全局锁持有时间长,并发吞吐量断崖式下降);依赖数据库支持 XA 协议(MySQL 5.7+ 支持);不支持 Redis 等非数据库资源。

适用场景:对数据一致性要求极高(比如金融、会计系统),但对性能要求不高,且所有资源都是支持 XA 协议的关系型数据库。在实际生产中很少使用,因为性能代价太大。
Seata Saga 模式
核心思想是把一个长事务拆成多个短事务,每个短事务都有自己的补偿操作。如果某个步骤失败,不再回滚已经成功的步骤,而是按顺序反向执行补偿操作。
其优点 是不依赖全局锁和 undo_log,适合长事务(如需要人工审核的订单流程);缺点是必须为每个正向操作编写反向补偿逻辑。
这种方式适用场景是业务流程长、需要人工干预或调用外部系统(如银行接口)。

总结
没有银弹。刚性事务(AT、TCC、XA)追求强一致性,但各有代价;柔性事务(本地消息表、MQ 事务消息)允许短暂不一致,但通过重试保证最终对齐。架构设计的本质是权衡------你在选择方案的同时,也是在选择你愿意为数据一致性付出多大的性能代价。