世界上只有一種一致性算法,就是 Paxos
。出自一位 Google
大神之口。Paxos
也是出名的 晦澀難懂,推理過程極其複雜。算法
Paxos
有點相似以前說的 2PC
,3PC
,可是解決了這兩種算法各類硬傷。該算法在不少大廠都獲得了工程實踐,好比阿里的 OceanBase
的 分佈式數據庫,底層就是使用的 Paxos
算法。再好比 Google
的 chubby
分佈式鎖 也是用的這個算法。可見該算法在分佈式系統中的地位,甚至於,Paxos
就是 分佈式一致性 的代名詞。數據庫
Paxos
算法是 基於消息傳遞 且具備 高效容錯特性 的一致性算法,目前公認的解決 分佈式一致性問題 最有效的算法之一.編程
拜占庭是古代東羅馬帝國的首都,因爲地域寬廣,守衛邊境的多個將軍(系統中的多個節點)須要經過信使來傳遞消息,達成某些一致的決定。但因爲信使中可能存在叛徒(系統中節點出錯),這些叛徒將努力向不一樣的將軍發送不一樣的消息,試圖會干擾一致性的達成。後端
故事背景是古希臘 Paxos
島上的多個法官在一個大廳內對一個議案進行表決,如何達成統一的結果。他們之間經過服務人員來傳遞紙條,但法官可能離開或進入大廳,服務人員可能偷懶去睡覺。緩存
在常見的 分佈式系統 中,總會發生 節點宕機 或 網絡異常 (包括消息的 重複、丟失、延遲、亂序、網絡分區) 等狀況。安全
Paxos
算法主要就是解決如何在一個 發生如上故障 的分佈式系統中,快速正確的在集羣內 對某個值達成一致,而且保證 整個系統的一致性。網絡
注意:提案的範圍>value.後面會講到,[提案=編號+Value].也可表示爲[M,V]. 如下描述中暫定: 提案=P,Value=V.多線程
Proposer : Proposer
能夠 提出提案 (Proposal
)。架構
Accecptor : Acceptor
能夠 接受提案。一旦接受提案,提案 裏面的 value
值就被選定了。框架
Learner : Acceptor
告訴 Learner
哪一個提案被選定了,那麼 Learner
就學習這個被選擇的 value
。
在具體的實現中,一個進程便可能是Proposer,也多是Acceptor,也多是Learner。
Paxos
算法的核心是 一致性。因此將從一致性問題的描述來說解該算法怎麼解決實際問題。
P
中,只有一個 V
被選中。P
被提出,就沒有 V
被選中。P
被選定後,進程均可以學習被選中的 P
。每一個角色以任意的速度執行,可能因出錯而中止,也可能會重啓。一個 value
被選定後,全部的角色可能失敗而後重啓,除非那些失敗後重啓的角色能記錄某些信息,不然等他們重啓後沒法肯定被選定的值。
消息在傳遞過程當中可能出現 任意時長的延遲,可能會 重複,也可能 丟失,可是消息不會被 損壞。
一個 Acceptor
接受一個 P
,那麼只有一個 V
被選定。
問題:若是這個 Acceptor 宕機,那麼整個系統服務不可用。
問題:如何在多 Proposer 和多 Acceptor 狀況下,選定一個 value?
講解步驟分兩階段:約定 P1
和 約定 P2
。
P1 :一個 Acceptor 必須接受一個它收到的第一個 P。
若是每一個 Proposer 會產生不一樣的 P,那麼多個 Proposer 一定產生多個 P,發給多個 Acceptor。根據 約定 P1
,Acceptor
分別接受到 P
,就會致使不一樣的 V
被選定,以下圖所示:
如上圖所示,P1
會產生的問題: v1
、v2
、v3
都沒有被選定,由於他們只有被一個 Acceptor
接受。
對於上述問題,咱們須要一個額外的約定:
P1a : 一個提案 P 被選定,須要被半數以上 Acceptor 接受.
對於 P1a
,其實就意味着 一個Acceptor必須接受不止一個提案。
顯然,這與 P1
相矛盾,因此須要從新設計提案。原來的設計是: [提案P = value]
,如今從新設計 [提案P = 提案編號 + value]
,可表示爲 [M,V]
。
新問題:多提案被選定,如何保證被選定的提案 P 具備相同的value?
P2 : 若是提案 P[M0,V0] 被選定了,那麼全部比 M0 編號更高的,且被選定的 P,其 value 的值也是 V0。
對於 P2
中的 「被選定」:一個提案要被選定,首先至少要被一個 Acceptor
批准。所以,能夠理解 P2
爲:
P2a : 若是提案 P[M0,V0] 被選定了,那麼全部比 M0 編號更高的,且 [被Acceptor批准] 的P,其 value 值也是 V0。
只要知足 P2a
,就能知足 P2
。多提案被選擇 的問題解決了,可是因爲 網絡不穩定 或者 宕機 的緣由(不可避免),會產生新問題:
假設有 5
個 Acceptor
。Proposer2
提出 [M1,V1]
的提案,Acceptor2~5
(半數以上)均接受了該提案,因而對於 Acceptor2~5
和 Proposer2
來說,它們都認爲 V1
被選定。Acceptor1
剛剛從 宕機狀態 恢復過來(以前 Acceptor1
沒有收到過任何提案),此時 Proposer1
向 Acceptor1
發送了 [M2,V2]
的提案 (V2≠V1且M2>M1)。對於 Acceptor1
來說,這是它收到的 第一個提案。根據 P1
(一個 Acceptor
必須接受它收到的 第一個提案),Acceptor1
必須接受該提案。同時 Acceptor1
認爲 V2
被選定。
這就出現了兩個問題:
Acceptor1
認爲 V2
被選定,Acceptor2~5
和Proposer2
認爲 V1
被選定。出現了不一致。
V1
被選定了,可是 編號更高 的被 Acceptor1
接受的提案 [M2,V2]
的 value
爲 V2
,且 V2≠V1。這就跟 P2a
(若是某個 value
爲 v
的提案被選定了,那麼每一個 編號更高 的被 Acceptor
接受的提案的 value
必須也是 v
)矛盾了。
基於以上問題,全部就有了 P2b
:
P2b : 若是 P[M0,V0] 被選定後,任何 Proposer 產生的 P,其值也是 V0。
對於 P2b
中的描述,怎樣保證 任何Proposer產生的P,其值也是V0 ?只要知足 P2c
便可:
P2c: 對於任意的 M、V,若是 [M,V] 被提出,那麼存在一個半數以上的 Acceptor 組成的組合 S,知足如下兩個條件中的任何一個: ① S 中沒有一個接受過編號小於 M 的提案。 ② S 中的 Acceptor 接受過的最大編號的提案的 value 爲 V。
推導完畢。。。
整體思路以下:
Proposer
選擇一個新的提案 P[MN,?]
向 Acceptor
集合 S
(數目在半數以上)發送請求,要求 S
中的每個 Acceptor
作出以下響應:
若是 Acceptor
沒有接受過提案,則向 Proposer
保證 再也不接受編號小於N的提案。
若是 Acceptor
接受過請求,則向 Proposer
返回 已經接受過的編號小於N的編號最大的提案。
若是 Proposer
收到 半數以上 的 Acceptor
響應,則 生成編號爲 N
,value
爲 V
的提案 [MN,V]
,V
爲全部響應中 編號最大 的提案的 value
。
若是 Proposer
收到的響應中 沒有提案,那麼 value
由 Proposer
本身生成,生成後將此提案發給 S
,並指望 Acceptor
能接受此提案。
Acceptor
能夠忽略任何請求(包括 Prepare
請求和 Accept
請求)而不用擔憂破壞 算法的安全性。所以,咱們這裏要討論的是何時 Acceptor
能夠響應一個請求。
對 Acceptor
接受提案給出以下約束:
P1b:一個 Acceptor 只要還沒有響應過任何編號大於 N 的 Prepare 請求,那麼就能夠接受這個編號爲 N 的提案。
若是 Acceptor
收到一個編號爲 N
的 Prepare
請求,在此以前它已經 響應過 編號大於 N
的 Prepare
請求。根據 P1b
,該 Acceptor
不可能接受編號爲 N
的提案。所以,該 Acceptor
能夠 忽略 編號爲 N
的 Prepare
請求。固然,也能夠回覆一個 error
,讓 Proposer
儘早知道本身的提案 不會被接受。
所以,一個 Acceptor
只需記住:
Learner
學習(獲取)被選定的 value
有以下三種方案:
Paxos
在 節點宕機恢復、消息無序或丟失、網絡分化 的場景下能保證 數據的一致性。而 Paxos
的描述側重於 理論,在實際項目應用中,處理了 N
多實際細節後,可能已經變成了另一種算法,這時候正確性已經沒法獲得理論的保證。
要證實分佈式一致性算法的正確性一般比實現算法還困難。因此不少系統實際中使用的都是以 Paxos
理論 爲基礎而 衍生 出來的變種和簡化版。例如 Google
的 Chubby
、MegaStore
、Spanner
等系統,ZooKeeper
的 ZAB
協議,還有更加容易理解的 Raft
協議。
大部分系統都是靠在實踐中運行很長一段時間,通過驗證發現系統已能夠基本運行,沒有發現大的問題才能上生產環境。
歡迎關注公衆號: 零壹技術棧
本賬號將持續分享後端技術乾貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分佈式和微服務,架構學習和進階等學習資料和文章。