Paxos 學習總結
近期學習了分布式領域的重要算法Paxos,這裏羅列下關鍵點當作總結。自己水平有限,難免存在謬誤,懇請讀者指正。本篇不包含Paxos的基本理論介紹。Paxos基礎能夠參考以下的學習資料章節。
1 Paxos圖示
繪圖總結了原始的Paxos算法,主要來源於Paxos Made Simple。
沒有Leader也沒有不論什麽優化過程,而且把Proposer/Acceptor/Learners分開表示。
這僅僅是為了更easy梳理出三者之間的關系。我們僅僅要知道實際Paxosproject中會把這三者重疊處理就能夠了。
Phase 1註:
1 Proposer向一個多數派Acceptor發送階段1a消息時。所謂多數派可能是所有Acceptor也可能僅僅是一個大於50%的Acceptor集合。
2 階段1可能產生多個Master,在若幹Master進入階段2後,仍有可能有新的攪局者進入階段1,Paxos的各個階段是能夠互相重疊的。也就是說可能出現某個Proposer處於階段1而還有一個Proposer處於階段2中,且這兩個Proposer也全然有可能由於訪問同一個Acceptor而互相影響。
處於階段1和階段2的Proposer之間的作用就是通過圖中的虛線完畢的:一個處於階段2的Proposer由於給某個Acceptor的(num value)賦值導致還有一個處於階段1的Proposer不得不放棄自己的取值轉而使用這個階段2的Proposer的取值。
Phase 2註:
Q:假設多個Master已經發送2a消息,是不是未來的確定性取值一定在這幾個Proposer中產生?
A:當然不是。由於隨時可能會有新攪局者在此時增加階段1,如果x(3) y(5) 兩個處於階段2的Proposer(3,5是其n值)已經發出2a階段消息,但龜速網絡導致x的包在路上耽誤了。而y的包也僅僅到達了1個Acceptor,其它的包在路上。此時一個新的攪局者z增加階段1,發送了更大的n(如果是6)。由於n大於Max(Max是5,6>5成立)所以它迅速獲得資格進入階段2並使用自己的值提交Acceptor。此後即使x和y的龜速消息再到達也無濟於事了。由於Acceptor的Max已經被z的階段1消息擡高了。x和y的階段2a消息會被Acceptor丟棄。當然了xy戰勝z的概率還是非常大的,由於僅僅要xy的2a階段消息能在z的階段a1階段到達Acceptor前,搶占一半以上的Acceptor,就能夠了。由於這樣就有一半多的Acceptor會阻擋掉z的階段1a消息(作用路徑是圖中虛線),迫使z無法使用自己的值。而僅僅能接受y已經設的值。
Phase 3註:
1 學習過程是可能會亂序的。所以必須依照id(也就是圖中Phase3的num)遞增順序學習。
2 階段3的學習過程,消息傳遞時有多種選擇,比方簡單的多對多,m個Acceptor和n個Learner建立m*n條消息鏈路,然後發送消息。
或者多對一對多。即全部的Acceptor將消息發送給一個固定的Learner,然後這個Learner再廣播給全部其它Learner。
可參考<<Paxos Made Simple>> 2.4節
2 學習資料
我認為讀論文大概是學習Paxos的最好的方式了,讀不懂怎麽辦?一篇一篇換著讀。以下4篇論文從不同的方面論述了Paxos原理及應用場景。內容雖有反復但也有非常大互補性,細致閱讀這4篇基本能夠對Paxos有全面的理解。我認為作為最早發表Paxos算法的<<The Part-Time Parliament>>反而能夠略過不讀,或者在全面理解Paxos後再當作歷史讀物看看。
1 <<Paxos Made Simple>>(原文,中文)
這是Lamport 2001年寫的基本上就是<<The Part-Time Parliament>>的簡化版本號。我認為這篇比較通俗易懂。同一時候中譯文質量也非常高並且關鍵位置譯者加入了一些凝視有助於理解。在這裏面Lamport強調的是Paxos的兩階段運行過程,最後的Phase 3沒有被當作獨立的階段(Lamport用獨立章節2.3介紹了Phase3),只是在其它文獻中很多其它把Paxos看作3階段的過程,第3階段也就是學習階段。
個人認為3階段更方便理解所以在作圖的時候分為三階段了。
2 <<The Chubby lock service for loosely-coupled distributed systems>>(原文。中文)
文章作者就是提出“世上僅僅有一種一致性算法,那就是Paxos”的Mike Burrows。
他在Google也是Paxos方面的先驅。而這篇Chubby也被以下Google自家的<<Paxos Made Live>>作為第一參考文獻來引用。這篇Googleproject實踐經驗主要介紹了Paxos在project中的應用。Paxos的兩大用途之中的一個就是分布式鎖,Google使用Paxos完畢了其內部的分布式鎖服務Chubby。
這篇文章環繞3個主題:1什麽是分布式鎖 2Paxos怎樣作為分布式鎖來使用 3project實踐中的各種困難怎樣克服。同一時候我們也能從這篇裏尋覓到GFS和Bigtable是怎樣與Chubby配合的。
3 <<Paxos Made Live>>(原文,中文)
這篇應該算是Google對於其內部使用Paxos的總結,相比於Chubby那篇總結性更強,同一時候對於Paxos算法本身也有一定介紹 。本篇文章在Chubby篇之後寫成。其參考文檔裏的第一篇就是那篇Chubby。本文非常適合作為學習完Paxos原理後的第一篇文章來閱讀。
通過project師視角描寫敘述的Paxos更easy被project師理解(以後看到不論什麽Engineering Perspective的字樣要註意了,非常可能會有驚喜哦)。比方在解釋為什麽實現一個Paxos系統如此困難時,他們是這麽說的“盡管Paxos算法本身用一頁偽代碼就能夠描寫敘述下來,可是我們的完整實現包括了數千行C++代碼。代碼膨脹並不簡單地是由於我們採用了C++來代替偽代碼。也不是由於我們代碼風格的繁瑣。將該算法轉換為一個實際的,產品級系統須要引入非常多的features和優化(有些是已經發表過的研究成果。有些不是)。”。對於Paxos的“在某個值上達成一致”這種使用方法,它也明白介紹成“在我們的系統中,須要在‘(多副本)日誌中的下一條記錄是哪條’上達成一致。”。這種文章讀起來真是酣暢淋漓!
4 <<Consensus on Transaction Commit>>(原文,中文)
兩位圖靈獎作者合著的文章必須不能錯過。這篇同一時候由兩位理論創始人介紹了自家的2PC和Paxos,然後提出了一個2PC和Paxos雜交出的Paxos提交算法。
通過正常情況和異常情況的性能對照,得出結論“兩階段提交實際上與僅僅有一個協調者Paxos提交是同構的”。
這篇文章能夠作為學習2PC和Paxos的一篇不錯的文章。
5 <<布式鎖服務>>(鏈接)
這篇要比上面4篇來頭小多了,是國內大學生論文性的文章,作者的團隊基本實現了一個簡化版的Chubby,可是由於文章來源於實踐還是非常有意思,非常值得一讀。
3 Paxos實例與Mutl-Paxos
一個Paxos實例就是一次完整的Paxos執行過程。
即完整的執行一遍Phase1~3(如果沒有優化),向Paxos提交一個value值就意味著在提交該value值時會啟動一個Paxos執行實例。實際系統會以Paxos為基礎來實如今一系列value值上的一致性,比方把一個日誌文件,逐條的分發到一個多機的備份環境,每次分發一條日誌可能就是執行一次Paxos實例。多個Paxos實例連續執行也就是Mutl-Paxos。Mutl-Paxos有幾種優化方法。能夠參考<<Paxos Made Live>>
4.2及5.2節。
4 編號n的選擇
Proposer須要選出一個遞增的唯一序列號,有一種很形象直觀的方法:在一個具有n個副本的系統中,首先為每一個副本r分配一個在0和n-1之間的標識符Ir。副本r能夠選擇一個比它全部已知的序列號大的最小s作為序號,同一時候保證s mod n=Ir。舉例來說,在一個5副本的Paxos系統裏,能夠為副本1制作一個序列號隊列0,5,10。15...為副本2制作好序列號隊列1。6,11,16...以此類推。當Proposer須要增大序列號時,即從自己的隊列裏順序取出一個就可以,這樣就保證了每一個Proposer取出的都是全局最大並且與別人不反復的編號了。可參考<<Paxos
Made Live>> 4.1節
5 Paxos優化
這裏優化的基礎和原始Paxos算法有微小差異。這裏如果全部的Proposer是由一個固定的Leader來發起請求的。選出一個Leader來作為唯一的提案提出者能夠防止活鎖,活鎖是因為不斷的新Proposer提出更高編號的階段1a請求而導致每一個Proposer完畢階段2a的消息。
關於Leader可參考<<Paxos Made Simple>> 2.4節
1 能夠通過讓Leader僅給半數以上的Acceptor發送階段2a消息來降低正常情況下的消息數。Leader僅僅要從半數以上的Acceptor接受到階段2b消息,就能夠知道v已經被選定。假設未收到足夠多的階段2b消息。再向其余的Acceptor發送階段2a消息。這樣的方法可參考<<Consensus on Transaction Commit>> 4.1節
2 能夠將多個Paxos實例串聯起來以降低消息數目。
假設在多個實例運行中,協調者(也就是Leader)沒有變化。那麽階段1a 1b的消息能夠被忽略。
為了從這個優化中獲益,Muti-Paxos算法會設計非常久才會選舉一次Leader(這事實上就是Fast Paxos的基本思想了,事實上這也比較符合直覺,選舉Leader事實上不是保持一致性的主要工作。而是為了應對異常而已。在非常多實現裏。選舉Leader這一步往往是被簡化的)。這樣的方法可參考<<Paxos Made Simple>> 4.2節
3 Acceptor在選定v後直接廣播給learner,而不是先發送給Leader。再由Leader轉發給Learner。這樣以額外消息數為代價將階段3的消息延遲消除,與Leader類似,在這些進程收到來自半數以上的acceptor的階段2b消息後。就能夠獲知被選定的value值了。這樣的方法可參考<<Consensus on Transaction Commit>> 4.1節
6 Paxos使用方法
我認為<<大規模分布式存儲系統>>中的說法比較準確的,Paxos有兩種使用方法:
1 實現全局的鎖服務或者命名和配置服務,比如Google Chubby以及Apache Zookeeper(Zookeeper使用了Zab協議,其作者覺得這不是Paxos。可參考<<a simple totally ordered broadcast protocol>>,可是通過分析Zookeeper的工作方式也有人覺得Zab是Paxos的一種簡化形式)。
2 將用戶數據拷貝到多個數據中心,比如Google Megastore以及Google Spanner。
對於用途2相對好理解。由於原始Paxos演示樣例就展示了復制數據到多備份機的功能。對於用途1。能夠閱讀Chubby論文來學習:首先要明確Paxos分布式鎖是建議性鎖而單機的類Mutex是強制鎖,作為建議性鎖要求使用方在使用資源前主動詢問鎖的狀況,建議鎖和資源在物理上是分離的。防君子不防小人。
我們如果建議性鎖與Mutex一樣,會有一個類似id的身份標識。
分布式鎖的id不是"123abc"這種無意義標識符。而是高大上的Unix文件路徑格式。從"123abc"變成"/123/abc",這樣id變得很直觀。由於能夠通過id直接表示出鎖是哪個組哪個應用的哪個功能的。這種名字"/projectX/groupA/add"明顯比"123abc"要直白了很多。
這種路徑同一時候使得鎖具備了層級父子關系也很easy引入很多其它特性。要明確這種表示法其本質上與Unix的文件系統關系不大,由於"/"全然能夠用別的符號比方“|”取代,比方"|projectX|groupA|app",寫成反斜線全然是為了照應程序猿的直覺,降低學習成本(Chubby 論文是這麽說的“由於Chubby的名字空間結構類似於文件系統......同一時候也減少了培訓Chubby用戶的難度。”)。所謂鎖的id表示法更雅觀的名字應該叫命名空間,這類似於編程語言的命名空間:通過層級關系終於定位一個類/變量的方法。
看完了命名空間,那Paxos到底怎樣當作分布式鎖使用呢?Paxos的基本用途就是使得多臺機器在一個確定性取值上達成一致。如果如今有一個5備份機的Paxos環境,我們就叫它5Paxos。有一個外部項目Xapp要使用5Paxos作為分布式鎖,Xapp要鎖住自己的某個服務http://www.xxx.com/make-id。它須要先向5Paxos發送一個lock值。和自己申請的路徑"/Project/Xapp/make-id"。
這樣5Paxos就在這個確定性取值(“/Project/Xapp/make-id”是lock還是unlock)上達成一致。當前的一致就是lock。僅僅有當Xapp再發送一個unlock後。5Paxos在新的取值unlock上又一次達成一致。由於是建議性鎖,所以Xapp內部不論什麽要使用這個make-id接口的服務必須在使用前主動檢查一下5Paxos存儲的"/Project/Xapp/make-id"鎖定狀態是lock還是unlock。並遵守“在unlock時不訪問這個服務”的君子協定。這樣鎖的作用就終於達成了。而作為分布式鎖的最大長處就是高可用性,5Paxos的5臺備份機中壞掉2臺全然不影響這個鎖的正常工作。
Paxos 學習總結