2. 强一致性方案之2PC:两阶段提交协议详解

聊到分布式事务,2PC 绝对是个绕不开的话题。我刚开始接触分布式系统时,第一个学的强一致性方案就是它。说白了,两阶段提交就是让多个节点在同一个事务里「要么全做,要么全不做」。

你想想看,在单机数据库里,我们靠本地事务和锁就能搞定一致性。但到了分布式环境,数据散落在不同机器上,怎么保证大家步调一致?2PC 就是最早被广泛使用的答案。

2.1 什么是两阶段提交?

两阶段提交(Two-Phase Commit,简称 2PC),是一种原子提交协议。它确保所有参与者节点要么全部提交事务,要么全部回滚。

整个协议分为两个阶段:

  • 第一阶段:投票阶段(Voting Phase)——协调者问大家「能不能提交?」
  • 第二阶段:执行阶段(Execution Phase)——协调者告诉大家「提交」或「回滚」

嗯,这里要注意:2PC 是一个阻塞协议。什么意思?就是参与者拿到锁之后,在第二阶段结束前,资源一直锁着。我在项目中遇到过因为 2PC 锁超时导致整个链路雪崩的情况,后面会细说。

核心思想:引入一个协调者(Coordinator),统一管理所有参与者(Participant)的事务生命周期。

2.2 协调者与参与者角色

2PC 里只有两种角色,但各自承担的责任完全不同。

协调者(Coordinator)

协调者是整个事务的「大脑」。我个人习惯把协调者设计成独立服务,而不是和业务服务混在一起。为什么呢?因为协调者挂了,整个事务就卡住了。

  • 发起事务请求
  • 收集投票结果
  • 做出全局决策(提交/回滚)
  • 处理超时和异常

参与者(Participant)

参与者是真正干活的人。每个参与者负责操作自己的本地资源,比如更新数据库、写消息队列。

  • 执行本地事务(但不提交)
  • 反馈投票结果(Yes/No)
  • 根据协调者指令执行提交或回滚
我的经验:参与者在第一阶段执行完操作后,一定要把 undo/redo 日志写进本地磁盘。我曾经遇到参与者收到提交指令后突然宕机,重启后靠 redo 日志恢复了数据,才没丢数据。

2.3 2PC 的完整流程

光说概念太抽象,我们画个流程图看看。

2PC 两阶段提交流程图 协调者 参与者A 参与者B 发送准备请求 prepare prepare 执行本地事务 执行本地事务 Yes Yes 第一阶段:投票阶段 发送提交指令 commit commit 提交事务 提交事务 ack ack 第二阶段:执行阶段 异常情况:任意参与者返回 No 或超时 发送回滚指令 rollback rollback 回滚事务 回滚事务

从图上可以清楚看到:

  1. 协调者先给所有参与者发 prepare 请求
  2. 每个参与者执行本地事务,写日志,但不提交
  3. 参与者回复 Yes 或 No
  4. 协调者收集所有回复:全 Yes 就发 commit,有 No 就发 rollback
  5. 参与者收到指令后执行提交或回滚
⚠️ 注意:第二阶段协调者发出 commit 后,如果某个参与者宕机了,它重启后必须根据本地日志自动完成提交。这就是为什么我前面强调要写 redo 日志。

2.4 2PC 的优缺点分析

任何方案都有两面性。2PC 看起来简单,但实际用起来坑不少。我结合自己的踩坑经历,给你梳理一下。

优点

  • 原理简单,容易理解——相比 Paxos、Raft 这些协议,2PC 的逻辑非常直白
  • 强一致性保障——要么全部提交,要么全部回滚,数据不会出现部分写入
  • 实现成本低——很多中间件(如 Atomikos、Seata)都内置了 2PC 支持
  • 兼容性好——只要支持 XA 协议的数据库都能直接接入

缺点

  • 同步阻塞问题——参与者持有资源锁,直到第二阶段结束才释放。高并发下容易死锁
  • 单点故障风险——协调者挂了,整个事务卡住。参与者只能一直等,或者超时回滚
  • 数据不一致风险——第二阶段协调者发 commit 时自己宕机了,部分参与者收到 commit,部分没收到,数据就不一致了
  • 性能瓶颈——两轮网络交互,延迟翻倍。我测过,跨机房场景下 2PC 的 TPS 比单机事务低 5-10 倍
维度 评价 说明
一致性 ⭐⭐⭐⭐⭐ 强一致性,但存在极端不一致风险
可用性 ⭐⭐ 协调者单点,参与者阻塞
性能 ⭐⭐ 两轮 RTT,锁持有时间长
实现复杂度 ⭐⭐⭐ 协议简单,但异常处理复杂
适用场景 ⭐⭐⭐ 短事务、低并发、强一致性要求
避坑指南:我曾经在一个支付系统中用 2PC 做账户扣款和积分发放。上线后发现,高峰期经常出现「参与者等待协调者超时」的问题。后来我们加了一个超时重试机制,并且把协调者做成了集群部署,才勉强稳住。说实话,如果对性能要求高,2PC 不是最佳选择。

2.5 什么时候该用 2PC?

我个人觉得,2PC 适合以下场景:

  • 事务涉及的数据量小,执行时间短(毫秒级)
  • 并发量不高,能接受锁等待
  • 对一致性要求极高,不能接受最终一致性
  • 参与者数量少(2-5 个),网络稳定

反过来,如果你的系统有高并发、长事务、跨机房部署这些特征,我建议你慎重考虑 2PC。后面章节我们会聊 TCC、Saga 这些方案,它们在某些场景下比 2PC 更合适。

一句话总结:2PC 是分布式事务的「入门方案」,理解它才能理解更复杂的协议。但生产环境慎用,除非你能接受它的阻塞和单点问题。

公众号:蓝海资料掘金营,微信deep3321