2PC与3PC#
一句话答案#
2PC 两阶段提交(prepare→commit),问题:协调者单点+参与者阻塞;3PC 增加 canCommit 预检+超时机制减少阻塞。
核心要点
2PC(Two-Phase Commit)两阶段提交:
引入一个协调者(Coordinator)来统一调度多个参与者(Participant)的事务提交。
阶段一:Prepare(准备阶段 / 投票阶段)
协调者 ──────→ 参与者1:「能否提交事务?」
──────→ 参与者2:「能否提交事务?」
──────→ 参与者3:「能否提交事务?」
各参与者执行事务操作(但不提交):
① 执行 SQL
② 写 redo log(重做日志)和 undo log(回滚日志)
③ 锁定相关资源
参与者 ──────→ 协调者:返回 YES(可以提交)或 NO(无法提交)
阶段二:Commit / Rollback(提交 / 回滚阶段)
情况A:所有参与者都返回 YES
协调者 ──────→ 所有参与者:发送 Commit 请求
参与者:提交事务,释放资源 → 返回 ACK
协调者:收到所有 ACK → 事务完成
情况B:任一参与者返回 NO 或超时未响应
协调者 ──────→ 所有参与者:发送 Rollback 请求
参与者:利用 undo log 回滚事务,释放资源 → 返回 ACK
协调者:收到所有 ACK → 事务回滚完成plaintext2PC 的四大问题:
| 问题 | 说明 |
|---|---|
| 同步阻塞 | 阶段一执行完后,所有参与者锁定资源等待协调者指令,期间资源被阻塞 |
| 单点故障 | 协调者挂了,参与者一直等待,资源无法释放,整个事务阻塞 |
| 数据不一致风险 | 阶段二协调者发 Commit 后挂了,部分参与者已提交,没收到的参与者处于不确定(in-doubt)状态只能阻塞等协调者恢复;若人工或超时擅自回滚,就会真正不一致 |
| 过于保守 | 任一参与者失败或超时都会回滚整个事务,没有容错机制 |
3PC(Three-Phase Commit)三阶段提交:
在 2PC 的基础上做了两个改进:(1) 引入超时机制;(2) 增加 CanCommit 预询问阶段。
阶段一:CanCommit(预询问阶段)
协调者 → 参与者:「你能执行这个事务吗?」(不执行,仅检查条件)
参与者 → 协调者:YES / NO
→ 如果有人 NO 或超时 → 直接终止,不会锁定任何资源
阶段二:PreCommit(预提交阶段)
协调者 → 参与者:「请执行事务,但先别提交」
参与者:执行事务、写 redo/undo log、锁定资源
参与者 → 协调者:ACK
→ 如果有人 NO 或超时 → 协调者发 Abort → 参与者回滚
阶段三:DoCommit(正式提交阶段)
协调者 → 参与者:「正式提交」
参与者:提交事务、释放资源
参与者 → 协调者:ACK
【关键改进】如果参与者在阶段三等待协调者指令超时:
→ 参与者会自动提交事务(因为既然走到了 PreCommit 阶段,说明大家都同意了)
→ 减少了阻塞,但可能导致数据不一致plaintext3PC 不能解决网络分区下的一致性问题: 它的非阻塞性依赖同步网络假设(消息延迟有界、只有节点宕机)。发生网络分区时,一侧收到 Abort 回滚,另一侧超时自动提交,结果不一致。工程上解决协调者单点的做法是用 Paxos/Raft 复制协调者状态(如 Paxos Commit),而不是 3PC。
2PC vs 3PC 对比:
| 维度 | 2PC | 3PC |
|---|---|---|
| 阶段数 | 2 | 3(多了 CanCommit 预询问) |
| 参与者超时 | 无(一直等协调者) | 有(超时自动提交,减少阻塞) |
| 阻塞程度 | 高(协调者挂了就完全阻塞) | 低(参与者可超时自动决策) |
| 一致性风险 | 有(脑裂导致部分提交) | 仍有(超时自动提交可能和实际不一致) |
| 实际应用 | MySQL XA 事务 | 较少使用(复杂且仍不能完全避免不一致) |
面试回答(2分钟版)
2PC 是分布式事务最经典的协议,分为 prepare 和 commit 两个阶段:协调者先问所有参与者”能不能提交”,参与者执行事务但不提交并锁住资源,全部返回 YES 后协调者再发 commit 指令正式提交。核心问题有三个——协调者单点故障会导致全体阻塞、参与者在 prepare 后一直持锁等待、以及协调者发出 commit 后宕机或消息丢失时部分节点已提交、其余节点只能阻塞等待,若擅自决策就会不一致。3PC 在此基础上做了两个改进:增加 CanCommit 预询问阶段,先轻量探测再锁资源;引入参与者超时机制,等不到协调者指令就自动提交,减少了阻塞,但它依赖同步网络假设,网络分区时超时自动提交会导致不一致,所以 3PC 并没有解决分区下的一致性问题。实际工程中 2PC 用于 MySQL XA 事务等场景,而纯 3PC 很少使用,更多是用 TCC、SAGA 等柔性事务方案来解决分布式一致性问题。
追问与易错
追问方向:
- “2PC 的阻塞问题?”→ Prepare 后参与者持锁等待协调者指令,协调者宕机则全体阻塞资源无法释放,且无超时机制
- “3PC 真解决了问题吗?”→ 缓解了阻塞(参与者超时自动提交),但超时自动提交可能与实际结果不一致,所以仍不完美
- “Seata AT 和传统 2PC 区别?”→ Seata AT 一阶段就提交本地事务、释放数据库本地锁,通过 undo_log 实现回滚;但 TC 上的全局锁会持有到二阶段结束,用于写隔离,全局默认隔离级别是读未提交(SELECT FOR UPDATE 才能读已提交),所以比 XA 持库锁的时间短,但不是无锁
易错点:
- ❌ “2PC 在网络分区下会自动破坏原子性”——严格 2PC 下不确定的参与者会一直阻塞,牺牲的是可用性;真正的不一致来自协调者日志丢失或人工/超时擅自提交回滚
- ❌ 3PC 完美解决了 2PC——它只在同步网络假设下非阻塞,网络分区时超时自动提交会导致不一致