zookeeper的一致性協議:Zab

五柳-先生發表於2015-11-25

Zookeeper使用了一種稱為ZabZookeeper Atomic Broadcast的協議作為其一致性複製的核心據其作者說這是一種新發演算法其特點是充分考慮了Yahoo的具體情況高吞吐量、低延遲、健壯、簡單但不過分要求其擴充套件性。下面將展示一些該協議的核心內容

另本文僅討論Zookeeper使用的一致性協議而非討論其原始碼實現

Zookeeper的實現是有Client、Server構成Server端提供了一個一致性複製、儲存服務Client端會提供一些具體的語義比如分散式鎖、選舉演算法、分散式互斥等。從儲存內容來說Server端更多的是儲存一些資料的狀態而非資料內容本身因此Zookeeper可以作為一個小檔案系統使用。資料狀態的儲存量相對不大完全可以全部載入到記憶體中從而極大地消除了通訊延遲。

Server可以Crash後重啟考慮到容錯性Server必須“記住”之前的資料狀態因此資料需要持久化但吞吐量很高時磁碟的IO便成為系統瓶頸其解決辦法是使用快取把隨機寫變為連續寫。

考慮到Zookeeper主要運算元據的狀態為了保證狀態的一致性Zookeeper提出了兩個安全屬性Safety Property


  • 全序Total order如果訊息a在訊息b之前傳送則所有Server應該看到相同的結果

  • 因果順序Causal order如果訊息a在訊息b之前發生a導致了b並被一起傳送則a始終在b之前被執行。

為了保證上述兩個安全屬性Zookeeper使用了TCP協議和Leader。通過使用TCP協議保證了訊息的全序特性先發先到通過Leader解決了因果順序問題先到Leader的先執行。因為有了LeaderZookeeper的架構就變為Master-Slave模式但在該模式中MasterLeader會Crash因此Zookeeper引入了Leader選舉演算法以保證系統的健壯性。歸納起來Zookeeper整個工作分兩個階段

  • Atomic Broadcast

  • Leader選舉

1. Atomic Broadcast

同一時刻存在一個Leader節點其他節點稱為“Follower”如果是更新請求如果客戶端連線到Leader節點則由Leader節點執行其請求如果連線到Follower節點則需轉發請求到Leader節點執行。但對讀請求Client可以直接從Follower上讀取資料如果需要讀到最新資料則需要從Leader節點進行Zookeeper設計的讀寫比例是21。

Leader通過一個簡化版的二段提交模式向其他Follower傳送請求但與二段提交有兩個明顯的不同之處

  • 因為只有一個LeaderLeader提交到Follower的請求一定會被接受沒有其他Leader干擾

  • 不需要所有的Follower都響應成功只要一個多數派即可

通俗地說如果有2f+1個節點允許f個節點失敗。因為任何兩個多數派必有一個交集當Leader切換時通過這些交集節點可以獲得當前系統的最新狀態。如果沒有一個多數派存在存活節點數小於f+1則演算法過程結束。但有一個特例

如果有A、B、C三個節點A是Leader如果B Crash則A、C能正常工作因為A是LeaderA、C還構成多數派如果A Crash則無法繼續工作因為Leader選舉的多數派無法構成。

2. Leader Election

Leader選舉主要是依賴Paxos演算法具體演算法過程請參考其他博文這裡僅考慮Leader選舉帶來的一些問題。Leader選舉遇到的最大問題是”新老互動“的問題新Leader是否要繼續老Leader的狀態。這裡要按老Leader Crash的時機點分幾種情況

  1. 老Leader在COMMIT前Crash已經提交到本地

  2. 老Leader在COMMIT後Crash但有部分Follower接收到了Commit請求

第一種情況這些資料只有老Leader自己知道當老Leader重啟後需要與新Leader同步並把這些資料從本地刪除以維持狀態一致。

第二種情況新Leader應該能通過一個多數派獲得老Leader提交的最新資料

老Leader重啟後可能還會認為自己是Leader可能會繼續傳送未完成的請求從而因為兩個Leader同時存在導致演算法過程失敗解決辦法是把Leader資訊加入每條訊息的id中Zookeeper中稱為zxidzxid為一64位數字高32位為leader資訊又稱為epoch每次leader轉換時遞增低32位為訊息編號Leader轉換時應該從0重新開始編號。通過zxidFollower能很容易發現請求是否來自老Leader從而拒絕老Leader的請求。

因為在老Leader中存在著資料刪除情況1因此Zookeeper的資料儲存要支援補償操作這也就需要像資料庫一樣記錄log。

3. Zab與Paxos

Zab的作者認為Zab與paxos並不相同只所以沒有采用Paxos是因為Paxos保證不了全序順序

Because multiple leaders can
propose a value for a given instance two problems arise.
First, proposals can conflict. Paxos uses ballots to detect and resolve conflicting proposals. 
Second, it is not enough to know that a given instance number has been committed, processes must also be able to figure out which value has been committed.

Paxos演算法的確是不關係請求之間的邏輯順序而只考慮資料之間的全序但很少有人直接使用paxos演算法都會經過一定的簡化、優化。

一般Paxos都會有幾種簡化形式其中之一便是在存在Leader的情況下可以簡化為1個階段Phase2。僅有一個階段的場景需要有一個健壯的Leader因此工作重點就變為Leader選舉在考慮到Learner的過程還需要一個”學習“的階段通過這種方式Paxos可簡化為兩個階段

  • 之前的Phase2

  • Learn

如果再考慮多數派要Learn成功這其實就是Zab協議。Paxos演算法著重是強調了選舉過程的控制對決議學習考慮的不多Zab恰好對此進行了補充。

之前有人說所有分散式演算法都是Paxos的簡化形式雖然很絕對但對很多情況的確如此但不知Zab的作者是否認同這種說法

4.結束

本文只是想從協議、演算法的角度分析Zookeeper而非分析其原始碼實現因為Zookeeper版本的變化文中描述的場景或許已找不到對應的實現。另本文還試圖揭露一個事實Zab就是Paxos的一種簡化形式。

【參考資料】

  • A simple totally ordered broadcast protocol

  • paxos

相關文章