本文記錄了自己對 Paxos 算法的學習和理解,並對多種分區情況進行了討論,整理了 Paxos 的執行思路並根據本人實踐對其中遇到的問題進行了講解。但還是可能存在理解不到位或者有紕漏的地方,還希望多多指教。
文章目錄
一 從CAP定理到Paxos算法
1 CAP定理
CAP定理是相對於一個分佈式計算系統而言的,首先需要了解C、A、P的定義:
- C:Consistency
一致性:所有節點持有相同最新數據副本(對外一致) - A:Availability
可用性:所有節點都能提供數據副本,但不一定最新(對外可用,但不一定好用) - P:Partition tolerance
分區容錯性:分區即指系統在一定的通信時限內不能達到數據一致性,即區間通信可能失敗。分區容錯性即允許出現分區。
CAP定理指 在一個分佈式系統中,無法同時滿足 CAP 3個特性。
例子:分佈式系統中出現了兩個分區 X 和 Y,X和Y分區內部是一致的 ,X 和 Y 無法溝通。則此時:
- 如果允許 X 和 Y 中的節點更新狀態,則會導致 X 和 Y 的數據不一致,此時喪失了一致性,爲 AP
- 如果只保留一個分區,將其他分區的節點設置爲不可用(通常是保留節點數過半的分區,捨棄其他的分區),則導致可用節點數下降,此時喪失可用性,爲 CP
- 如果要求 X和Y狀態相同且全部可用,則需要 X 和 Y 在同一分區,即該分佈式系統不存在分區,即否認了 該分佈式系統的 分區容錯性,爲 AC
2 分析
分區是分佈式系統必然面對的問題,如果不允許出現分區的話,有一臺主機由於網絡問題脫離集羣,就會導致系統運行失敗,那這個分佈式系統是毫無穩定性可言的。
故分區容錯性是必須滿足的,即可視爲 CAP 中的 P 總是成立的。所以 CAP 定理可以進一步理解成:
在滿足分區容錯性(P)的前提下,一個分佈式系統只能滿足 一致性(C)或者 可用性(A)。
可選的組合只有 CP 和 AP,這兩者各有優缺點,需根據應用場景進行選擇。
在實際應用中,一般沒有絕對的CP或者AP,一般都是在符合P的情況下,在A和C之間尋求平衡,我們說的CP或者AP通常指的是一種思想的實現或者說選擇的傾向。
如在各個註冊中心中,Netflix的Eureka是AP的,而Zookeeper和Consul都是CP的。一般認爲服務中心對可用性要求比較高,提供過時的服務總比不提供好。而配置中心則對一致性要求比較高。當然這種說法也不絕對。
3 從 CAP 看 Paxos 算法
Paxos 算法的目的是要 在消息可能會延遲、丟失、重複的基於消息傳遞通信模型的分佈式系統中就某個值最終達成一致。
這裏的“消息可能會延遲、丟失、重複”可理解爲 分區容錯性 P,“就某個值達成一致”可理解爲 一致性 C,在 Paxos 選舉期間服務不可用,則是犧牲了 A。
這麼說不準確,但可理解爲 Paxos 從設計上就是爲了在 P 的前提下實現 C。
二 Paxos 算法
Paxos相關概念
Paxos 中有三類角色 Proposer、Acceptor 和 Learner,主要交互過程在 Proposer 和 Acceptor 之間。
- Proposer
提案者/提議者:提議一個值,用於被投票決議 - Acceptor
附議者/接受者:對每個提議進行投票(只接受其中一個提案) - Learner
學習者/被告知者:被告知投票決議的結果,對其學習,不參與投票過程
注意:一個節點可同時擔任多個角色。
Paxos算法流程
Paxos分準備階段和批准階段兩個階段完成,每個階段後還需要判斷該階段的結果是否有效是否需要重來,另外需要注意提案編號全局遞增且唯一。
- 第一階段:prepare準備階段(只需攜帶提案號):
- request
proposer選擇一個提案編號n並將prepare請求發送給所在分區中的所有acceptor; - response
- 情況1:提案編號n < acceptor的最大提案編號 MaxN
不迴應或迴應錯誤提醒 - 情況2:acceptor從未承諾(promise)過提案
acceptor記錄最大提案編號MaxN爲n,迴應ok
- 情況3:提案編號n > acceptor的最大提案編號 MaxN
acceptor記錄最大提案編號MaxN爲n,迴應ok
,如果已經接受過提案,則將接受過的提案編號和值(AcceptN和AcceptV)一併回覆
- 情況1:提案編號n < acceptor的最大提案編號 MaxN
- request
- 判斷:如果propose收到超過半數的
ok
,則進入批准階段,否則重新回到準備階段 - 第二階段:批准階段(攜帶提案號和值):
- request
proposer向所有回覆ok
的acceptor發送accept請求,包括編號n和 上一階段拿到的最大的AcceptN 所對應的 AcceptV(如果沒有已經接受的value,那麼它可以自由決定value)。 - response
- 情況1:提案編號n < acceptor的最大提案編號 MaxN
不迴應或迴應錯誤提醒 - 情況2:提案編號n >= acceptor的最大提案編號 MaxN
批准請求,迴應ok
,記錄AcceptN=n,AcceptV=value(如果之前已經有AcceptN和AcceptV,這個時候也會覆蓋!)
- 情況1:提案編號n < acceptor的最大提案編號 MaxN
- request
- 判斷:如果Proposer收到超過半數的
ok
,則選舉結束,將結果通知給 Learner,否則重新回到準備階段
Paxos 算法流程理解
如果分區節點數沒有過半,則對外表現爲不可用(內部持續選舉,即使已經有統一值)
如果分區節點數過半,則對外表現爲可用(內部選舉結束,有統一值),這個時候稱之爲主分區。
其他分區只要在後面連接到主分區的任意一個節點,就會被這個節點的狀態同化,實現這個分區與主分區狀態一致,重複這個過程,最終實現分佈式系統的一致性。
Paxos 要點及解惑
- 最終要保證有唯一的狀態,所以要求要有過半的節點狀態統一才能確定選舉結束。(過半指全局過半而非分區內過半),所以每個階段後面都要校驗收到
ok
迴應的節點數,未過半則該階段操作無需,從準備階段重新開始 - Accepter確定AcceptV的過程就是投票的過程,表決有效則 AcceptV 不可修改,未表決成功的 AcceptV 則可以修改。Acceptor本身不知道表決是否有效,這是依靠規則自動維護的,而非人爲設置的。
達到的效果即是:選舉結束之後主分區的節點狀態不能被修改,只有主分區同化其他分區而不會有其他分區同化主分區。這一點後面會再作證明。 - AcceptV 需要修改說明 該AcceptV 的支持數未過半(表決失敗)且有另一未過半的AcceptV對應的AcceptN比它大。
- 提案編號越大代表越晚提出,這個時候有更大可能性有Acceptor已經投票了。Proposer會將提案的值修改爲有最多Acceptor支持的值。
這裏爲 最大的AcceptN 所對應的AcceptV,也就是說最大的AcceptN 所對應的AcceptV更有可能是最多人支持的value。
選舉結束的分區情況討論
-
情況1(單分區投票)
如果有一個分區X在審批階段結束後節點仍過半,其內部形成一致意見 [Nx,Vx],則選舉結束,不再討論。 -
情況2(雙分區投票)
如果有另一節點過半的分區Y接觸且僅接觸到分區X的任意節點,則會在準備階段收到 [Nx,Vx] 的迴應。
如果批准階段節點數仍過半,則順利以 [Ny,Vx] 結束,不再討論。
如果批准階段節點回應數不足,則會增加 [Ny,Vx] 的節點,可視爲對下一輪選舉無影響。 -
情況3(多分區投票)
如果存在於分區X情況類似的分區Z [Nz,Vz],且 Nz>Nx,此時有節點過半的分區Y接觸到分區X的節點和分區Y的節點。
如果批准階段節點數仍過半,則順利以 [Ny,Vz] 結束,不再討論。
如果批准階段節點回應數不足,則會增加 [Ny,Vz] 的節點,可視爲對下一輪選舉無影響。
如何證明參與選舉成功的節點的投票不可被修改?
投票不可被修改即 投票通過的 AcceptV 不可被覆蓋。
所以命題爲:有一個投票通過的值 [N1,V1] 被投票通過,則該分佈式系統中不存在已被某節點接受的[N2,V2]符合條件 ----- N2>N1 && V1<> V2 。
反證法:
假設 有一個投票通過的值 [N1,V1] 被投票通過,且該分佈式系統中存在已被某節點接受的[N2,V2]符合條件 ----- N2>N1 && V1<> V2 。
- N2已經被某節點接受 -----> [N2,V2] 通過了準備階段
- 由 承諾原則,知 N1 投票通過必然發生在 N2 準備階段結束 之前(否則有過半節點 應允N2不再接受N1)
- 由 過半原則,知 N1 投票通過 和 N2 的準備階段結束 必然存在交集,故 N2 必然接收到 [N1,V1]
- 接收到V1卻採用了與V1不同的V2,說明N2的準備階段存在已經被某節點接受的 [N3,V2],且 N3>N1 && V2 <> V1。
- 遞歸回題目,得出有無窮個 [Nn,Vn] ,假設顯然不成立,故原命題正確。
所以投票結束後,再加入的 Proposer 的提議 [N2,V2] 必然符合 N2>N1 && V2==V1。
循環這個過程,最終實現狀態一致。
三 Paxos算法流程圖
四 Paxos 圖例
- 有兩個Proposer,兩個都提出 prepare request。來自 Proposer A的 request 先於Proposer B 的 request 到達 Acceptor X和 Acceptor Y,但來自 Proposer B的 request 首先到達 Proposer Z.
- 如果接收(accept)prepare request 的 Acceptor 之前沒有看到其他的提議,則 Acceptor 以 prepare response 作出響應,該 prepare response 承諾永遠不接受具有較低提議編號的另一提議。
- 後面, Acceptor Z收到了 Proposer A 的 request ,Acceptor X和 Acceptor Y收到了 Proposer B的 request 。
如果 Proposer 之前已經看到具有更高提議號的 request ,則忽略晚到的 request,如Acceptor Z將忽略 Proposer A的 request(因爲2<4)。
如果 Proposer 之前沒有看到更高編號的 request ,它再次承諾忽略具有較低提議編號的任何請求,併發回其已接受的編號最高的提議以及該提議的值。如 Acceptor X和Y 對Proposer B的 request 的做法。
- 一旦 Proposer 收到大多數 Acceptor 的準備響應,它就可以發出接受請求。
-
對於Proposer A:由於 Proposer A僅收到表明沒有先前提案的答覆,因此它向每個具有與其初始提案相同的提議編號和值的 Acceptor 發送 accept request(n = 2,v = 8)。然而,這些 request 都將被忽略,因爲目標 Acceptor 都承諾不接受的提議編號低於4 的 request(這是對 Proposer B 的承諾)。
-
對於Proposer B:Proposer B 向每個 Acceptor 發送 accept request ,該 request 包含先前使用的提議號(n = 4)以及與其接收的準備響應消息中的最高提議號相關聯的值(v = 8)。請注意,這不是 Proposer B 最初提出的值,而是它看到的 prepare response 消息中的最高值。
-
- 如果 Acceptor accept 的 accept request 的 編號 比其已經看到的更高或相等,則它會 accept 並向每個 Learner 節點發送通知。當 Learner 發現大多數 Acceptor 已接受某個值時,Paxos算法會選擇該值
- 一旦Paxos選擇了一個值,與其他 Proposer 的進一步溝通就無法改變這個值。
如果另一個 Proposer(如 Proposer C)發送的 request 的 提案號比之前看到的提案號更高,並且具有不同的值(例如,n = 6,v = 7),則每個接受者都會使用之前的最高提案進行響應(n = 4,v = 8)。這要求提議者C發送包含[n = 6,v = 8] 的接受請求,該請求僅確認已經選擇的值。此外,如果一些少數接受者還沒有選擇一個價值,這個過程可以確保他們最終就同一價值達成共識。(批註,這個過程總是成立的,具體論證過程見上)
參考資料
Paxos 算法 維基百科:https://zh.wikipedia.org/zh-cn/Paxos算法
圖解分佈式一致性協議Paxos:http://codemacro.com/2014/10/15/explain-poxos/
Paxos By Example(本文圖解來自這裏):https://angus.nyc/2012/paxos-by-example/