Saga模式是Go微服务最务实的分布式事务方案,需状态持久化、幂等操作及明确重试边界;TCC适合强一致性但成本高;最终一致性+补偿任务是多数场景的底线选择。

本地事务无法跨服务,必须放弃“单体式”思维
微服务架构下,database/sql 的 Begin/Commit 只能保证单个数据库内的一致性。一旦订单服务写入 MySQL、库存服务写入 PostgreSQL、支付服务调用第三方 HTTP 接口,传统 ACID 就彻底失效——这不是 Go 语言的限制,而是分布式系统的基本约束。
强行用 Two-Phase Commit(2PC) 协调多个服务?Go 生态几乎没有成熟可靠的开源实现,且会引入严重性能瓶颈和单点故障风险。实际项目中应直接排除该路径。
Saga 模式是 Go 微服务最务实的选择
Saga 把一个分布式事务拆成一系列本地事务,每个步骤都有对应的补偿操作(Compensating Action)。Go 实现时关键在两点:状态持久化 + 明确的失败重试边界。
- 用
state machine(如go-statemachine或自定义状态字段)记录当前执行到哪一步,避免重复提交或漏执行 - 每个正向操作(如
deductInventory())和补偿操作(如restoreInventory())都必须是幂等的;建议在 DB 加唯一索引或用WHERE status = 'pending'做条件更新 - 不要依赖内存状态或临时 channel 传递事务上下文;所有中间状态必须落库(例如存进
transaction_log表),否则服务重启就丢失进度
TCC 模式适合强一致性要求但开发成本高
TCC(Try-Confirm-Cancel)比 Saga 更严格:先预留资源(Try),再统一确认(Confirm)或全部回退(Cancel)。Go 中实现要注意:
立即学习“go语言免费学习笔记(深入)”;
-
Try阶段不能真正扣减,比如库存要写入inventory_freeze字段而非直接减inventory_total -
Confirm和Cancel必须设计为无业务逻辑的纯状态变更,否则可能引发二次异常 - 超时处理极其关键:若 Confirm 超时,需启动定时任务扫描未终态事务并人工介入;Go 的
time.AfterFunc不适合做这个,要用带持久化的调度器(如asynq或entgo+ cron job)
最终一致性 + 补偿任务是大多数场景的底线方案
不是所有业务都需要“立刻一致”。下单成功后异步发消息触发库存扣减,失败时由独立的补偿服务扫描 order_status = 'created' 且 inventory_updated = false 的订单并重试——这种模式在 Go 中最容易落地。
重点在于监控和可观测性:
- 给每个补偿任务加
max_retry = 3和指数退避(如time.Second * 1 ) - 把失败日志打到
ELK或Loki,并设置inventory_compensation_failed_count指标告警 - 避免在 HTTP handler 里直接调用补偿逻辑;用
github.com/hibiken/asynq这类异步队列解耦,防止主链路阻塞
真正的难点不在代码怎么写,而在于怎么定义“可接受的不一致窗口期”,以及当补偿也失败时,是否有人工兜底流程。这些往往比选哪种分布式事务模型更重要。










