一旦业务跨服务、跨库,“本地事务”就不够用了。
很多系统的问题不是没有事务方案,而是选错方案:
- 该用最终一致,却追求强一致
- 该用补偿模型,却硬上全局锁
本文给出一套务实决策:先定一致性目标,再选实现机制。
【场景:下单 + 扣库存 + 营销发券】
一个订单链路可能涉及:
- 订单服务创建订单。
- 库存服务扣减库存。
- 营销服务发放优惠券。
如果第二步失败,第一步已成功,就会出现数据不一致。
【先定目标:不是所有场景都要强一致】
优先级建议:
- 资金扣款、余额变更:强一致优先
- 订单、库存、营销:多数场景可最终一致
- 用户通知、积分变更:可异步补偿
一致性目标越高,系统复杂度和成本越高。
【方案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)+ 异步补偿】
适用
- 大部分电商、订单、履约场景
- 接受最终一致,重视吞吐和解耦
核心流程
- 本地事务中同时写业务表 + outbox 表
- 异步任务投递消息到 MQ
- 消费方幂等处理
- 失败重试 + 死信补偿
transaction.execute(() -> {
orderRepo.save(order);
outboxRepo.save(new OutboxEvent("ORDER_CREATED", payload));
});
【如何选:一张决策表】
- 强一致要求极高、资金类:优先 TCC。
- 通用业务一致性、希望快速接入:评估 Seata。
- 吞吐优先、可接受最终一致:本地消息表最稳。
不要追求“一个方案打天下”,可以分业务域混用。
【落地时最容易忽略的三件事】
- 幂等:消费端必须按业务键去重。
- 补偿:失败要有可重放、可人工介入的流程。
- 可观测:每个事务链路都要有 requestId/traceId。
【事务治理检查清单】
- 是否明确了该场景的一致性级别?
- 是否定义了失败补偿与回滚边界?
- 是否实现了消费端幂等?
- 是否配置了重试、死信、告警策略?
- 是否有链路追踪与审计日志?
- 是否做过故障演练(超时、重复、乱序)?
下期预告:
《从单体到微服务:一条可落地的迁移路线》