Zookeeper詳解(八):Zookeeper數據存儲

zookeeper日誌有三類:快照(雖然不是日誌可是它是數據)、事務日誌(記錄每次操做)、zookeeper本身系統日誌。第三個不屬於數據類因此這裏不作說明。
數據庫


快照數據數組

Zookeeper在運行時會在內存中維護一個完整的數據,就像內存數據庫同樣。ZKDatabase就是Zookeeper的內存數據庫,負載管理Zookeeper的會話、存儲和事務日誌。它會按期dump一份數據快照到硬盤上,在Zookeeper啓動時根據這個快照數據和事務日誌來加載一份完整的數據到內存。這一點跟Redis很像,其實不少時候思路都是同樣。服務器

經過在配置文件中設置dataDir來指定快照保存位置。將內存數據庫寫入快照文件實際上是一個序列化的過程。快照文件保存只是每一個節點的元數據而非數據自己數據結構

那麼每次快照間隔多久呢?異步

其實能夠經過snapCount來進行配置,這個值得含義是每次快照之間的事務數量。也就是說執行多少次事務操做後進行一次快照。ide


  1. 每完成一次事務操做Zookeeper都會檢查是否達到snapCount設置也就是來判斷是否須要進行快照操做,由於快照自己對機器性能有影響,要避免集羣中全部節點都進行快照性能

  2. 若是要進行快照操做,首先就須要對事務日誌進行截斷而後切換,因此事務日誌寫滿不是以64M爲標準而是以事務條數爲標準的。spa

  3. 建立異步線程來執行快照操做(這一點又和Redis的bgsave同樣)操作系統

  4. 從ZKDatabase中獲取全量數據和會話,由於要保存內存全部數據節點信息和會話信息線程

  5. 生成快照名稱,會根據已提交的最大ZXID來生成快照名稱

  6. 數據序列化,首先會序列化文件頭信息(魔數、版本、dbid信息),而後對會話信息和DataTree(Zookeeper內存數據的核心一個樹形數據結構,表明內存完整數據)分別序列化,同時生成一個校驗和,而後一塊兒寫入數據文件中。


事務日誌

在配置文件中經過dataLogDir來配置事務日誌路徑,若是不配置默認保存在dataDir指定的路徑下面。

Snip20180630_34.png

這些文件都是65M,實際上是64M。並且都是以log.開頭後面是一個十六進制數字做爲後綴。這個後綴是一個ZXID它是寫入該日誌的第一個事務的事務ID,這樣能夠達到快速定位某一事務的效果。


日誌寫入過程:

  1. 當須要寫入事務日誌的時候Zookeeper會判斷它是否和一個可寫入的事務日誌相關聯,若是關聯就寫入,若是沒有則用該事務的事務ID來建立一個事務日誌,同時將這個事務日誌對應的文件流放入到一個集合中(streamsToFlush),這個集合中記錄的是當前須要強刷數據到磁盤的文件流(由於操做系統一般有延遲寫入機制,對於Linux系統強刷等於調用fsync)。

  2. 事務日誌文件採用預先分配空間策略,這樣爲了保證單一事務日誌文件所佔用的磁盤塊是連續的,這也是爲了提升性能。當Zookeeper發現當前正在寫入的事務日誌文件空間不足4KB時,就會啓動預先分配空間策略進行擴容。第一次使用事務日誌或者事務日誌達到切割條數(snapCount參數觸發快照)會啓動預先分配策略;其餘時候只要發現當前使用的事務日誌空餘不足4KB就進行擴容,擴容時使用0進行填充。

  3. 確保日誌文件空間夠以後就須要對進行事務序列化操做,最終產生一個字節數組。主要對事務頭(TxnHeader)和事務體(Record)進行序列化。

  4. 根據序列化後的字節數據計算一個校驗和

  5. 將字節數組和校驗寫入到文件流中。

  6. 因爲該事務日誌的文件流在集合中,這時候就會從集合裏面取出文件流強刷落盤。

初始化

Zookeeper啓動期間要完成數據初始化也就是將完整的數據加載到內存。它會根據快照和事務日誌來完成加載過程。快照保存的是數據的元數據(不包括節點數據)而事務日誌記錄的操做和數據,那麼使用快照+事務日誌的方式能夠還原完整的數據。它會先解析快照文件用於恢復出一個DataTree,此時就能夠解析出個最新的ZXID,而後再根據ZXID來經過事務日誌進行數據填充。另外還須要獲取大於ZXID以後的事務日誌進行應用。當完成數據恢復之後就能夠得到一個最新的ZXID,這ZXID就是上次最後一個提交的事務ID。


完成初始化以後而且若是在集羣環境中,Leader和其餘服務器還會有一個數據同步過程。

Leader服務器會提取三個值:peerLastZxid(其餘角色服務器最後處理的ZXID)、minCommittedLog(Leader服務器當前最小的ZXID)、maxCommittedLog(Leader服務器當前最大的ZXID)。

  • 差別同步:若是其餘角色服務器的peerLastZxid介於minCommittedLog、maxCommittedLog那麼就使用差別化同步,這時候Leader就知道他和對端服務器差多少,而後把這個差別進行發送再提交就是Zookeeper的兩階段提交。

  • 先回滾在差別同步:另一種狀況是在上一種狀況下發生了意外原來的Leader要發送可是此時該Leader掛了,那麼其他的會進行Leader選舉,新的Leader產生後原來的Leader恢復了,那麼顯然他倆的ZXID有可能不一樣,新的Leader的ZXID小,可是要保證Leader的權威因此其餘ZXID比它大的都要回滾到和現有Leader同樣的狀態或者小於Leader的ZXID的狀態,而後在進行差別同步。

  • 僅回滾:也就是Leader讓其餘服務器都回滾到和本身同樣的最大ZXID上

  • 全量同步:peerLastZxid小於Leader上的minCommittedLog,其實就是Leader將所有內存數據給Learner。這種狀況一般用於在現有集羣中又增長了一臺Zookeeper服務器。

相關文章
相關標籤/搜索