分佈式理論:深刻淺出Paxos算法

前言

Paxos算法是用來解決分佈式系統中,如何就某個值達成一致的算法。它晦澀難懂的程度徹底能夠跟它的重要程度相匹敵。目前關於paxos算法的介紹已經很是多,但大多數是和稀泥式的人云亦云,卻不多有人能對提出本身的看法。本文試圖從不同的角度來對Paxos made simple的論文進行解釋,而不只僅是對論文的拙劣翻譯,但願即便沒有看過論文的同窗也能看懂。算法

一致性問題

爲了實現集羣的高可用性,用戶的數據每每要多重備份,多個副本雖然避免了單點故障,但同時也引入了新的挑戰。
假設有一組服務器保存了用戶的餘額,初始是100塊,如今用戶提交了兩個訂單,一個訂單是消費10元,一個訂單是充值50元。因爲網絡錯誤和延遲等緣由,致使一部分服務器只收到了第一個訂單(餘額更新爲90元),一部分服務器只收到了第二個訂單(餘額更新爲150元),還有一部分服務器兩個訂單都接收到了(餘額更新爲140元),這三者沒法就最終餘額達成一致。這就是一致性問題。
一致性算法並不保證全部提出的值都是正確的(這多是安全管理員的職責)。咱們假設全部提交的值都是正確的,算法須要對到底該選哪一個作出決策,並使決策的結果被全部參與者獲悉。
一致性算法並不保證全部節點中的數據是徹底一致的,但它能保證即便一小部分節點出現故障,仍然能對外提供一致的服務(客戶端無感知)
在正式開始介紹Paxos所面臨的難題前,爲了表述方便,先提一下Paxos算法中的三個角色,後面會比較頻繁的用到:安全

  • Proposer:議案發起者。
  • Acceptor:決策者,能夠批准議案。
  • Learner:最終決策的學習者。

咱們虛擬一個一致性問題的場景:有一個用戶小綠,如今要對他的姓氏信息進行修改,此時有多個不一樣的議案被提出,如何就最終的結果達成一致。
首先看一下下面這種最簡單的狀況:A1接受了Pa的議案「趙」,A2和A3接受了Pb的議案「錢」,那麼最終小綠應該姓什麼?
p1
答案很簡單:超過半數的的議案就是最終的選定值。小綠應該姓「錢」!在議案提交後,Pa和Pb只要查詢一下小綠姓氏,很容易就能查到 「錢」的數量超過半數,所以Pb的議案將會返回「成功」,Pa的議案將會返回「失敗」。服務器

P0. 當集羣中,超過半數的Acceptor接受了一個議案,那咱們就能夠說這個議案被選定了(Chosen)。網絡

P0已是一個完備的一致性算法,保證了P0也就解決了一致性問題。可是P0的實用性不佳,一個議案想被半數以上的Acceptor接受是一件極其困難的事情!
看下面這種狀況:A1,A2,A3分別接受了「趙」,「錢」,「孫」,結果沒有任何一個議案造成多數派,全部的議案都將返回「失敗」。議案的數量越多,那議案被選定的機率就越低,這顯然是無法容忍的。
p2
要解決這個問題,必須容許一個Acceptor接受多個議案,後接受的議案能夠覆蓋掉以前接受的議案。
以下圖所示, A1已經接受了「趙」,A2已經接受了「錢」,此時Pc提出了「孫」,並被A1,A2,A3接受,這樣就解決了沒法造成多數派的問題。
p3
但如今又會面臨下圖中的新問題:A1,A2,A3已經接受了「趙」,此時咱們認爲「趙」是被選定的,但此時恰恰Pb和Pc不識時務,Pb向A2提出了「錢」,Pc向A3提出了「孫」。這樣就從一致性狀態,又回到了不一致的狀態…這顯然破壞了一致性。
p4
Paxos就是在上述背景下產生的,Paxos要實現的目標的是:分佈式

T1.一次選舉必需要選定一個議案(不能出現全部議案都被拒絕的狀況)
T2.一次選舉必須只選定一個議案(不能出現兩個議案有不一樣的值,卻都被選定的狀況)學習

Paxos算法的推導

首先,Paxos算法的必需要能知足第一個條件:翻譯

P1:一個Acceptor必須接受它收到的第一個議案。code

要知足這個條件實在太過簡單了,方法略。。。
下面是我我的對這個條件的理解,爲何必須知足這個條件:
假設只有一個Acceptor,只有一個Proposer。若是Acceptor出於某些緣由拒絕了Proposer的議案,那必然致使Paxos的目標T1沒法達成。所以能夠認爲目標T1隱含了P1。遊戲

在開始P2的推導的前,爲了區分不一樣議案,須要先對每一個Proposer的議案進行編號,編號時必須保證每一個議案的編號具備惟一性(不討論實現方法),並且編號是不斷增大的。
Paxos的目標T2隱含了P2:io

P2:若是一個值爲v的議案被選定了,那麼被選定的更大編號的議案,它的值必須也是v。

P2很容易理解,除了其中的一個形容詞更大編號的,這個形容詞很扎眼,爲何只對更大編號的議案進行限制,更小的編號怎麼辦? 老頭子給的解釋很簡單「By induction on proposal number」(若是不看論文後半部分,沒人知道他在說什麼…)我說一下我本身的理解:
首先把「更大編號的」幾個字換成「其餘的」,咱們稱它爲P2S。那麼P2S可否知足Paxos的目標?答案是確定的。而後比較P2和P2S,誰的約束更強?這得看「更小的編號」是怎麼處理的,從論文後面的推演來看更小編號的議案絕對不容許被選定!!!所以知足P2的議案是P2S的一個子集。
顯而易見,P2S和P2都能知足Paxos目標。換句話說,能知足Paxos目標的辦法不少,但咱們只選其中一個辦法就OK了。不過,要選最簡單的辦法(看完後面就知道了)。
總之,如今咱們能夠得出一個結論:
若是P1和P2都可以被知足,那麼Paxos的兩個目標就可以達成。 若是你對上面這個結論沒有異議,那麼就說明你已經充分理解了P1和P2。
接下來就須要想辦法,如何才能知足P2:議案在選定前,都要先被Acceptor接受,所以要知足P2,咱們只要知足下面的條件:

P2a:若是一個值爲v的議案被選定了,那麼Acceptor接受的更大編號的議案,它的值必須也是v。

P2a是P2的充分條件,可是P2a存在一個大麻煩:當一個議案被選定後,一部分Acceptor沒法馬上得到通知。例以下圖中A1和A2已經接受了「趙」,這時「趙」就被選定了,此時Pb向A3提出了一個議案「錢」,這是A3接受的第一個議案,爲了知足P1,A3必須接受這個議案,此時就會致使P2a沒法被知足了。
p5
爲了解決上述的問題,咱們想一下:要是此時不讓Pb提出「錢」這個議案,而是提出「趙」這議案就萬事大吉了。順着這個思路,咱們獲得了P2b:

P2b:若是一個值爲v的議案被選定了,那麼Proposer提出的更大編號的議案,它的值必須也是v。

P2b是一個比P2a更強的約束,也就是說P2b是P2a的充分條件,只要能知足P2b,那P2a就自動知足。但P2b很難被知足,考慮下圖這種狀況,A1接受了議案「趙」,A2即將接受議案「趙」,此時Pb提出了一個議案「錢」,這種狀況下咱們又會遇到跟P2a徹底相同的麻煩。
p6
很明顯,要想知足P2b,咱們必需讓Proposer擁有「預測將來」的能力,這聽起來像在講鬼故事,後面會想辦法解決這一點。 在介紹如何「預測將來」以前,咱們必須先肯定Proposer在提出一個議案時,它的值該如何選取,由於取值的方法決定了「預測」的方法。
一個理所固然的取值方法:找到一個Acceptor的多數派的集合,集合內被接受的議案的值都是v,此時Proposer提出一個新的議案,議案的值必須也是v;若是沒有這樣的多數派集合,那Proposer就職意提。
這個取值方法,徹底能符合P2b,這是一目瞭然的,但問題出在 「預測」上,咱們必須能預測到即將造成多數派的那個議案,若是有誰能作到那就真的是在講鬼故事了。
Proposal提出議案的正確姿式:

P2c:在全部Acceptor中,任意選取半數以上的Acceptor集合,咱們稱這個集合爲S。Proposal新提出的議案(簡稱Pnew)必須符合下面兩個條件之一:
  1)若是S中全部Acceptor都沒有接受過議案的話,那麼Pnew的編號保證惟一性和遞增便可,Pnew的值能夠是任意值。
  2)若是S中有一個或多個Acceptor曾經接受過議案的話,要先找出其中編號最大的那個議案,假設它的編號爲N,值爲V。那麼Pnew的編號必須大於N,Pnew的值必須等於V。

P2c提出議案的規則有點複雜,它真的能知足P2b嗎?至少看上去不是那麼一目瞭然…..老頭子用了概括法來證實P2c能知足P2b,但效果不佳,沒什麼人能看懂,因此下面的證實過程即便你看不懂也必要太沮喪(後面會給出圖文解釋)。
證實題(注意!前方高能):

已知議案 $(m, v_a)$,是集合中第一個被選定的議案,接受這個議案的Acceptor集合爲 $S_m$,在知足P2c的規則2的狀況下,提出了一個新的議案 $(n, v_b)$,其中$n>m$,證實$v_b = v_a$

  1. 證實初始成立:當議案的編號$n = m+1$時,證實$v_b = v_a$
    由於$(m, v_a)$是第一個被選定的議案,所以在$m+1$提出以前,$m$必然是集羣當中編號最大的議案。
     根據P2c的規則2,議案$(m+1,v_b)$可以被提出,是由於存在一個多數派集合$S_n$,這個集合中,編號最大的議案的值爲$v_b$。由於$Sm$和$Sn$都是多數派集合,因此他們一定存在交集。交集中的Acceptor一定都接受了$(m,v_a)$,$m$是整個集羣最大的編號,固然也是$S_n$中最大的編號,根據P2c的規則2,議案$m+1$的值只能是$v_a$,若$v_b$不等於$v_a$,將致使矛盾,所以$v_b = v_a$

  2. 當$n > m+1$時,假設編號從$m+1$到$n-1$的議案的值都是$v_a$,證實$v_b = v_a$
     編號爲$m+1$到$n-1$的議案提出後,咱們沒辦法判斷究竟那一個議案會被選定,但有一點是能夠確定的:全部接受了$v_a$的Acceptor構成了一個新的集合$S_{n-1}$,這個集合包含了集合$Sm$中的全部Acceptor,$S_{n-1}$顯然是一個多數派集合,這個集合接受的議案的編號在$m$到$n-1$之間,並且值爲$v_a$。沒有包含在集合$S_{n-1}$中的Acceptor所接受的議案必定小於$m$。
     根據P2c的規則2,議案$(n,v_b)$可以被提出,那麼必定存在一個多數派集合$Sn$,$Sn$中接受的最大編號的議案的值爲$v_b$。由於$S_n$和都$S_{n-1}$是多數派集合,因此他們一定存在交集。交集中的議案的最大編號必定在$m$到$n-1$之間。所以$S_n$集合中編號最大的議案必定位於交集內。根據P2c的規則,此時$v_b$一定等於$v_a$。

這個證實過程,若是你能看懂,請受我三跪。。。
接下來,上圖,舉例說明。
假設有一個議案(3,Va)提交後,這個議案成爲了被Acceptor集羣選定的第一個議案 ,那此時集羣的狀態可能會以下圖所示:
p7
一共5個Acceptor,有3個Acceptor接受了議案(3,Va),剛剛過半。此時有一個編號爲4的議案要提出,根據P2c的規則2,首先選一個過半的集合,就選上圖中藍色線圈出來的A3,A4,A5好了(任意選),這個集合中編號最大的議案是(3,Va),所以新提出的議案一定爲(4,Va)。符合P2b。
議案(4,Va)提出後,集羣的狀態多是下面這樣:
p8
此時再提出編號爲5或6,7,8,9,10的議案,這個議案的值一定也是Va(不信的話請舉出反例),符合P2b。依此類推。。。
由此可證,P2c是可以知足P2b的!!!
想一想看P2,P2a,P2b中爲何必定要有「更大編號的」這幾個扎眼的字眼,此時你應該能有一點感受了,可能你會把它理解成「後提出的」,若是你是這樣理解的話,請往下看。
有些童鞋確定早就已經想到了:當議案(3,Va)提交後,這個議案成爲了被Acceptor集羣選定的第一個議案,此時集羣的狀態有沒有多是下面這樣?
p9
注意,這時議案(4,Vb)纔是集羣當中的編號最大的議案,要是這樣就糟糕了!當咱們提出編號爲5的議案時,它的取值就有多是Vb,致使沒法知足P2b。
爲了保證不出現這種狀況,就要用到前面提到的「預測將來」的能力。跟P2c的議案規則相配套的,須要預測的將來是:

當一個議案在提出時(即便已經在發送的半路上了),它必須可以知道當前已經提出的議案的最大編號。

這樣的話,議案(3,Va)提交時,就會知道有一個(4,Vb)的議案已經提交了,而後將本身的編號改爲5或更大編號提交,一切就完美了。
可是你知道的,咱們並不可能真的預測將來,換個思路,議案確定是要提交給Acceptor的,只要由Acceptor來保證議案編號的順序就OK了。因而有:

議案(n,v)在提出前,必須將本身的編號通知給半數以上的Acceptor。收到通知的Acceptor將n跟本身以前收到的通知進行比較,若是n更大,就將n確認爲最大編號。當半數以上Acceptor確認n是最大編號時,議案(n,v)才能正式提交。

兩個編號不一樣的議案,不可能同時被確認爲最大編號,證實略。
可是實際環境上,上面的條件還不足以保證議案被接受的順序,好比議案(n,Va)被確認爲最大編號後,開始向Acceptor發送,此時(n+1,Vb)提出,因爲網絡速度的緣由,(n+1,Vb)可能比(n,Va)更早被Acceptor接收到。
所以Acceptor收到一個新的編號n,在確認n比本身以前收到的編號大時,必須作出承諾(Promise):再也不接受比n小的議案。
這個承諾會致使部分漏網之魚(在發送途中被搶走最大編號的議案),沒法造成多數派。
例以下圖所示:有一個在途的議案(1,Va),當A2和A3對議案(2,Vb)作出承諾的同時,(1,Va)就失去了造成多數派的權利。
p10

至此,咱們就造成了一個完整的算法(具體實現請自行搜索PhxPaxos)。

後記

算法原文中,將Promise看作是P2c的具體實現,而咱們將Promise當作是彌補P2c的補充條件。這二者沒有質的差異,只是角度不一樣,我我的認爲後一種更容易被理解,因此採用了後一種。不過採用後一種會遇到下面的麻煩:
按下面的順序提交議案:

①議案(1,Va)向A1發送Prepare,得到A1的承諾。
②議案(2,Vb)向A1發送Prepare,得到A1的承諾。
③發送議案(1,Va)

此時A1會拒絕議案(1,Va)
p11
採用後一種解釋的話,會發現A1拒絕議案(1,Va)是違反了P1的,而採用前一種解釋則不違反P1。(這不過是個文字遊戲,我已經懶的去思考了,就這樣吧)

若是咱們將半數以上的Acceptor對同一個議案(n,v)作出承諾的狀態稱做是「鎖定」狀態。那麼這個「鎖定」狀態具備如下性質:
排它性: 全部比n小的議案都不容許提交,已經在途的議案,則不容許其造成多數派。
惟一性: 任意時刻,全局只有一個議案能得到「鎖定」狀態。
原子性: 議案n從鎖定狀態變爲非鎖定狀態的過程是原子的,議案n+1從非鎖定狀態變動爲鎖定狀態的過程也是原子的。 我相信,正是上面的這三條性質保證了一致性。 最後,感謝老頭子給出的如此精彩的算法。

相關文章
相關標籤/搜索