Paxos算法(一)- Basic Paxos


提到分佈式算法,就不得不提 Paxos 算法,在過去幾十年裏,它基本上是分佈式共識的代名詞,因爲當前最常用的一批共識算法都是基於它改進的。比如,Fast Paxos 算法、Cheap Paxos 算法、Raft 算法等等。而很多同學都會在準確和系統理解 Paxos 算法上踩坑,比如,只知道它可以用來達成共識,但不知道它是如何達成共識的。


這其實側面說明了 Paxos 算法有一定的難度,可分佈式算法本身就很複雜,Paxos 算法自然也不會例外,當然了,除了這一點,還跟蘭伯特有關。


蘭伯特提出的 Paxos 算法包含 2 個部分:

  • 一個是 Basic Paxos 算法,描述的是多節點之間如何就某個值(提案 Value)達成共識;
  • 另一個是 Multi-Paxos 思想,描述的是執行多個 Basic Paxos 實例,就一系列值達成共識。


可因爲蘭伯特提到的 Multi-Paxos 思想,缺少代碼實現的必要細節(比如怎麼選舉領導者),所以在理解上比較難。


爲了讓你理解 Paxos 算法,接下來我會用 2 節課的時間,分別以 Basic Paxos 和 Multi-Paxos 爲核心,帶你瞭解 Basic Paxos 如何達成共識,以及針對 Basic Paxos 的侷限性 Multi-Paxos 又是如何改進的。今天咱們先來聊聊 Basic Paxos。


在我看來,Basic Paxos 是 Multi-Paxos 思想的核心,說白了,Multi-Paxos 就是多執行幾次 Basic Paxos。所以掌握它之後,你能更好地理解後幾講基於 Multi-Paxos 思想的共識算法(比如 Raft 算法),還能掌握分佈式共識算法的最核心內容,當現在的算法不能滿足業務需求,進行權衡折中,設計自己的算法。


來看一道思考題。


假設我們要實現一個分佈式集羣,這個集羣是由節點 A、B、C 組成,提供只讀 KV 存儲服務。你應該知道,創建只讀變量的時候,必須要對它進行賦值,而且這個值後續沒辦法修改。因此一個節點創建只讀變量後就不能再修改它了,所以所有節點必須要先對只讀變量的值達成共識,然後所有節點再一起創建這個只讀變量。


那麼,當有多個客戶端(比如客戶端 1、2)訪問這個系統,試圖創建同一個只讀變量(比如 X),客戶端 1 試圖創建值爲 3 的 X,客戶端 2 試圖創建值爲 7 的 X,這樣要如何達成共識,實現各節點上 X 值的一致呢?帶着這個問題,我們進入今天的學習。




在一些經典的算法中,你會看到一些既形象又獨有的概念(比如二階段提交協議中的協調者),Basic Paxos 算法也不例外。爲了幫助人們更好地理解 Basic Paxos 算法,蘭伯特在講解時,也使用了一些獨有而且比較重要的概念,提案、準備(Prepare)請求、接受(Accept)請求、角色等等,其中最重要的就是“角色”。因爲角色是對 Basic Paxos 中最核心的三個功能的抽象,比如,由接受者(Acceptor)對提議的值進行投票,並存儲接受的值。


你需要了解的三種角色


在 Basic Paxos 中,有提議者(Proposer)、接受者(Acceptor)、學習者(Learner)三種角色,他們之間的關係如下:




看着是不是有些複雜,其實並不難理解:

  • 提議者(Proposer):提議一個值,用於投票表決。爲了方便演示,你可以把圖 1 中的客戶端 1 和 2 看作是提議者。但在絕大多數場景中,集羣中收到客戶端請求的節點,纔是提議者(圖 1 這個架構,是爲了方便演示算法原理)。這樣做的好處是,對業務代碼沒有入侵性,也就是說,我們不需要在業務代碼中實現算法邏輯,就可以像使用數據庫一樣訪問後端的數據。


  • 接受者(Acceptor):對每個提議的值進行投票,並存儲接受的值,比如 A、B、C 三個節點。 一般來說,集羣中的所有節點都在扮演接受者的角色,參與共識協商,並接受和存儲數據。


講到這兒,你可能會有疑惑:前面不是說接收客戶端請求的節點是提議者嗎?這裏怎麼又是接受者呢?這是因爲一個節點(或進程)可以身兼多個角色。想象一下,一個 3 節點的集羣,1 個節點收到了請求,那麼該節點將作爲提議者發起二階段提交,然後這個節點和另外 2 個節點一起作爲接受者進行共識協商,就像下圖的樣子:















  • 學習者(Learner):被告知投票的結果,接受達成共識的值,存儲保存,不參與投票的過程。一般來說,學習者是數據備份節點,比如“Master-Slave”模型中的 Slave,被動地接受數據,容災備份。


其實,這三種角色,在本質上代表的是三種功能:


  • 提議者代表的是接入和協調功能,收到客戶端請求後,發起二階段提交,進行共識協商;
  • 接受者代表投票協商和存儲數據,對提議的值進行投票,並接受達成共識的值,存儲保存;
  • 學習者代表存儲數據,不參與共識協商,只接受達成共識的值,存儲保存。


因爲一個完整的算法過程是由這三種角色對應的功能組成的,所以理解這三種角色,是你理解 Basic Paxos 如何就提議的值達成共識的基礎。那麼接下來,咱們看看如何使用 Basic Paxos 達成共識,解決開篇提到的那道思考題。


如何達成共識?


想象這樣一個場景,現在疫情這麼嚴重,每個村的路都封得差不多了,就你的村委會不作爲,遲遲沒有什麼防疫的措施。你決定給村委會提交個提案,提一些防疫的建議,除了建議之外,爲了和其他村民的提案做區分,你的提案還得包含一個提案編號,來起到唯一標識的作用。


與你的做法類似,在 Basic Paxos 中,蘭伯特也使用提案代表一個提議。不過在提案中,除了提案編號,還包含了提議值。爲了方便演示,我使用[n, v]表示一個提案,其中 n 爲提案編號,v 爲提議值。


我想強調一下,整個共識協商是分 2 個階段進行的(也就是我在 03 講提到的二階段提交)。那麼具體要如何協商呢?


我們假設客戶端 1 的提案編號爲 1,客戶端 2 的提案編號爲 5,並假設節點 A、B 先收到來自客戶端 1 的準備請求,節點 C 先收到來自客戶端 2 的準備請求。


準備(Prepare)階段



先來看第一個階段,首先客戶端 1、2 作爲提議者,分別向所有接受者發送包含提案編號的準備請求:















你要注意,在準備請求中是不需要指定提議的值的,只需要攜帶提案編號就可以了,這是很多同學容易產生誤解的地方。


接着,當節點 A、B 收到提案編號爲 1 的準備請求,節點 C 收到提案編號爲 5 的準備請求後,將進行這樣的處理:















  • 由於之前沒有通過任何提案,所以節點 A、B 將返回一個 “尚無提案”的響應。也就是說節點 A 和 B 在告訴提議者,我之前沒有通過任何提案呢,並承諾以後不再響應提案編號小於等於 1 的準備請求,不會通過編號小於 1 的提案。


  • 節點 C 也是如此,它將返回一個 “尚無提案”的響應,並承諾以後不再響應提案編號小於等於 5 的準備請求,不會通過編號小於 5 的提案。


另外,當節點 A、B 收到提案編號爲 5 的準備請求,和節點 C 收到提案編號爲 1 的準備請求的時候,將進行這樣的處理過程:














  • 當節點 A、B 收到提案編號爲 5 的準備請求的時候,因爲提案編號 5 大於它們之前響應的準備請求的提案編號 1,而且兩個節點都沒有通過任何提案,所以它將返回一個 “尚無提案”的響應,並承諾以後不再響應提案編號小於等於 5 的準備請求,不會通過編號小於 5 的提案。
  • 當節點 C 收到提案編號爲 1 的準備請求的時候,由於提案編號 1 小於它之前響應的準備請求的提案編號 5,所以丟棄該準備請求,不做響應。


接受(Accept)階段


第二個階段也就是接受階段,首先客戶端 1、2 在收到大多數節點的準備響應之後,會分別發送接受請求:















  • 當客戶端 1 收到大多數的接受者(節點 A、B)的準備響應後,根據響應中提案編號最大的提案的值,設置接受請求中的值。因爲該值在來自節點 A、B 的準備響應中都爲空(也就是圖 5 中的“尚無提案”),所以就把自己的提議值 3 作爲提案的值,發送接受請求[1, 3]。
  • 當客戶端 2 收到大多數的接受者的準備響應後(節點 A、B 和節點 C),根據響應中提案編號最大的提案的值,來設置接受請求中的值。因爲該值在來自節點 A、B、C 的準備響應中都爲空(也就是圖 5 和圖 6 中的“尚無提案”),所以就把自己的提議值 7 作爲提案的值,發送接受請求[5, 7]。


當三個節點收到 2 個客戶端的接受請求時,會進行這樣的處理:














  • 當節點 A、B、C 收到接受請求[1, 3]的時候,由於提案的提案編號 1 小於三個節點承諾能通過的提案的最小提案編號 5,所以提案[1, 3]將被拒絕。
  • 當節點 A、B、C 收到接受請求[5, 7]的時候,由於提案的提案編號 5 不小於三個節點承諾能通過的提案的最小提案編號 5,所以就通過提案[5, 7],也就是接受了值 7,三個節點就 X 值爲 7 達成了共識。


講到這兒我想補充一下,如果集羣中有學習者,當接受者通過了一個提案時,就通知給所有的學習者。當學習者發現大多數的接受者都通過了某個提案,那麼它也通過該提案,接受該提案的值。


通過上面的演示過程,你可以看到,最終各節點就 X 的值達成了共識。那麼在這裏我還想強調一下,Basic Paxos 的容錯能力,源自“大多數”的約定,你可以這麼理解:當少於一半的節點出現故障的時候,共識協商仍然在正常工作。


內容小結

本節課我主要帶你瞭解了 Basic Paxos 的原理和一些特點,我希望你明確這樣幾個重點。


  1. 你可以看到,Basic Paxos 是通過二階段提交的方式來達成共識的。二階段提交是達成共識的常用方式,如果你需要設計新的共識算法的時候,也可以考慮這個方式。


  1. 除了共識,Basic Paxos 還實現了容錯,在少於一半的節點出現故障時,集羣也能工作。它不像分佈式事務算法那樣,必須要所有節點都同意後才提交操作,因爲“所有節點都同意”這個原則,在出現節點故障的時候會導致整個集羣不可用。也就是說,“大多數節點都同意”的原則,賦予了 Basic Paxos 容錯的能力,讓它能夠容忍少於一半的節點的故障。


  1. 本質上而言,提案編號的大小代表着優先級,你可以這麼理解,根據提案編號的大小,接受者保證三個承諾,具體來說:如果準備請求的提案編號,小於等於接受者已經響應的準備請求的提案編號,那麼接受者將承諾不響應這個準備請求;如果接受請求中的提案的提案編號,小於接受者已經響應的準備請求的提案編號,那麼接受者將承諾不通過這個提案;如果接受者之前有通過提案,那麼接受者將承諾,會在準備請求的響應中,包含已經通過的最大編號的提案信息。


參考:

  1. https://time.geekbang.org/column/article/201700
  2. https://zhuanlan.zhihu.com/p/31780743
  3. https://baike.baidu.com/item/Paxos%20%E7%AE%97%E6%B3%95/10688635?fr=aladdin
  4. https://en.wikipedia.org/wiki/Paxos_(computer_science)

附圖

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章