分布式事务实战:Seata、TCC、本地消息表怎么选

6 阅读2分钟

一旦业务跨服务、跨库,“本地事务”就不够用了。

很多系统的问题不是没有事务方案,而是选错方案:

  • 该用最终一致,却追求强一致
  • 该用补偿模型,却硬上全局锁

本文给出一套务实决策:先定一致性目标,再选实现机制


【场景:下单 + 扣库存 + 营销发券】

一个订单链路可能涉及:

  1. 订单服务创建订单。
  2. 库存服务扣减库存。
  3. 营销服务发放优惠券。

如果第二步失败,第一步已成功,就会出现数据不一致。


【先定目标:不是所有场景都要强一致】

优先级建议:

  • 资金扣款、余额变更:强一致优先
  • 订单、库存、营销:多数场景可最终一致
  • 用户通知、积分变更:可异步补偿

一致性目标越高,系统复杂度和成本越高。


【方案1:Seata(AT/TCC)】

适用

  • 你已经是微服务 + 多数据源
  • 想要较快接入分布式事务能力

注意点

  • Seata AT 对 SQL 与数据库类型有约束
  • 全局事务链路长时,性能与锁冲突需要重点评估

【方案2:TCC(Try/Confirm/Cancel)】

适用

  • 核心链路要求高一致可控
  • 团队可接受较高开发成本

特点

  • 语义清晰,可控性强
  • 需要业务显式实现 Try/Confirm/Cancel
tryPhase.reserveStock(orderId, skuId, qty);
if (allSuccess) {
   confirmPhase.confirmStock(orderId);
} else {
   cancelPhase.releaseStock(orderId);
}

【方案3:本地消息表(Outbox)+ 异步补偿】

适用

  • 大部分电商、订单、履约场景
  • 接受最终一致,重视吞吐和解耦

核心流程

  1. 本地事务中同时写业务表 + outbox 表
  2. 异步任务投递消息到 MQ
  3. 消费方幂等处理
  4. 失败重试 + 死信补偿
transaction.execute(() -> {
   orderRepo.save(order);
   outboxRepo.save(new OutboxEvent("ORDER_CREATED", payload));
});

【如何选:一张决策表】

  • 强一致要求极高、资金类:优先 TCC。
  • 通用业务一致性、希望快速接入:评估 Seata。
  • 吞吐优先、可接受最终一致:本地消息表最稳。

不要追求“一个方案打天下”,可以分业务域混用。


【落地时最容易忽略的三件事】

  1. 幂等:消费端必须按业务键去重。
  2. 补偿:失败要有可重放、可人工介入的流程。
  3. 可观测:每个事务链路都要有 requestId/traceId。

【事务治理检查清单】

  1. 是否明确了该场景的一致性级别?
  2. 是否定义了失败补偿与回滚边界?
  3. 是否实现了消费端幂等?
  4. 是否配置了重试、死信、告警策略?
  5. 是否有链路追踪与审计日志?
  6. 是否做过故障演练(超时、重复、乱序)?

下期预告:

《从单体到微服务:一条可落地的迁移路线》