1. 程式人生 > >區塊鏈系列----分散式一致性演算法---Paxos 和 Raft

區塊鏈系列----分散式一致性演算法---Paxos 和 Raft

背景

在一個分散式系統中,如何保證叢集中所有節點中的資料完全相同並且能夠對某個提案(Proposal)達成一致是分散式系統正常工作的核心問題,而共識演算法就是用來保證分散式系統一致性的方法。
提案1
然而分散式系統由於引入了多個節點,所以系統中會出現各種非常複雜的情況;隨著節點數量的增加,節點失效、故障或者宕機就變成了一件非常常見的事情,解決分散式系統中的各種邊界條件和意外情況也增加了解決分散式一致性問題的難度。
提案2
在一個分散式系統中,除了節點的失效是會導致一致性不容易達成的主要原因之外,節點之間的網路通訊收到干擾甚至阻斷以及分散式系統的執行速度的差異都是解決分散式系統一致性所面臨的難題。

CAP

CAP即:保證一致性(Consistency)、可用性(Availability)和分割槽容錯性(Partition-tolerance) 。在非同步的網路模型中,所有的節點由於沒有時鐘僅僅能根據接收到的訊息作出判斷,每一個系統只能在這三種特性中選擇兩種。

不過這裡討論的一致性其實都是強一致性,也就是所有節點接收到同樣的操作時會按照完全相同的順序執行,被一個節點提交的更新操作會立刻反映在其他通過非同步或部分同步網路連線的節點上,如果想要同時滿足一致性和分割槽容錯性,在非同步的網路中,我們只能中心化儲存所有資料,通過其他節點將請求路由給中心節點達到這兩個目的。

但是在現實世界中其實並不存在絕對非同步的網路環境,如果我們允許每一個節點擁有自己的時鐘,這些時鐘雖然有著完全不同的時間,但是它們的更新頻率是完全相同的,所以我們可以通過時鐘得知接收訊息的間隔時間,在這種更寬鬆的前提下,我們能夠得到更強大的服務。

然而在部分同步的網路環境中,我們仍然沒有辦法同時保證一致性、可用性和分割槽容錯性,證明的過程其實非常簡單,可以直接閱讀 論文 的 4.2 節,然而時鐘的出現能夠讓我們知道當前訊息有多久沒有得到迴應,通過超時時間就能在一定程度上解決資訊丟失的問題。

由於網路一定會存在延時,所以沒有辦法在分散式系統中做到強一致性的同時保證可用性,不過我們可以通過降低對一致性的要求,在一致性和可用性之間做出權衡,而這其實也是設計分散式系統首先需要考慮的問題,由於強一致性的系統會導致系統的可用性降低,僅僅將接受請求的工作交給其他節點對於高併發的服務並不能解決問題,所以在目前主流的分散式系統中都選擇最終一致性
最終一致性


最終一致性允許多個節點的狀態出現衝突,但是所有能夠溝通的節點都能夠在有限的時間內解決衝突,從不一致的狀態恢復到一致,這裡列出的兩個條件比較重要,一是節點直接可以正常通訊,二是衝突需要在有限的時間內解決,只有在這兩個條件成立時才能達到最終一致性。

共識演算法

在分散式系統中,我們會遇到節點故障宕機或者不響應等情況,這便有了Paxos 和 Raft。

Paxos 和 Raft

Paxos 和 Raft 是目前分散式系統領域中兩種非常著名的解決一致性問題的共識演算法,兩者都能解決分散式系統中的一致性問題,但是前者的實現與證明非常難以理解,後者的實現比較簡潔並且遵循人的直覺,它的出現就是為了解決 Paxos 難以理解並和難以實現的問題。
我們先來簡單介紹一下 Paxos 究竟是什麼,Paxos 其實是一類能夠解決分散式一致性問題的協議,它能夠讓分散式網路中的節點在出現錯誤時仍然保持一致;Leslie Lamport 提出的 Paxos 可以在沒有惡意節點的前提下保證系統中節點的一致性,也是第一個被證明完備的共識演算法,目前的完備的共識演算法包括 Raft 本質上都是 Paxos 的變種

作為一類協議,Paxos 中包含 Basic Paxos、Multi-Paxos、Cheap Paxos 和其他的變種,在這一小節我們會簡單介紹 Basic Paxos 和 Multi-Paxos 這兩種協議。

Basic Paxos

Basic Paxos 是 Paxos 中最為基礎的協議,每一個 Basic Paxos 的協議例項最終都會選擇唯一一個結果;使用 Paxos 作為共識演算法的分散式系統中,節點都會有三種身份,分別是 Proposer(提案)、Acceptor 和 Learner。
我們在這裡會忽略最後一種身份 Learner 簡化協議的執行過程,便於讀者理解;Paxos 的執行過程分為兩個階段,分別是準備階段(Prepare)和接受階段(Accept),當 Proposer 接收到來自客戶端的請求時,就會進入如下流程:
Basic Paxos

在整個共識演算法執行的過程中,Proposer 負責提出提案並向 Acceptor 分別發出兩次 RPC 請求,Prepare 和 Accept;Acceptor 會根據其持有的資訊 minProposalacceptedProposalacceptedValue 選擇接受或者拒絕當前的提案,當某一個提案被過半數的 Acceptor 接受之後,我們就認為當前提案被整個叢集接受了
Basic Paxos
我們簡單舉一個例子介紹 Paxos 是如何在多個提案下保證最終能夠達到一致性的,上述圖片中 S1 和 S5 分別收到了來自客戶端的請求 X 和 Y,S1 首先向 S2 和 S3 發出 Prepare RPC 和 Accept RPC,三個伺服器都接受了 S1 的提案 X;在這之後,S5 向 S3 和 S4 伺服器發出 Prepare(2.5) 的請求,S3 由於已經接受了 X,所以它會返回接受的提案和值 (1.1, X),這時伺服器使用接收到的提案代替自己的提案 Y,重新向其他伺服器傳送 Accept(2.5, X) 的 RPC,最終所有的伺服器會達成一致並選擇相同的值。

Multi-Paxos

由於大多數的分散式叢集都需要接受一系列的值,如果使用 Basic Paxos 來處理資料流,那麼就會導致非常明顯的效能損失,而 Multi-Paxos 是前者的加強版,如果叢集中的 Leader 是非常穩定的,那麼我們往往不需要準備階段的工作,這樣就能夠將 RPC 的數量減少一半。
Multi-Paxos
上述圖片中描述的就是穩定階段 Multi-Paxos 的處理過程,S1 是整個叢集的 Leader,當其他的伺服器接收到來自客戶端的請求時,都會將請求轉發給 Leader 進行處理。

當然,Leader 角色的出現自然會帶來另一個問題,也就是 Leader 究竟應該如何選舉,在 Paxos Made Simple 一文中並沒有給出 Multi-Paxos 的具體實現方法和細節,所以不同 Multi-Paxos 的實現上總有各種各樣細微的差別。

Raft

Raft 其實就是 Multi-Paxos 的一個變種,Raft 通過簡化 Multi-Paxos 的模型,實現了一種更容易讓人理解的共識演算法,它們兩者都能夠對一系列連續的問題達成一致。

Raft 在 Multi-Paxos 的基礎之上做了兩個限制,首先是 Raft 中追加日誌的操作必須是連續的,而 Multi-Paxos 中追加日誌的操作是併發的,但是對於節點內部的狀態機來說兩者都是有序的,第二就是 Raft 對 Leader 選舉的條件做了限制,只有擁有最新、最全日誌的節點才能夠當選 Leader,但是 Multi-Paxos 由於任意節點都可以寫日誌,所以在選擇 Leader 上也沒有什麼限制,只是在選擇 Leader 之後需要將 Leader 中的日誌補全。
Paxos and Raft
在 Raft 中,所有 Follower 的日誌都是 Leader 的子集,而 Multi-Paxos 中的日誌並不會做這個保證,由於 Raft 對日誌追加的方式和選舉過程進行了限制,所以在實現上會更加容易和簡單。

從理論上來講,支援併發日誌追加的 Paxos 會比 Raft 有更優秀的效能,不過其理解和實現上還是比較複雜的,很多人都會說 Paxos 是科學,而 Raft 是工程,當作者需要去實現一個共識演算法,會選擇使用 Raft 和更簡潔的實現,避免因為一些邊界條件而帶來的複雜問題。

總結

在這篇文章中,我們首先介紹了分散式系統中面對的最重要問題,分散式一致性。這裡介紹瞭解決非拜占庭問題下一致性演算法 Paxos 和 Raft。現在工程上常用的一致性工程是ETCD。

參考

Paxos

Raft

Etcd