PAXOS

Paxos算法萊斯利·蘭伯特英語:Leslie LamportLaTeX中的“La”)於1990年提出的一種基於消息傳遞且具有高度容錯特性的一致性算法。

問題和假設

分佈式系統中的節點通信存在兩種模型:共享內存(Shared memory)和消息傳遞(Messages passing)。基於消息傳遞通信模型的分佈式系統,不可避免的會發生以下錯誤:進程可能會慢、被殺死或者重啓,消息可能會延遲、丟失、重複,在基礎Paxos場景中,先不考慮可能出現消息篡改即拜占庭錯誤的情況。Paxos算法解決的問題是在一個可能發生上述異常的分佈式系統中如何就某個值達成一致,保證不論發生以上任何異常,都不會破壞決議的一致性。一個典型的場景是,在一個分佈式數據庫系統中,如果各節點的初始狀態一致,每個節點都執行相同的操作序列,那麼他們最後能得到一個一致的狀態。爲保證每個節點執行相同的命令序列,需要在每一條指令上執行一個“一致性算法”以保證每個節點看到的指令一致。一個通用的一致性算法可以應用在許多場景中,是分佈式計算中的重要問題。因此從20世紀80年代起對於一致性算法的研究就沒有停止過。

爲描述Paxos算法,Lamport虛擬了一個叫做Paxos的希臘城邦,這個島按照議會民主制的政治模式制訂法律,但是沒有人願意將自己的全部時間和精力放在這種事情上。所以無論是議員,議長或者傳遞紙條的服務員都不能承諾別人需要時一定會出現,也無法承諾批准決議或者傳遞消息的時間。但是這裏假設沒有拜占庭將軍問題(Byzantine failure,即雖然有可能一個消息被傳遞了兩次,但是絕對不會出現錯誤的消息);只要等待足夠的時間,消息就會被傳到。另外,Paxos島上的議員是不會反對其他議員提出的決議的。

對應於分佈式系統,議員對應於各個節點,制定的法律對應於系統的狀態。各個節點需要進入一個一致的狀態,例如在獨立Cache對稱多處理器系統中,各個處理器讀內存的某個字節時,必須讀到同樣的一個值,否則系統就違背了一致性的要求。一致性要求對應於法律條文只能有一個版本。議員和服務員的不確定性對應於節點和消息傳遞通道的不可靠性。

 算法

 算法的提出與證明

首先將議員的角色分爲proposers,acceptors,和learners(允許身兼數職)。proposers提出提案,提案信息包括提案編號和提議的value;acceptor收到提案後可以接受(accept)提案,若提案獲得多數acceptors的接受,則稱該提案被批准(chosen);learners只能“學習”被批准的提案。劃分角色後,就可以更精確的定義問題:

  1. 決議(value)只有在被proposers提出後才能被批准(未經批准的決議稱爲“提案(proposal)”);
  2. 在一次Paxos算法的執行實例中,只批准(chosen)一個value;
  3. learners只能獲得被批准(chosen)的value。

另外還需要保證progress。這一點以後再討論。

作者通過不斷加強上述3個約束(主要是第二個)獲得了Paxos算法。

批准value的過程中,首先proposers將value發送給acceptors,之後acceptors對value進行接受(accept)。爲了滿足只批准一個value的約束,要求經“多數派(majority)”接受的value成爲正式的決議(稱爲“批准”決議)。這是因爲無論是按照人數還是按照權重劃分,兩組“多數派”至少有一個公共的acceptor,如果每個acceptor只能接受一個value,約束2就能保證。

於是產生了一個顯而易見的新約束:

P1:一個acceptor必須接受(accept)第一次收到的提案。

注意P1是不完備的。如果恰好一半acceptor接受的提案具有value A,另一半接受的提案具有value B,那麼就無法形成多數派,無法批准任何一個value。

約束2並不要求只批准一個提案,暗示可能存在多個提案。只要提案的value是一樣的,批准多個提案不違背約束2。於是可以產生約束P2:

P2:一旦一個具有value v的提案被批准(chosen),那麼之後批准(chosen)的提案必須具有value v。

注:通過某種方法可以爲每個提案分配一個編號,在提案之間建立一個全序關係,所謂“之後”都是指所有編號更大的提案。

如果P1和P2都能夠保證,那麼約束2就能夠保證。

批准一個value意味着多個acceptor接受(accept)了該value.因此,可以對P2進行加強:

P2a:一旦一個具有value v的提案被批准(chosen),那麼之後任何acceptor再次接受(accept)的提案必須具有value v。

由於通信是異步的,P2a和P1會發生衝突。如果一個value被批准後,一個proposer和一個acceptor從休眠中甦醒,前者提出一個具有新的value的提案。根據P1,後者應當接受,根據P2a,則不應當接受,這中場景下P2a和P1有矛盾。於是需要換個思路,轉而對proposer的行爲進行約束:

P2b:一旦一個具有value v的提案被批准(chosen),那麼以後任何proposer提出的提案必須具有value v。

由於acceptor能接受的提案都必須由proposer提出,所以P2b蘊涵了P2a,是一個更強的約束。

但是根據P2b難以提出實現手段。因此需要進一步加強P2b。

假設一個編號爲m的value v已經獲得批准(chosen),來看看在什麼情況下對任何編號爲n(n>m)的提案都含有value v。因爲m已經獲得批准(chosen),顯然存在一個acceptors的多數派C,他們都接受(accept)了v。考慮到任何多數派都和C具有至少一個公共成員,可以找到一個蘊涵P2b的約束P2c:

P2c:如果一個編號爲n的提案具有value v,那麼存在一個多數派,要麼他們中所有人都沒有接受(accept)編號小於n 
的任何提案,要麼他們已經接受(accept)的所有編號小於n的提案中編號最大的那個提案具有value v。

可以用數學歸納法證明P2c蘊涵P2b:

假設具有value v的提案m獲得批准,當n=m+1時,採用反證法,假如提案n不具有value v,而是具有value w,根據P2c,則存在一個多數派S1,要麼他們中沒有人接受過編號小於n的任何提案,要麼他們已經接受的所有編號小於n的提案中編號最大的那個提案是value w。由於S1和通過提案m時的多數派C之間至少有一個公共的acceptor,所以以上兩個條件都不成立,導出矛盾從而推翻假設,證明了提案n必須具有value v;

若(m+1)..(N-1)所有提案都具有value v,採用反證法,假如新提案N不具有value v,而是具有value w',根據P2c,則存在一個多數派S2,要麼他們沒有接受過m..(N-1)中的任何提案,要麼他們已經接受的所有編號小於N的提案中編號最大的那個提案是value w'。由於S2和通過m的多數派C之間至少有一個公共的acceptor,所以至少有一個acceptor曾經接受了m,從而也可以推出S2中已接受的所有編號小於n的提案中編號最大的那個提案的編號範圍在m..(N-1)之間,而根據初始假設,m..(N-1)之間的所有提案都具有value v,所以S2中已接受的所有編號小於n的提案中編號最大的那個提案肯定具有value v,導出矛盾從而推翻新提案n不具有value v的假設。根據數學歸納法,我們證明了若滿足P2c,則P2b一定滿足。

P2c是可以通過消息傳遞模型實現的。另外,引入了P2c後,也解決了前文提到的P1不完備的問題。

 算法的內容

要滿足P2c的約束,proposer提出一個提案前,首先要和足以形成多數派的acceptors進行通信,獲得他們進行的最近一次接受(accept)的提案(prepare過程),之後根據回收的信息決定這次提案的value,形成提案開始投票。當獲得多數acceptors接受(accept)後,提案獲得批准(chosen),由proposer將這個消息告知learner。這個簡略的過程經過進一步細化後就形成了Paxos算法。

在一個paxos實例中,每個提案需要有不同的編號,且編號間要存在全序關係。可以用多種方法實現這一點,例如將序數和proposer的名字拼接起來。如何做到這一點不在Paxos算法討論的範圍之內。

如果一個沒有chosen過任何proposer提案的acceptor在prepare過程中回答了一個proposer針對提案n的問題,但是在開始對n進行投票前,又接受(accept)了編號小於n的另一個提案(例如n-1),如果n-1和n具有不同的value,這個投票就會違背P2c。因此在prepare過程中,acceptor進行的回答同時也應包含承諾:不會再接受(accept)編號小於n的提案。這是對P1的加強:

P1a:當且僅當acceptor沒有迴應過編號大於n的prepare請求時,acceptor接受(accept)編號爲n的提案。

現在已經可以提出完整的算法了。

 決議的提出與批准

通過一個決議分爲兩個階段:

  1. prepare階段:
    1. proposer選擇一個提案編號n並將prepare請求發送給acceptors中的一個多數派;
    2. acceptor收到prepare消息後,如果提案的編號大於它已經回覆的所有prepare消息,則acceptor將自己上次接受的提案回覆給proposer,並承諾不再回復小於n的提案;
  2. 批准階段:
    1. 當一個proposer收到了多數acceptors對prepare的回覆後,就進入批准階段。它要向回覆prepare請求的acceptors發送accept請求,包括編號n和根據P2c決定的value(如果根據P2c沒有已經接受的value,那麼它可以自由決定value)。
    2. 在不違背自己向其他proposer的承諾的前提下,acceptor收到accept請求後即接受這個請求。

這個過程在任何時候中斷都可以保證正確性。例如如果一個proposer發現已經有其他proposers提出了編號更高的提案,則有必要中斷這個過程。因此爲了優化,在上述prepare過程中,如果一個acceptor發現存在一個更高編號的提案,則需要通知proposer,提醒其中斷這次提案。

 實例

用實際的例子來更清晰地描述上述過程:

有A1, A2, A3, A4, A5 5位議員,就稅率問題進行決議。議員A1決定將稅率定爲10%,因此它向所有人發出一個草案。這個草案的內容是:

現有的稅率是什麼?如果沒有決定,則建議將其定爲10%.時間:本屆議會第3年3月15日;提案者:A1

在最簡單的情況下,沒有人與其競爭;信息能及時順利地傳達到其它議員處。

於是, A2-A5迴應:

我已收到你的提案,等待最終批准

而A1在收到2份回覆後就發佈最終決議:

稅率已定爲10%,新的提案不得再討論本問題。

這實際上退化爲二階段提交協議。

現在我們假設在A1提出提案的同時, A5決定將稅率定爲20%:

現有的稅率是什麼?如果沒有決定,則建議將其定爲20%.時間:本屆議會第3年3月15日;提案者:A5

草案要通過侍從送到其它議員的案頭. A1的草案將由4位侍從送到A2-A5那裏。現在,負責A2和A3的侍從將草案順利送達,負責A4和A5的侍從則不上班. A5的草案則順利的送至A4和A3手中。

現在, A1, A2, A3收到了A1的提案; A4, A3, A5收到了A5的提案。按照協議, A1, A2, A4, A5將接受他們收到的提案,侍從將拿着

我已收到你的提案,等待最終批准

的回覆回到提案者那裏。

而A3的行爲將決定批准哪一個。

 情況一

假設A1的提案先送到A3處,而A5的侍從決定放假一段時間。於是A3接受並派出了侍從. A1等到了兩位侍從,加上它自己已經構成一個多數派,於是稅率10%將成爲決議. A1派出侍從將決議送到所有議員處:

稅率已定爲10%,新的提案不得再討論本問題。

A3在很久以後收到了來自A5的提案。由於稅率問題已經討論完畢,他決定不再理會。但是他要抱怨一句:

稅率已在之前的投票中定爲10%,你不要再來煩我!

這個回覆對A5可能有幫助,因爲A5可能因爲某種原因很久無法與與外界聯繫了。當然更可能對A5沒有任何作用,因爲A5可能已經從A1處獲得了剛纔的決議。

 情況二

依然假設A1的提案先送到A3處,但是這次A5的侍從不是放假了,只是中途耽擱了一會。這次, A3依然會將"接受"回覆給A1.但是在決議成型之前它又收到了A5的提案。這時協議有兩種處理方式:

1.如果A5的提案更早,按照傳統應該由較早的提案者主持投票。現在看來兩份提案的時間一樣(本屆議會第3年3月15日)。但是A5是個惹不起的大人物。於是A3回覆:

我已收到您的提案,等待最終批准,但是您之前有人提出將稅率定爲10%,請明察。

於是, A1和A5都收到了足夠的回覆。這時關於稅率問題就有兩個提案在同時進行。但是A5知道之前有人提出稅率爲10%.於是A1和A5都會向全體議員廣播:

 稅率已定爲10%,新的提案不得再討論本問題。

一致性得到了保證。

2. A5是個無足輕重的小人物。這時A3不再理會他, A1不久後就會廣播稅率定爲10%.

 情況三

在這個情況中,我們將看見,根據提案的時間及提案者的權勢決定是否應答是有意義的。在這裏,時間和提案者的權勢就構成了給提案編號的依據。這樣的編號符合"任何兩個提案之間構成偏序"的要求。

A1和A5同樣提出上述提案,這時A1可以正常聯繫A2和A3; A5也可以正常聯繫這兩個人。這次A2先收到A1的提案; A3則先收到A5的提案. A5更有權勢。

在這種情況下,已經回答A1的A2發現有比A1更有權勢的A5提出了稅率20%的新提案,於是回覆A5說:

我已收到您的提案,等待最終批准。

而回復了A5的A3發現新的提案者A1是個小人物,不予理會。

A1沒有達到多數,A5達到了,於是A5將主持投票,決議的內容是A5提出的稅率20%.

如果A3決定平等地對待每一位議員,對A1做出"你之前有人提出將稅率定爲20%"的回覆,則將造成混亂。這種情況下A1和A5都將試圖主持投票,但是這次兩份提案的內容不同。

這種情況下, A3若對A1進行回覆,只能說:

有更大的人物關注此事,請等待他做出決定。

另外,在這種情況下, A4與外界失去了聯繫。等到他恢復聯繫,並需要得知稅率情況時,他(在最簡單的協議中)將提出一個提案:

現有的稅率是什麼?如果沒有決定,則建議將其定爲15%.時間:本屆議會第3年4月1日;提案者:A4

這時,(在最簡單的協議中)其他議員將會回覆:

稅率已在之前的投票中定爲20%,你不要再來煩我!

 決議的發佈

一個顯而易見的方法是當acceptors批准一個value時,將這個消息發送給所有learner。但是這個方法會導致消息量過大。

由於假設沒有Byzantine failures,learners可以通過別的learners獲取已經通過的決議。因此acceptors只需將批准的消息發送給指定的某一個learner,其他learners向它詢問已經通過的決議。這個方法降低了消息量,但是指定learner失效將引起系統失效。

因此acceptors需要將accept消息發送給learners的一個子集,然後由這些learners去通知所有learners。

但是由於消息傳遞的不確定性,可能會沒有任何learner獲得了決議批准的消息。當learners需要了解決議通過情況時,可以讓一個proposer重新進行一次提案。注意一個learner可能兼任proposer。

 Progress的保證

根據上述過程當一個proposer發現存在編號更大的提案時將終止提案。這意味着提出一個編號更大的提案會終止之前的提案過程。如果兩個proposer在這種情況下都轉而提出一個編號更大的提案,就可能陷入活鎖,違背了Progress的要求。這種情況下的解決方案是選舉出一個leader,僅允許leader提出提案。但是由於消息傳遞的不確定性,可能有多個proposer自認爲自己已經成爲leader。Lamport在The Part-Time Parliament一文中描述並解決了這個問題。

 其他

微軟公司爲簡化的Paxos算法申請了專利[2]。但專利中公開的技術和本文所描述的不盡相同。

谷歌公司(Google公司)在其分佈式鎖服務(Chubby lock)中應用了Paxos算法[3]。Chubby lock應用於大表(Bigtable),後者在谷歌公司所提供的各項服務中得到了廣泛的應用[4]

    PAXOS是一種基於消息傳遞且具有高度容錯特性的一致性算法。算法本身用語言描述極其精簡:

phase 1
a) proposer向網絡內超過半數的acceptor發送prepare消息
b) acceptor正常情況下回復promise消息
phase 2
a) 在有足夠多acceptor回覆promise消息時,proposer發送accept消息
b) 正常情況下acceptor回覆accepted消息

PAXOS中有三類角色Proposer、Acceptor及Learner,主要交互過程在Proposer和Acceptor之間,做成圖便如下圖所示:
其中1,2,3,4代表順序。
以下圖描述多Proposer的情況,T代表時間軸,圖中僅畫全一個Proposer與Acceptor的關係:

A3在T1發出accepted給A1,然後在T2收到A5的prepare,在T3的時候A1才通知A5最終結果(稅率10%)。這裏會有兩種情況:
1. A5發來的N5小於A1發出去的N1,那麼A3直接拒絕(reject)A5
2. A5發來的N5大於A1發出去的N1,那麼A3回覆promise,但帶上A1的(N1, 10%)
最終A5也會接受10%
上圖描述,如果已經Promise一個更大的N,那麼會直接Reject更小的N
上述描述了,即使Promise了一個N,如果在未Accepted前,再收到一個更大的N,那麼依舊會Reject那個即使已經Promise的N
總流程圖氪概括如下:
PAXOS協議用於微信PaxosStore中,每分鐘調用Paxos協議過程數十億次量級。


    作爲現在共識算法設計的鼻祖,以最初論文的難懂(算法本身並不複雜)出名。

算法中將節 點分爲三種類型:

proposer:提出一個提案,等待大家批准爲結案。往往是客戶端擔任該角色;

acceptor:負責對提案進行投票。往往是服務端擔任該角色;

learner:被告知結案結果,並與之統一,不參與投票過程。可能爲客戶端或服務端。

並且,算法需要滿足 safety 和 liveness 兩方面的約束要求(實際上這兩個基礎屬性是大部分 分佈式算法都該考慮的):

safety:保證決議結果是對的,無歧義的,不會出現錯誤情況。

    決議(value)只有在被 proposers 提出的 proposal 才能被最終批准;

    在一次執行實例中,只批准(chosen)一個最終決議,意味着多數接受(accept) 的結果能成爲決議;

liveness:保證決議過程能在有限時間內完成。

    決議總會產生,並且 learners 能獲得被批准(chosen)的決議。

基本過程包括 proposer 提出提案,先爭取大多數 acceptor 的支持,超過一半支持時,則發送 結案結果給所有人進行確認。

一個潛在的問題是 proposer 在此過程中出現故障,可以通過超 時機制來解決。

極爲湊巧的情況下,每次新的一輪提案的 proposer 都恰好故障,系統則永遠 無法達成一致(概率很小)。

Paxos 能保證在超過1/2的正常節點存在時,系統能達成共識。

發佈了21 篇原創文章 · 獲贊 2 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章