分布式事务:Seata AT/TCC、Saga 与本地消息表
更新时间:2026-09-18。微服务后一个业务动作横跨多个服务/数据库(下单要同时写订单库、扣库存库、扣余额),单机
BEGIN/COMMIT管不住它们。分布式事务没有免费午餐:强一致要付可用性与性能代价,高可用通常退回最终一致。本文对比 XA/2PC、Seata AT、TCC、Saga、本地消息表五类方案。选型速览见 FAQ 分布式事务方案怎么选,幂等底座见 接口幂等设计。
本文要回答的问题
- 为什么跨库不能直接用本地事务?2PC/XA 的协调者两阶段为什么有阻塞代价?
- Seata AT 的 undo_log、一阶段提交、全局锁是怎么做到「弱侵入强一致」的?
- TCC 的 Try/Confirm/Cancel 要处理哪些空回滚、悬挂、幂等问题?
- Saga 和本地消息表分别适合什么长流程/异步场景?
- 这些方案在一致性、性能、侵入性、适用场景上怎么选?
一、问题与 2PC/XA 基线

XA/2PC:第一阶段所有分支事务预提交并锁住资源,第二阶段统一提交或回滚。优点强一致;缺点是同步阻塞(跨库行锁持有到第二阶段)、协调者单点、并发吞吐低,互联网高并发 OLTP 很少用它扛主链路。
二、Seata AT:一阶段提交 + 自动补偿
AT 模式是 Seata 对关系型库的「自动版」方案,侵入很小(加代理 + undo_log 表):
一阶段(业务 SQL 正常执行,本地事务即提交,释放本地锁):
1. 解析 SQL,查询并保存变更前镜像 before image
2. 执行业务 SQL
3. 保存变更后镜像 after image
4. 生成 undo_log,与业务在同一本地事务提交
5. 向 TC(事务协调器) 注册并申请「全局锁」(逻辑锁, 在 TC 侧)
二阶段:
全部成功 -> TC 通知异步删除 undo_log(极快)
任一失败 -> 用 undo_log 的 before image 反向补偿回滚关键设计:本地事务在一阶段就提交、释放数据库行锁,只保留 TC 侧的全局行锁(保证其他全局事务不并发改同一行),所以比 XA 锁得短、性能好;回滚由框架根据 before/after 镜像自动生成反向 SQL。隔离级别默认读未提交级别,需要读已提交可用 SELECT FOR UPDATE(会拿全局锁)。代价:依赖关系型库与 SQL 解析、写 undo_log 有额外存储、极端情况下全局锁仍有竞争。
三、TCC:业务层的两阶段
TCC 不走数据库资源锁,而是把每个操作拆成业务语义的三步:
Try : 预留资源(冻结库存/冻结余额),不是真扣
Confirm: 确认执行(用冻结部分真正完成)
Cancel : 释放预留(解冻)下单:
Try: 冻结库存 1、冻结余额 100(都成功才进 Confirm)
Confirm: 扣减冻结、建单
任一 Try 失败/超时: Cancel 全部解冻它性能高、不长期持库锁、可跨非事务资源(账户、外部接口),但侵入性强,每个参与方都要实现三个接口,并正确处理三类异常:
- 空回滚:Try 因网络丢失没执行,Cancel 却到了——Cancel 要识别「没有预留记录」,正常返回而不是报错或错误释放。
- 悬挂:空回滚之后迟到的 Try 才到——必须拒绝这次 Try,否则资源被冻结却永远 Confirm/Cancel 不了。通常用事务记录表:Cancel 先插入 xid 记录,Try 发现已存在则放弃。
- 幂等:Confirm/Cancel 可能被重试,必须幂等(配合事务状态表),底座见 接口幂等设计。
四、Saga 与本地消息表:最终一致
Saga(长事务):把长流程拆成一系列本地事务 T1..Tn,每个 Ti 配一个补偿 Ci。正向一路走下去,某步失败则按相反顺序调用已完成步骤的补偿。适合跨多服务、耗时长、可接受中间态的业务流程(订票、供应链),没有资源锁定但隔离性弱(要业务上能接受并处理脏读/补偿逻辑,且有些动作不可逆——如发了短信无法真撤,只能再发更正)。
本地消息表 / 事务消息(最常用的最终一致):

业务数据和「待发送消息」在同一个本地事务里落库,保证「业务成了消息一定在」;后台扫描消息表投递 MQ,失败重试;消费端幂等。配合 binlog/CDC 还能不侵入业务地产生事件,见 CDC 数据同步。
五、方案对比与选型
| 方案 | 一致性 | 性能/锁 | 侵入性 | 适合 |
|---|---|---|---|---|
| XA/2PC | 强一致 | 差,跨库阻塞锁 | 低(数据源支持) | 传统、并发不高、必须强一致 |
| Seata AT | 接近强一致(全局锁) | 中,本地锁短 | 低(加 undo_log/代理) | 关系型库、可引入 TC、想少改代码 |
| TCC | 强(业务保证) | 好,短锁 | 高(三接口+异常处理) | 高并发、跨非事务资源、核心资金 |
| Saga | 最终一致、有中间态 | 好 | 中(要写补偿) | 长流程、多步编排、可补偿 |
| 本地消息表/事务消息 | 最终一致 | 好 | 中(消息表+幂等消费) | 大多数异步解耦、订单后置动作 |
工程原则:能最终一致就别强一致;下单后发券、通知、建索引、同步缓存用消息表/CDC;核心账户扣减且强隔离要求用 TCC;多为关系库、团队想低侵入可接受 Seata 运维成本用 AT;跨系统长编排用 Saga。无论哪种,幂等、空补偿、重试与对账都是必备配套。
六、坑与对照
- 把 XA 用在高并发热点。 两阶段持锁会拖垮吞吐。
- TCC 不处理空回滚/悬挂。 网络乱序下必然出资金悬挂事故,要 xid 事务记录三件套。
- AT 忽略全局锁竞争。 热点行多个全局事务仍会在 TC 排队。
- Saga 补偿不可行还硬做。 不可逆动作要设计人工兜底/对冲而非假装回滚。
- 消息表投递成功但回执丢失。 靠幂等消费与对账,不能假设恰好一次。
- 没有对账。 最终一致系统必须有定期对账与补偿任务兜底极端漏单。
理论边界(为什么 CP/AP 要取舍)见 CAP 与 BASE;缓存侧一致性见 缓存与数据库一致性。
总结
分布式事务:跨库无法用本地事务,XA/2PC 靠协调者两阶段锁资源实现强一致但同步阻塞、吞吐低;Seata AT 在一阶段就提交本地事务并在同事务写 undo_log、只保留 TC 全局行锁,失败时按 before 镜像自动补偿,关系库低侵入接近强一致;TCC 把操作拆成 Try 预留/Confirm 确认/Cancel 解冻,性能好可跨非事务资源但侵入强且必须幂等、防空回滚防悬挂;Saga 用正向本地事务+反向补偿适合可补偿的长流程、隔离性弱;最常用的是本地消息表/事务消息——业务数据与消息同事务落库、后台投递、消费端幂等,达成高吞吐最终一致并配对账兜底,原则是能最终一致就别上强一致。