微服务拆分后,一个业务操作跨越多个服务、多个数据库——传统的数据库事务(BEGIN → COMMIT/ROLLBACK)只能管住单个数据库内部,跨服务的数据一致性需要新的方案。2PC(两阶段提交)追求强一致、TCC(Try-Confirm-Cancel)最终一致但侵入强、Saga(长事务拆分+补偿)适合长流程——三种方案覆盖了分布式事务的主要战场。本篇讲清它们的机制、适用场景与选型框架。
2PC:两阶段提交——强一致的代价
2PC(Two-Phase Commit)的流程分两个阶段。阶段一(Prepare/投票):协调者向所有参与者发送 prepare 请求,参与者执行事务但不提交,返回「可以提交」或「要回滚」。阶段二(Commit/Rollback):如果所有参与者都返回「可以提交」,协调者发送 commit;否则发送 rollback。
2PC 的问题集中在协调者的单点故障与同步阻塞。同步阻塞:参与者 prepare 后进入阻塞状态(持有锁、等待指令),协调者宕机时参与者永远阻塞——这是 2PC 最致命的缺陷(三阶段提交 3PC 加入超时机制试图缓解,但仍不能完全消除)。数据不一致窗口:阶段二协调者发送 commit 时部分参与者收到、部分没收到(网络分区),收到者提交、未收到者阻塞——数据不一致。性能:两轮网络往返 + 全程持有锁——吞吐量低。适用场景:对一致性要求极高、参与者数量少(2-3 个)、事务时长短(数据库层面的跨库事务,如 XA 协议的实现)。
TCC:Try-Confirm-Cancel——业务层的最终一致
TCC 的机制:将一个业务操作拆成三个阶段,由业务代码实现。Try——预留资源(冻结库存、预扣余额,不真正扣减);Confirm——确认执行(将冻结的库存真正扣减);Cancel——取消预留(解冻库存、恢复余额)。TCC 与 2PC 的本质区别:2PC 的 prepare/commit 由数据库执行(资源层),TCC 的 Try/Confirm/Cancel 由业务代码实现(业务层)——业务层的灵活性更高(可以定义业务语义的「预留」),但侵入性也更强(每个操作都要写三个方法)。
TCC 的三个经典问题。空回滚:Try 未执行(网络超时),Cancel 先到达——Cancel 需要识别「Try 未执行」并直接返回成功。悬挂:Cancel 执行完毕后,迟到的 Try 才到达——Try 需要检查「Cancel 已执行」并拒绝执行。幂等:Confirm 和 Cancel 可能被重试多次调用——必须保证幂等(重复调用不产生副作用)。这三个问题的解法都依赖「事务状态表」——记录每个事务的当前阶段,参与者根据状态决定行为。
Saga:长事务拆分 + 补偿——适合微服务的默认选择
Saga 的机制:将一个长事务拆分为多个本地事务(T1, T2, …, Tn),每个本地事务提交后立即生效(不预留资源)——如果某个 Ti 失败,依次执行前面步骤的补偿操作(C(i-1), C(i-2), …, C1)回滚效果。与 TCC 的区别:TCC 的 Try 是「预留」(不产生真实变更),Saga 的 Ti 是「直接执行」(产生真实变更)——Saga 的补偿操作需要「逆向执行」真实变更(扣了钱要退回来、加了库存要减回去)。Saga 的两种实现方式:编排式(Choreography)——每个服务监听前一步的事件并自行决定下一步(去中心化,但流程逻辑分散);协调式(Orchestration)——一个中央协调器驱动整个流程(集中管理,但协调器是单点)。Saga 适合的场景:流程长(涉及多个微服务)、参与方不配合改造(无法实现 Try/Confirm/Cancel)、最终一致性可接受——微服务架构下,Saga 是「默认的分布式事务方案」。
选型框架:三种方案的决策树
选型的第一问:能不用分布式事务吗?——最好的方案是「不需要分布式事务」(通过领域设计避免跨服务的原子操作——例如用消息队列异步解耦,接受最终一致)。第二问:一致性要求是什么?——强一致(金额不能出错且必须同时成功或失败)→ 2PC 或 TCC;最终一致(允许短暂不一致,如积分、通知)→ Saga 或消息队列。第三问:能否改造参与方?——能改造(可以实现 Try/Confirm/Cancel)→ TCC;不能改造(只提供标准数据库接口)→ 2PC 或 Saga。最后的务实建议:绝大多数微服务场景的最优解是「消息队列 + 本地事务 + 最终一致」——用本地事务保证「业务操作与消息发送」的原子性,用消息队列的重试保证下游最终执行——这不是传统的分布式事务方案,但解决了 90% 的实际问题。
消息队列的本地事务方案
分布式事务的最务实方案是本地消息表加消息队列——它不需要复杂的框架,只需要一个本地事务加一条消息。流程:在同一个本地数据库事务中完成业务操作加写入待发送消息到本地消息表——本地事务保证业务操作与消息写入的原子性。事务提交后,后台任务将消息表中的消息发送到消息队列,发送成功后标记为已发送。下游服务消费消息执行后续操作。这个方案的精髓:它不追求跨服务的原子性,而是保证本服务的操作与消息发送的原子性——消息发送出去之后,下游的最终一致由消息队列的重试机制保证。代价:需要一个消息表和清理策略、需要消息消费端的幂等设计。微服务架构下 90% 的分布式事务场景用本地消息表加 MQ 就够了。
Saga 的编排式与协调式的对比
Saga 的实现有两种模式,各有优劣。编排式(Choreography):没有中央协调器,每个服务在完成本地事务后发布事件,下游服务监听事件并触发自己的本地事务——例如「订单服务创建订单后发布 OrderCreated 事件,库存服务消费该事件并扣减库存,扣减成功后发布 InventoryDeducted 事件,支付服务消费该事件并扣款」。优势:去中心化(没有单点)、服务间松耦合。劣势:流程逻辑分散在多个服务中(理解完整流程需要看所有服务的代码)、事件链过长时难以追踪、循环依赖的风险。协调式(Orchestration):一个中央协调器(Saga Orchestrator)定义整个流程的步骤,依次调用各服务的本地事务,失败时按反向顺序调用补偿操作。优势:流程逻辑集中(一目了然)、支持复杂的分支与并行、易于监控与追踪。劣势:协调器是单点(需要高可用部署)、协调器与各服务之间的耦合增加。选型的建议:流程步骤少(2-3 步)且简单 → 编排式;流程步骤多或有分支 → 协调式——Seata 的 Saga 模式提供了状态机语言来定义协调式流程。
TCC 的「空回滚」「悬挂」「幂等」详解
TCC 的三个经典问题各有一个通用的解决方案。空回滚:Try 没执行但 Cancel 先到了(网络超时导致 Try 请求丢失,但事务管理器认为 Try 已发送)——解法:Cancel 时检查事务状态表——如果状态是「初始化」(Try 还没执行),直接返回成功(等同于空操作)。悬挂:Cancel 执行完毕后,迟到的 Try 才到达(Try 的网络延迟比 Cancel 大)——解法:Try 执行前检查事务状态表——如果状态已经是「已回滚」(Cancel 已执行),拒绝 Try。幂等:Confirm 和 Cancel 可能被重试框架多次调用——解法:每个 TCC 操作都设计为幂等(Confirm 用「状态从 Try 转为 Confirmed」的条件更新——已确认则不重复执行)。三个问题的通用解法都是依赖事务状态表——每个分布式事务在数据库中维护一行状态记录(事务 ID、当前阶段、参与方状态),所有 TCC 操作都基于状态表做前置检查——状态表是 TCC 方案的「锚」,没有它就无法解决三个经典问题。
柔性事务的终极方案:消息驱动的最终一致
分布式事务的最佳实践总结:用本地消息表加消息队列加幂等消费实现最终一致——这个方案覆盖了 90% 的微服务事务场景。这个方案的三个优势:性能好无分布式协调开销、可靠性高消息队列保证至少一次投递加消费端幂等、实现简单不需要引入分布式事务框架。剩余 10% 需要强一致的场景再考虑 TCC 或数据库层面的方案。微服务的架构哲学:能用最终一致就不要强一致——最终一致的用户体验差异通常可以忽略但强一致的性能代价是持续的。
TCC 的空回滚悬挂幂等详解
TCC 的三个经典问题各有一个通用的解决方案。空回滚:Try 没执行但 Cancel 先到了——解法是 Cancel 时检查事务状态表如果状态是初始化直接返回成功。悬挂:Cancel 执行完毕后迟到的 Try 才到达——解法是 Try 执行前检查事务状态表如果状态已经是已回滚则拒绝 Try。幂等:Confirm 和 Cancel 可能被重试多次调用——解法是每个 TCC 操作都设计为幂等。三个问题的通用解法都是依赖事务状态表——每个分布式事务在数据库中维护一行状态记录,所有 TCC 操作都基于状态表做前置检查——状态表是 TCC 方案的锚没有它就无法解决三个经典问题。
小结
分布式事务收成四条:2PC 追求强一致但阻塞(协调者单点故障时参与者永久阻塞),适合数据库层面的跨库事务;TCC 最终一致但侵入强(每个操作写三个方法),三个经典问题(空回滚/悬挂/幂等)依赖事务状态表解决;Saga 长事务拆分加补偿,适合微服务默认方案(编排式去中心化、协调式集中管理);最好的分布式事务是「不需要分布式事务」——消息队列加本地事务加最终一致解决 90% 的场景。分布式事务的选型不是技术竞赛,是业务权衡——一致性级别的选择,取决于你的业务能承受多大的不一致窗口。
参考资料
- 本库前篇:《一篇读懂消息不丢:MQ 可靠性的三方合同》《一文都懂微服务》
- Chris Richardson, 《微服务架构设计模式》第 4 章—— Saga 与分布式事务的权威讲解
- Seata 官方文档—— 国内最流行的分布式事务框架(支持 AT/TCC/Saga/XA)
读者留言
COMMENTS 暂无还没有留言,来说第一句?