Paxos 算法詳解(一)

前言

提到分佈式算法,就不得不提 Paxos 算法,在過去幾十年裏,它基本上是分佈式共識的代 名詞,因爲當前最常用的一批共識算法都是基於它改進的。比如,Fast Paxos 算法、 Cheap Paxos 算法、Raft 算法、ZAB 協議等等。

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

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

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

Basic Paxos 是 Multi-Paxos 思想的核心,說白了,Multi-Paxos 就是多執行 幾次 Basic Paxos

思考題。

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

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

三種角色:

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

他們之間的關係如下:
在這裏插入圖片描述

提議者(Proposer):提議一個值,用於投票表決。爲了方便演示,你可以把圖 1 中的 客戶端 1 和 2 看作是提議者。但在絕大多數場景中,集羣中收到客戶端請求的節點,才 是提議者(圖 1 這個架構,是爲了方便演示算法原理)。這樣做的好處是,對業務代碼 沒有入侵性,也就是說,我們不需要在業務代碼中實現算法邏輯,就可以像使用數據庫 一樣訪問後端的數據。
接受者(Acceptor):對每個提議的值進行投票,並存儲接受的值,比如 A、B、C 三 個節點。 一般來說,集羣中的所有節點都在扮演接受者的角色,參與共識協商,並接受 和存儲數據。

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

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

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

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

在 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 的容錯能力,源自“大多數”的約定,你可以這麼理解:當少於一半的節點出現故障的時候,共識協商仍然在正常工作

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