一、ZooKeeper概述
ZooKeeper內部有一個in-memory DB,表示為一個樹形結構。每個樹節點稱為Znode(代碼在DataTree.java和DataNode.java中)。
客戶端可以連接到zookeeper集群中的任意一台。
對於讀請求,直接返回本地znode數據。寫操作則轉換為一個事務,並轉發到集群的Leader處理。Zookeeper提交事務保證寫操作(更新)對於zookeeper集群所有機器都是一致的。
二、Zab協議介紹
Zookeeper使用了一種稱為Zab(Zookeeper Atomic Broadcast)的協議作為其一致性復制的核心,據其作者說這是一種新發算法,其特點是充分考慮了Yahoo的具體情況:高吞吐量、低延遲、健壯、簡單,但不過分要求其擴展性。
(1)Zookeeper的實現是由Client、Server構成
① Server端提供了一個一致性復制、存儲服務;
② Client端會提供一些具體的語義,比如分布式鎖、選舉算法、分布式互斥等;
(2)從存儲內容來說,Server端更多的是存儲一些數據的狀態,而非數據內容本身,因此Zookeeper可以作為一個小文件系統使用。數據狀態的存儲量相對不大,完全可以全部加載到內存中,從而極大地消除了通信延遲。
(3)Server可以Crash后重啟,考慮到容錯性,Server必須“記住”之前的數據狀態,因此數據需要持久化,但吞吐量很高時,磁盤的IO便成為系統瓶頸,其解決辦法是使用緩存,把隨機寫變為連續寫。☆☆☆
(4)安全屬性
考慮到Zookeeper主要操作數據的狀態,為了保證狀態的一致性,Zookeeper提出了兩個安全屬性(Safety Property)
① 全序(Total order):如果消息a在消息b之前發送,則所有Server應該看到相同的結果
② 因果順序(Causal order):如果消息a在消息b之前發生(a導致了b),並被一起發送,則a始終在b之前被執行。☆☆
(5)安全保證
為了保證上述兩個安全屬性,Zookeeper使用了TCP協議和Leader。
① 通過使用TCP協議保證了消息的全序特性(先發先到)
② 通過Leader解決了因果順序問題:先到Leader的先執行。
因為有了Leader,Zookeeper的架構就變為:Master-Slave模式,但在該模式中Master(Leader)會Crash,因此,Zookeeper引入了Leader選舉算法,以保證系統的健壯性。
(6)Zookeeper整個工作分兩個階段:
① Atomic Broadcast
② Leader選舉
(7)Zab特性
ZooKeeper中提交事務的協議並不是Paxos,而是由二階段提交協議改編的ZAB協議。Zab可以滿足以下特性:
①可靠提交 Reliable delivery:如果消息m被一個server遞交了,那么m也將最終被所有server遞交。
②全局有序 Total order:如果server在遞交b之前遞交了a,那么所有遞交了a、b的server也會在遞交b之前遞交a。
③因果有序 Casual order:對於兩個遞交了的消息a、b,如果a因果關系優先於(causally precedes)b,那么a將在b之前遞交。
第三條的因果優先指的是同一個發送者發送的兩個消息a先於b發送,或者上一個leader發送的消息a先於當前leader發送的消息。
2.1 Zab工作模式
Zab協議中Server有兩個模式:broadcast模式、recovery模式
(1)恢復模式
Leader在開始broadcast之前,必須有一個同步更新過的follower的quorum。
Server在Leader服務期間恢復在線時,將進入recovery模式,與Leader進行同步。
(2)廣播模式
Broadcast模式使用二階段提交,但是簡化了協議,不需要abort。follower要么ack,要么拋棄Leader,因為zookeeper保證了每次只有一個Leader。另外也不需要等待所有Server的ACK,只需要一個quorum應答就可以了。
① Follower收到proposal后,寫到磁盤(盡可能批處理),返回ACK。
② Leader收到大多數ACK后,廣播COMMIT消息,自己也deliver該消息。
③ Follower收到COMMIT之后,deliver該消息。
(4)面臨問題
然而,這個簡化的二階段提交不能處理Leader失效的情況,所以增加了recovery模式。切換Leader時,需要解決下面兩個問題。① Never forget delivered messages
Leader在COMMIT投遞到任何一台follower之前宕機,只有它自己commit了。新Leader必須保證這個事務也必須commit。
② Let go of messages that are skipped
Leader產生某個proposal,但是在宕機之前,沒有follower看到這個proposal。該server恢復時,必須丟棄這個proposal。
因為Proposal是保存在follower的事務日志中,並且順序有保證,因此COMMIT的順序也是確定的。解決的第一個問題。
② 上個沒有把proposal發送出去的Leader重啟后,新Leader將告訴它截斷事務日志,一直截斷到follower的epoch對應的最后一個commit位置。
三、 Zab與Paxos
3.1 Paxos瓶頸
ZooKeeper的主要功能是維護一個高可用且一致的數據庫,數據庫內容復制在多個節點上,總共2f+1個節點中只要不超過f個失效,系統就可用。實現這一點的核心是ZAB,一種Atomic Broadcast協議。所謂Atomic Broadcast協議,形象的說就是能夠保證發給各復本的消息順序相同。
由於Paxos的名氣太大,所以我看ZAB的時候首先就想為什么要搞個 ZAB,ZAB相比Paxos有什么優點?這里首要一點是Paxos的一致性不能達到ZooKeeper的要求。舉個例子。
假設一開始Paxos系統中的 leader是P1,他發起了兩個事務<t1, v1>(表示序號為t1的事務要寫的值是v1)和<t2, v2>的過程中掛了。新來個leader是P2,他發起了事務<t1, v1'>。而后又來個新leader是P3,他匯總了一下,得出最終的執行序列<t1, v1'>和<t2, v2>,即P2的t1在前,P1的t2在后。
注意:在這我們可以看出,對於序號為t1的事務,Leader2將Leader1的覆蓋了
這樣的序列為什么不能滿足ZooKeeper的需求呢?ZooKeeper是一個樹形結構,很多操作都要先檢查才能確定能不能執行,比如P1的事務t1可能是創建節點“/a”,t2可能是創建節點“/a/aa”,只有先創建了父節點“/a”,才能創建子節點“/a/aa”。而P2所發起的事務t1可能變成了創建“/b”。這樣P3匯總后的序列是先創建“/b”再創建“/a/aa”,由於“/a”還 沒建,創建“a/aa”就搞不定了。
3.2 解決局方案
為了保證這一點,ZAB要保證同一個leader的發起的事務要按順序被apply,同時還要保證只有先前的leader的所有事務都被apply之后,新選的leader才能在發起事務。
ZAB 的核心思想,形象的說就是保證任意時刻只有一個節點是leader,所有更新事務由leader發起去更新所有復本(稱為follower),更新時用的就是兩階段提交協議,只要多數節點prepare成功,就通知他們commit。各follower要按當初leader讓他們prepare的順序來 apply事務。因為ZAB處理的事務永遠不會回滾,ZAB的2PC做了點優化,多個事務只要通知zxid最大的那個commit,之前的各 follower會統統commit。☆☆☆
如果沒有節點失效,那ZAB上面這樣搞下就完了,麻煩在於leader失效或leader得不到多數節點的支持時怎么處理。這里有幾個關鍵點:
① leader所follower之間通過心跳來檢測異常;
② 檢測到異常之后的節點若試圖成為新的leader,首先要獲得大多數節點的支持,然后從狀態最新的節點同步事務,完成后才可正式成為leader發起事務;
③區分新老leader的關鍵是一個會一直增長的epoch;當然細節很多了,這里就不說了因為我也沒完全搞懂,要了解詳情請看《Zab: High-performance broadcast for primary-backup systems.》這篇論文。
除了能保證順序外,ZAB的性能也能不錯,基於千兆網絡的測試,一般的5節點部署的TPS達到25000左右,而響應時間只有約6ms。
3.3 Zab與Paoxs
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.
- 之前的Phase2
- Learn