目錄
為什么需要 3PC?
核心結論
3PC的優缺點
3PC與 Paxos / Raft 對比
本篇文章內容的前置知識為?分布式2PC理論,如果不了解,可點擊鏈接學習
分布式2PC理論-CSDN博客
為什么需要 3PC?
1) 2PC 的根本問題:阻塞 + 不確定
2PC 兩階段:投票(prepare)→提交(commit)
一旦協調者在關鍵窗口宕機或網絡分區,參與者會進入不確定狀態——既不能提交也不敢回滾,只能無限等待協調者的最終決定。這帶來兩類問題:
阻塞:協調者掛了或網絡中斷,參與者無法自決,業務線程/鎖資源被長期占用
進退兩難:參與者貿然提交會破壞原子性;貿然回滾也可能與其他節點的提交相沖突
工程副作用:長時間占鎖、長事務、資源饑餓、吞吐下降,甚至雪崩。
直觀例子:協調者收齊所有YES后掛了。2PC 里參與者都已準備就緒,但沒有最終指令,誰也不敢動,導致系統卡死。
2) 3PC 的設計目標:在可判定超時的條件下非阻塞
3PC(Three-Phase Commit)的目標不是更強一致,而是降低阻塞:
在一個同步或部分同步網絡(消息延遲有明確上限 Δ)下,引入超時與一個緩沖階段,讓參與者在協調者失聯時能自行做決定,不再無限等待。
3PC的關鍵前提假設(很重要):
故障模型為停機型(Fail-Stop):節點故障后只會停止服務,不會發送錯誤消息(即不作惡)
消息延遲有上限:網絡傳輸、節點處理的延遲不會超過 Δ,因此超時可作為協調者失聯的有效判定依據
無嚴重網絡分區:若出現分區(部分節點斷連),3PC 的一致性可能被破壞(見第 5 節)
同步網絡:消息從發起到接收的延遲,一定≤某個明確的固定值 Δ,且節點處理消息的速度也有上限,全程可預判。
部分同步網絡:大部分時候消息延遲≤Δ,但允許偶爾超出(可恢復),核心是多數情況下能按同步網絡的規則決策。
3) 3PC 的核心改動:把 2PC 的準備再切半
????????3PC 的核心改動是將 2PC 的 Prepare 階段拆分為 CanCommit(預詢問)和 PreCommit(預提交)兩個階段,再保留 DoCommit(正式提交)階段,通過三階段日志 + 超時自決規則,將 2PC 的單一不確定窗口拆分為兩個可安全決策的窗口。
階段 | 協調者行為 | 參與者行為 | 超時規則(核心) |
---|---|---|---|
1. CanCommit(預詢問) | 向所有參與者發送是否可提交請求,等待反饋 | 僅自檢(不占鎖、不持久化關鍵日志),若可行返回 YES,否則返回NO | 協調者:超時未收齊YES → 宣告Abort 參與者超時未收到協調者后續指令 → 直接Abort(安全,因未占資源) |
2. PreCommit(預提交) | 僅當收齊所有YES時,發送預提交請求;否則發送Abort | 收到預提交后,持久化預提交完成日志(占鎖、預留資源),返回ACK;收到Abort則直接回滾 | 協調者:超時未收齊ACK → 發送Abort 參與者超時未收到DoCommit指令 → 自主Commit(安全,因所有節點已通過預詢問) |
3. DoCommit(正式提交) | 收齊預提交ACK后,發送正式提交請求;若中途發現異常(如參與者回滾),則發送Abort | 收到Commit則執行提交、釋放資源、刪除鎖;收到Abort則回滾。返回ACK給協調者 |
非阻塞的本質在于兩條超時自決規則:
3PC 通過階段拆分,讓參與者在任何超時場景下都有安全決策:
CanCommit 階段超時:在 CanCommit 等不到協調者/指令,直接 ABORT
PreCommit 階段超時:在 PreCommit 等不到DoCommit ,自主 COMMIT
這把 2PC 的唯一不確定窗口拆成兩個:
PreCommit 之前超時 → 放棄(保證不會有人已提交)
PreCommit 之后超時 → 提交(保證大家都具備提交條件)
階段1:CanCommit(預詢問)
協調者 → 參與者:能提交嗎?
參與者自檢資源/邏輯,可行則回 YES,否則 NO
——超時策略:收不齊 YES => 協調者宣告 ABORT;參與者也可安全放棄階段2:PreCommit(預提交)
條件:收齊所有 YES
協調者 → 參與者:進入“可提交態”,先落本地日志/鎖資源,但還不寫入最終提交;參與者回 ACK
——關鍵:一旦進入 PreCommit,大家都“隨時可完成提交”階段3:DoCommit(正式提交)
協調者 → 參與者:執行提交;參與者提交并釋放資源,回 ACK
4) 3PC 如何緩解 2PC 的典型故障場景
用三個場景對比:
場景 A:協調者在收齊 YES 之前宕機
2PC:有人已投 YES,有人還在等,不知道最終態,可能阻塞
3PC:仍處 CanCommit,參與者超時后安全 ABORT(沒人進入 PreCommit)
場景 B:協調者在發出 PreCommit 之后、DoCommit 之前宕機
2PC:所有參與者都 prepare 完成,只能無限等最終決定
3PC:大家處于 PreCommit,等不到 DoCommit 就自主 COMMIT(非阻塞)
場景 C:協調者在發出 DoCommit 之后宕機
兩者都能依賴持久化日志恢復到一致提交(已收到的提交照常提交,未收到的要么等恢復、要么依據恢復協議補齊)
協調者 DoCommit 后宕機,影響是沒法收參與者的最終 ACK、沒法統一處理異常,但不影響一致性,因為它已發完 Commit 指令:收到的參與者正常提交,沒收到的參與者靠 PreCommit 日志(知道自己該提交),重啟 / 超時后也會提交,最終結果一致
重啟是因為參與者有可能在 PreCommit 階段突然宕機,宕機后內存里的狀態會丟,重啟時只能靠日志找回之前已進入 PreCommit 的狀態,才能按規則補提交;若沒宕機,超時后直接自主 Commit 即可,不用重啟。
5) 3PC 仍然不萬能:網絡分區下可能不一致
致命邊界條件:分區導致有些參與者收到 PreCommit,另一些沒收到
收到 PreCommit 的那一側:按 3PC 規則,超時會 COMMIT
未收到的一側:按規則會 ABORT
破壞原子性
這就是為什么 3PC 的非阻塞要靠同步/無分區假設;而在真實互聯網環境(異步且可能分區),3PC 不能保證強一致
6) 形式化地看:狀態機與超時轉移
從工程實現角度,3PC 的非阻塞和一致性依賴狀態機 + 持久化日志(WAL,Write-Ahead Log) ,確保節點重啟后能恢復到正確狀態。
參與者的核心狀態流轉:
Init(初始) → Waiting(CanCommit后,待預提交) → PreCommit(預提交完成) → Commit/Abort(最終態)
狀態躍遷規則:
Waiting 狀態:超時 / 收到 Abort → 跳轉到 Abort;收到 PreCommit → 跳轉到 PreCommit
PreCommit 狀態:超時 / 收到 Commit → 跳轉到 Commit;收到 Abort → 跳轉到 Abort
任何狀態躍遷前,必須先持久化日志,防止重啟后狀態丟失
日志的核心作用:冪等恢復
節點宕機重啟后,通過讀取 WAL 日志確定自身狀態:
若日志記錄已進入 PreCommit?→ 直接執行 Commit
若日志記錄僅 Waiting?→ 執行 Abort
若日志記錄已 Commit?→ 無需操作(確保冪等,避免重復提交)
冪等是多次執行同一操作,結果不變;恢復是崩潰后回到崩潰前的正確狀態
核心結論
????????2PC 的痛點是阻塞:卡在 Prepare 后、Commit 前的不確定窗口,只能死等協調者,活性差但異步環境下安全性穩
????????3PC 的改進是拆窗自救:用 CanCommit+PreCommit 拆分窗口,讓參與者能按狀態超時自決(Waiting→Abort/PreCommit→Commit),緩解阻塞,但代價是多一輪通信、且分區下可能分叉,需依賴同步 / 部分同步網絡
????????工業界少用 3PC,是因為它解決了 2PC 的阻塞,卻引入了分區下的一致性風險,而 Raft/Paxos 等共識協議能在異步 + 分區環境下同時保證安全與活性,更適配復雜場景。
3PC的優缺點
優點:
比 2PC 更少阻塞。
引入超時機制,避免無限等待。
缺點:
實現復雜,消息開銷大(多一輪通信)
仍不能完全解決一致性問題(極端網絡分區時仍可能矛盾)
在實際工業界應用很少(數據庫幾乎不用 3PC)
3PC與 Paxos / Raft 對比
3PC:事務原子提交協議,目標是“分布式事務一致性”。
Paxos / Raft:分布式一致性協議,目標是“副本之間達成共識”。
區別:
3PC 偏向數據庫事務。
Paxos / Raft 偏向分布式存儲、日志復制。
工業界更常用 Raft / Paxos,而不是 3PC。
為啥工業界更愛 Raft/Paxos,不用 3PC?
因為 3PC 的本事太單一,只解決事務原子提交,實際場景中,事務的需求可以用更靈活的方案(如 TCC、Saga)替代,不用死磕 3PC;
而副本共識(同步數據、選主)是所有分布式系統的基礎剛需(比如分布式數據庫、緩存集群、云服務都要),Raft/Paxos 剛好能穩定解決這個剛需,還能容忍網絡分區、節點故障,比 3PC 更通用抗造。