分佈式一致性與共識算法簡介

在介紹Raft算法以前,請考慮一下若是有機會,你會怎麼設計一個分佈式系統?注意,這裏所說的分佈式系統是幾臺服務器組成的一個對外服務的系統,好比分佈式KV系統、分佈式數據庫系統等。
若是是單機系統,數據通常都在本地,基本不須要與外部通訊,好比單機數據庫系統。但若是有一天你的系統遇到了單機系統難以承受的高請求量,爲了防止系統宕機,也爲了提升系統的可用性,能夠搭建相似master-slave結構的系統,而且容許請求落到slave服務器上。
通過一段時間的設計和編碼,你可能會發現這個系統沒有想象中那麼簡單。首先,相比單機系統,分佈式系統須要和多臺服務器通訊,而通訊有超時的可能,此時發送方沒法肯定通訊是成功仍是失敗。其次,一份數據被放到了多臺服務器,數據更新有延遲。最後,一旦master服務器宕機,沒有一個自動的機制能夠立馬提高slave服務器爲master服務器。
這時你可能會想,是否有方法能夠解決上述問題?或者說是否有框架能夠解決分佈式系統所面臨的問題?答案是沒有,依據是接下來要講到的分佈式系統領域的CAP定理。


分佈式一致性算法開發實戰git

09e2398e634e50df5ccec2876a954c1e.png


CAP定理
CAP分別是以下3個單詞的首字母縮寫。
  • Consistency:一致性github

  • Availability:可用性算法

  • Partition-tolerance:分區容錯性數據庫


CAP定理指出,在異步網絡模型中,不存在一個系統能夠同時知足上述3個屬性。換句話說,分佈式系統必須捨棄其中的一個屬性。
下圖展現了分佈式系統中不存在同時覆蓋3個屬性的區域,可是能夠找到同時覆蓋兩個屬性的區域。

ecbf32f42ad36762005c19154ec791e4.jpeg


在文章開頭提到的系統實現問題中,通訊超時和更新延遲都屬於一致性問題,出現這個問題的緣由是存在多臺服務器,而每臺服務器都有本身的數據。數據冗餘雖然能夠提升系統的可用性和分區容錯性,可是相應地難以知足強一致性。若是想要解決一致性問題,也就是達到強一致性,好比把全部請求所有經過單臺服務器處理,那麼就很難達到高可用性。
CAP定理對於分佈式系統的設計是一個很重要的參考。對於須要在分佈式條件下運行的系統來講,如何在一致性、可用性和分區容錯性中取捨,或者說要弱化哪個屬性,是首先須要考慮的問題。從經驗上來講,可用性或一致性每每是被弱化的對象。
對於要求高可用性的系統來講,每每會保留強一致性。典型的例子就是延遲處理,利用Message Queue之類的中間件,在後臺逐個處理隊列中的請求,當處理完畢時,系統達到強一致性狀態。可是要求強一致性的系統,好比元數據系統、分佈式數據庫系統,它們的可用性每每是有上限的。
從實現效果上來講,不少人或多或少都瞭解或者設計過具備強一致性的系統。可是,大部分人並不瞭解強一致性的系統是如何運做的,也不知道該怎麼設計。老實說這確實很難,以致於計算機科學界有一類專門解決這種問題的算法 —— 共識算法。


共識算法緩存

09e2398e634e50df5ccec2876a954c1e.png


「共識」的意思是保證全部的參與者都有相同的認知(能夠理解爲強一致性)。共識算法自己能夠依據是否有惡意節點分爲兩類,大部分時候共識算法指的都是沒有惡意節點的那一類,即系統中的節點不會向其餘節點發送惡意請求,好比欺騙請求。共識算法中最有名的應該是Paxos算法。
Paxos算法
Paxos算法是Leslie Lamport在1998年發表的The Part-Time Parliament中提出的一種共識算法。
Paxos算法除了難懂以外,還難以實現。儘管如此,如下服務仍是在生產環境中使用了Paxos算法和Paxos算法的修改版。
  • Google Chubby:分佈式鎖服務服務器

  • Google Spanner:NewSQL網絡

  • Ceph併發

  • Neo4japp

  • Amazon Elastic Container Service框架


Paxos算法中的節點有如下3種可能的角色:
  • Proposer:提出提案,並向Acceptor發送提案。

  • Acceptor:參與決策,迴應提案。若是提案得到多數(過半)Acceptor接受,則認爲提案被批准。

  • Learner:不參與決策,只學習最新達成一致的提案。


Paxos算法中的決議過程分兩種,一種是對單個value(值)的決議過程,也就是對單個值達成一致;另外一種是針對連續多個value的決議過程。前者稱爲Basic Paxos,後者稱爲Multi Paxos。
Basic Paxos的過程以下:
  1. Proposer生成全局惟一且遞增的proposal id,並向全部Acceptor發送prepare請求。

  2. Acceptor收到請求後,若是proposal id正常,則回覆已接受的proposal id中最大的決議id和value。

  3. Proposer接收到多數Acceptor的響應後,從應答中選擇proposal id最大的value,併發送給全部Acceptor。

  4. Acceptor收到proposal以後,接收並持久化當前proposal id和value。

  5. Proposer收到多數Acceptor的響應以後造成決議,Proposer發送決議給全部Learner。


能夠看到,對於多個value,Basic Paxos兩次來回的決議使其性能不是很理想,因此有針對連續多個value決議的Multi Paxos。
Multi Paxos的基本思想是先使用Basic Paxos決議出Leader,再由Leader推動決議。Multi Paxos和Basic Paxos具體有如下不一樣。
每一個Proposer使用惟一的id標識。
使用Basic Paxos在全部Proposer中選出一個Leader,由Leader提交proposal給Acceptor表決,這樣Basic Paxos中的1~3步能夠跳過,提升效率。
以上只是對Paxos算法最基本的理解,但在實際實現中還有不少具體問題須要解決。由於部分問題難以解決或者沒有能夠參考的解決方案,致使如下Paxos的變種算法的出現(按出現的時間前後排列)。
  • Disk Paxos,2002年

  • Cheap Paxos,2003年

  • Fast Paxos,2004年

  • Generalized Paxos,2005年

  • Stoppable Paxos,2008年

  • Vertical Paxos,2009年


從系統實現的角度來講,從變種算法中選擇一個並實現多是比較好的選擇,可是沒法避免地要對Paxos有必定了解才能開始編碼。到了2014年,出現了一種更容易理解的共識算法 —— Raft算法。
Raft算法
Raft算法由斯坦福大學的Diego Ongaro和John Ousterhout在2014年提出。在保證和Paxos算法同樣的正確性的前提下,具體分析了選舉及日誌複製的實現,甚至還提供了參考代碼。
關於Raft算法的論文中給出了計算機科學系學生對於Raft算法的可理解性的調查結果。結論是40多人中的大部分人認爲Raft算法更容易理解,相對的只有個位數的人認爲Paxos算法更容易理解。論文做者之一的Diego Ongaro以後在他的博士論文中進一步給出了Raft算法的詳細分析,包括優化後的集羣成員變動算法。
Raft算法的官方網站raft.github.io中給出了可視化的Raft算法中Leader的選舉過程(另外一個網站thesecretlivesofdata.com給出了更詳細的Leader選舉和日誌複製的可視化過程),同時給出了一些Raft算法相關的論文、演講和教程連接。對於想要快速瞭解Raft算法的人來講,這是一個很好的入口。
官方網站主頁上還給出了一些Raft算法實現的列表,其中不少是試驗性的實現。這些實現的源代碼大部分能夠在GitHub上找到。若是想要特定語言的實現版本,能夠嘗試在上面查找。官方網站主頁上Raft算法實現的列表能夠經過GitHub Pages的Pull Request通知管理員修改。若是對本身的算法實現有信心,能夠克隆主頁的GitHub庫,而後添加本身的算法實現後提交Pull Request。
生產環境級別的Raft算法實現比較有名的是基於Go語言的etcd,基於etcd設計出來的上層軟件能夠用於生產環境。
下面大體介紹一下Raft算法。
Raft算法是單Leader、多Follower模型,能夠理解爲主從模型。Raft算法中有如下3種角色:
  • Leader:集羣的Leader

  • Candidate:想要成爲Leader的候選者

  • Follower:Leader的跟隨者。


系統在啓動後會立刻選舉出Leader,以後的請求所有經過Leader處理。Leader在處理請求時,會先加一條日誌,把日誌同步給Follower。當寫入成功的節點過半以後持久化日誌,通知Follower也持久化,最後將結果回覆給客戶端。
ZAB算法
現存的而且能夠做爲集羣一部分的分佈式同步軟件中,Apache ZooKeeper(簡稱ZooKeeper)多是最有名的一個。ZooKeeper本來是Apache Hadoop的一部分,如今是頂級Apache Project中的一個。ZooKeeper被不少大公司使用,是一個通過生產環境考驗的中間件。
從功能上來講,ZooKeeper是一個分佈式等級型KV服務(Hierarchical Key-Value Store)。和通常用於緩存的KV服務不一樣,客戶端能夠監聽某個節點下的Key的變動,所以ZooKeeper常常被用於分佈式配置服務。
ZooKeeper的核心是一個名叫ZAB的算法,這是Paxos算法的一個變種。ZAB算法的詳細內容這裏不作展開,一方面ZAB算法和Paxos算法有相同的地方,另外一方面ZooKeeper在面向客戶端方面所作的設計可能比ZAB算法更加複雜,所以就算理解了ZAB算法也不必定能徹底理解ZooKeeper的設計。
如何選擇
整體來講,若是是第一次接觸分佈式一致性算法,那麼Raft算法是一個很好的入門算法。但若是想深刻研究,Paxos算法仍舊是沒法迴避的。其實算法自己並沒有優劣之分,理解其背後的思想纔是最重要的。
若是想使用現成的分佈式一致性中間件,那麼Apache ZooKeeper多是一個不錯的選擇。若是想進一步瞭解分佈式一致性的算法細節實現,那麼ZooKeeper的源代碼很值得閱讀和參考。


總結

09e2398e634e50df5ccec2876a954c1e.png

本文主要介紹了在實現分佈式系統時可能碰到的CAP問題,也就是一致性、可用性和分區容錯性不能同時知足的限制,以及在實現強一致性系統時所用到的共識算法。
相關文章
相關標籤/搜索