在分佈式系統中,一個事務可能涉及到集羣中的多個節點。單個節點很容易知道本身執行的事務成功仍是失敗,但由於網絡不可靠難以瞭解其它節點的執行狀態(可能事務執行成功但網絡訪問超時)。算法
若部分節點事務執行失敗進行回滾,而其它節點完成事務提交,則事務會處於部分完成的不一致狀態。爲了不錯誤,分佈式系統須要使用分佈式一致性協議來保證分佈式事務的執行。網絡
2PC
兩階段提交(2-Phase Commit, 2PC)是一種比較簡單的分佈式一致性協議。分佈式
2PC協議中,每一個事務須要一個協調者來協調各個參與者。每一個事務分爲兩步執行。學習
- 階段一: 事務請求
- 協調者向全部參與者發送事務內容,詢問是否能夠執行事務操做。
- 各參與者執行事務,寫事務日誌但不進行提交。 各參與者鎖定事務相關的資源,保證事務能夠正常提交。
- 各參與者向協調者返回響應,YES表示能夠提交,NO表示不能夠提交。若協調者收到全部參與者的YES回覆,則準備進行事務提交。如有參與者回覆NO或者超時,則準備回滾事務。
- 階段二: 提交事務
- 協調者向全部參與者發送提交請求
- 參與者正式提交事務,並在完成後釋放相關資源。
- 參與者協調者回復ACK,協調者收到全部參與者的ACK後認爲事務提交成功。
- 回滾事務
- 在事務請求階段如有參與者回覆NO或者超時,協調者向全部參與者發出回滾請求
- 各參與者執行事務回滾,並在完成後釋放相關資源。
- 參與者協調者回復ACK,協調者收到全部參與者的ACK後認爲事務回滾成功。
2PC是一種簡單的一致性協議,它存在一些問題:設計
- 單點服務: 若協調者忽然崩潰則事務流程沒法繼續進行或者形成狀態不一致
- 沒法保證一致性: 若協調者第二階段發送提交請求時崩潰,可能部分參與者受到COMMIT請求提交了事務,而另外一部分參與者未受到請求而放棄事務形成不一致現象。
- 阻塞: 爲了保證事務完成提交,各參與者在完成第一階段事務執行後必須鎖定相關資源直到正式提交,影響系統的吞吐量。
參與者在完成階段一的事務執行後等待協調者的下一個請求,若協調者超時則能夠自行放棄事務。日誌
這種方案仍然有沒法保證一致性的缺點,但並不會出現某些資料所述一直鎖定資源,沒法繼續的狀況。進程
3PC
三階段提交協議(3-Phase Commit, 3PC)進一步將事務請求分爲兩個階段,能夠解決2PC協議阻塞的問題但沒法解決單點服務和不一致的問題。事務
3PC協議下事務分三步提交:資源
- CanCommit
- 協調者向全部參與者發送CanCommit請求
- 各參與者判斷是否能夠完成事務提交,但不執行事務也不鎖定資源
- 各參與者根據是否能夠完成事務向協調者回復YES或NO
- PreCommit
- 協調者向全部參與者發送PreCommit請求,執行事務預提交
- 各參與者執行事務,寫事務日誌但不進行提交。 各參與者鎖定事務相關的資源,保證事務能夠正常提交。
- 各參與者向協調者返回響應。若協調者收到全部參與者的YES回覆,則準備進行事務提交。如有參與者回覆NO或者超時,則放棄事務。
- DoCommit
- 協調者向全部參與者發送提交請求
- 參與者正式提交事務,並在完成後釋放相關資源。
- 參與者協調者回復ACK,協調者收到全部參與者的ACK後認爲事務提交成功。如有參與者回覆NO或者超時,則回滾事務。
- 參與者進入 PreCommit 狀態後,若始終未收到協調者的 DoCommit 請求則會超時後自動執行提交。
三階段提交協議在CanCommit階段不鎖定資源,解決了阻塞下降吞吐量的問題。開發
若某個參與者進入 PreCommit 後始終未收到協調者的進一步指令則會自動提交,該策略必定程度上避免協調者單點服務問題。
可是 3PC 仍然沒法解決數據不一致問題。
Paxos
Paxos 算法的目的在於使分佈式系統對於某個值達成一致,好比 Master 選舉過程當中保證最終全部節點對 Master 身份達成共識。
做者認爲 Paxos 解決的分佈式共識問題與分佈式事務有着較大不一樣。
Paxos 認爲信道可能丟失數據可是不會篡改數據(即不存在拜占庭將軍問題),實際上咱們也很容易經過校驗檢查數據是否被篡改。
在介紹Paxos算法以前,咱們先來分析2PC(3PC)協議在分佈式共識問題上的不足。
2PC(3PC)協議要求收到全部參與者的 ACK 消息後才認爲提交成功,而在Master選舉這類分佈式共識問題上只須要過半參與者達成一致便可。
而最難以解決的問題在於協調者的單點服務問題,若協調者在過程當中崩潰則集羣很難繼續達成共識。
所以,關鍵在於設計在有多個協調者的狀況下仍然能夠達成共識的協議。
Basic Paxos
Paxos算法中有3個角色:
- Proposer: 負責發起提案,相似於2PC中的協調者
- Acceptor: 負責批准提案,相似於2PC中的參與者
- Learner: 不參與提案過程,只從其它Acceptor那裏學習已經過的提案。
咱們重點介紹 Proposer 和 Acceptor 參與的流程,暫時不介紹 Learner。
在集羣中每一個進程(節點)可能會扮演其中多個角色。
提案由編號N和值V組成記做(N, V), 每一個提案都的編號N是惟一的。保證編號惟一很是簡單,若集羣中有k個 Proposer, 那麼第i個Proposer提出的第n個提案編號爲 i + k * n。
咱們但願集羣最終能夠選中一個V,且全部節點知道集羣最終選定的V值。
算法作出幾個規定:
- 只要集羣中有超過半數的Accpetor批准了提案,Proposer 就能夠認爲集羣對接受了提案
- 在一輪投票中,Acceptor老是批准它收到的第一個提案
- 在一輪投票中,Acceptor能夠批准多個提案,可是批准提案的值V必須相同
算法分爲兩個階段:
- prepare 階段
- Proposer 選擇提案N,向半數以上Acceptor發送請求Prepare(N)
- Acceptor 保存本身受到過的最大請求的編號 maxN 和已接受的編號最大提案 (acceptedN, acceptedV)。
- 若 maxN > N, 那麼 Acceptor 返回拒絕響應
- 若 maxN < N, 那麼 Acceptor 返回已接受的編號最大提案(acceptN, acceptV),若還沒有接受過提案則返回空的成功響應。同時,Acceptor 更新 maxN, 即不會在接受編號小於N的請求
- accept 階段
- 若 Proposer 收到過半 Acceptor 對 Prepare(N) 返回的ACK響應,那麼它會從響應的提案中選出編號最大的一個(acceptN, acceptV), 若響應中不包含提案則由 Proposer 決定提案。決定提案後 Proposer 會向過半 Acceptor 發送 Accept(N, V)請求。
- Acceptor 收到 Accept(N, V) 請求後
- 若 maxN > N, 那麼 Acceptor 返回拒絕響應
- 若 maxN < N, 那麼 Acceptor 返回成功響應,並更新已接受的編號最大提案 (acceptedN, acceptedV)
- 若 Proposer 未收到過半 Acceptor 對 Accept(N, V) 請求的成功響應,則認爲提案被拒絕。
若集羣中存在兩個 Proposer 依次提出編號遞增的提案可能會使 Paxos 算法陷入死循環:
- Proposer1 提出提案 N1, 並收到過半Prepare(N1)響應
- Proposer2 提出提案 N2 (N2 > N1), 並收到過半Prepare(N2)響應
- Proposer1 進入第二階段, 過半Accept(N1)請求被拒絕 (過半Acceptor 的 maxN = N2)。 Proposer1 提出提案 N3 (N3 > N2) ...
這種狀況稱爲算法陷入活鎖,在工程實踐中咱們一般選擇一個 Proposer 做爲 leader。
Paxos 算法能夠在數學上證實它的正確性深刻淺出Paxos算法協議。
Paxos 算法實現難度和運行開銷很是大,所以開發出 Raft、ZAB等協議用於生產實踐。