【分佈式】Zookeeper數據與存儲

1、前言java

  前面分析了Zookeeper對請求的處理,本篇博文接着分析Zookeeper中如何對底層數據進行存儲,數據存儲被分爲內存數據存儲於磁盤數據存儲。數據庫

2、數據與存儲apache

  2.1 內存數據api

  Zookeeper的數據模型是樹結構,在內存數據庫中,存儲了整棵樹的內容,包括全部的節點路徑、節點數據、ACL信息,Zookeeper會定時將這個數據存儲到磁盤上。緩存

  1. DataTree服務器

  DataTree是內存數據存儲的核心,是一個樹結構,表明了內存中一份完整的數據。DataTree不包含任何與網絡、客戶端鏈接及請求處理相關的業務邏輯,是一個獨立的組件。網絡

  2. DataNodesession

  DataNode是數據存儲的最小單元,其內部除了保存告終點的數據內容、ACL列表、節點狀態以外,還記錄了父節點的引用和子節點列表兩個屬性,其也提供了對子節點列表進行操做的接口。異步

  3. ZKDatabase源碼分析

  Zookeeper的內存數據庫,管理Zookeeper的全部會話、DataTree存儲和事務日誌。ZKDatabase會定時向磁盤dump快照數據,同時在Zookeeper啓動時,會經過磁盤的事務日誌和快照文件恢復成一個完整的內存數據庫。

  2.2 事務日誌

  1. 文件存儲

  在配置Zookeeper集羣時須要配置dataDir目錄,其用來存儲事務日誌文件。也能夠爲事務日誌單獨分配一個文件存儲目錄:dataLogDir。若配置dataLogDir爲/home/admin/zkData/zk_log,那麼Zookeeper在運行過程當中會在該目錄下創建一個名字爲version-2的子目錄,該目錄肯定了當前Zookeeper使用的事務日誌格式版本號,當下次某個Zookeeper版本對事務日誌格式進行變動時,此目錄也會變動,即在version-2子目錄下會生成一系列文件大小一致(64MB)的文件。

  2. 日誌格式

  在配置好日誌文件目錄,啓動Zookeeper後,完成以下操做

  (1) 建立/test_log節點,初始值爲v1。

  (2) 更新/test_log節點的數據爲v2。

  (3) 建立/test_log/c節點,初始值爲v1。

  (4) 刪除/test_log/c節點。

  通過四步操做後,會在/log/version-2/目錄下生成一個日誌文件,筆者下是log.cec。

  將Zookeeper下的zookeeper-3.4.6.jar和slf4j-api-1.6.1.jar複製到/log/version-2目錄下,使用以下命令打開log.cec文件。

  java -classpath ./zookeeper-3.4.6.jar:./slf4j-api-1.6.1.jar org.apache.zookeeper.server.LogFormatter log.cec

  

  ZooKeeper Transactional Log File with dbid 0 txnlog format version 2 。是文件頭信息,主要是事務日誌的DBID和日誌格式版本號。  

  ...session 0x159...0xcec createSession 30000。表示客戶端會話建立操做。

  ...session 0x159...0xced create '/test_log,... 。表示建立/test_log節點,數據內容爲#7631(v1)。

  ...session 0x159...0xcee setData ‘/test_log,...。表示設置了/test_log節點數據,內容爲#7632(v2)。

  ...session 0x159...0xcef create ’/test_log/c,...。表示建立節點/test_log/c。

  ...session 0x159...0xcf0 delete '/test_log/c。表示刪除節點/test_log/c。

  3. 日誌寫入

  FileTxnLog負責維護事務日誌對外的接口,包括事務日誌的寫入和讀取等。Zookeeper的事務日誌寫入過程大致能夠分爲以下6個步驟。

  (1) 肯定是否有事務日誌可寫。當Zookeeper服務器啓動完成須要進行第一次事務日誌的寫入,或是上一次事務日誌寫滿時,都會處於與事務日誌文件斷開的狀態,即Zookeeper服務器沒有和任意一個日誌文件相關聯。所以在進行事務日誌寫入前,Zookeeper首先會判斷FileTxnLog組件是否已經關聯上一個可寫的事務日誌文件。若沒有,則會使用該事務操做關聯的ZXID做爲後綴建立一個事務日誌文件,同時構建事務日誌的文件頭信息,並當即寫入這個事務日誌文件中去,同時將該文件的文件流放入streamToFlush集合,該集合用來記錄當前須要強制進行數據落盤的文件流。

  (2) 肯定事務日誌文件是否須要擴容(預分配)。Zookeeper會採用磁盤空間預分配策略。當檢測到當前事務日誌文件剩餘空間不足4096字節時,就會開始進行文件空間擴容,即在現有文件大小上,將文件增長65536KB(64MB),而後使用"0"填充被擴容的文件空間。

  (3) 事務序列化。對事務頭和事務體的序列化,其中事務體又可分爲會話建立事務、節點建立事務、節點刪除事務、節點數據更新事務等。

  (4) 生成Checksum。爲保證日誌文件的完整性和數據的準確性,Zookeeper在將事務日誌寫入文件前,會計算生成Checksum。

  (5) 寫入事務日誌文件流。將序列化後的事務頭、事務體和Checksum寫入文件流中,此時併爲寫入到磁盤上。

  (6) 事務日誌刷入磁盤。因爲步驟5中的緩存緣由,沒法實時地寫入磁盤文件中,所以須要將緩存數據強制刷入磁盤。

  4. 日誌截斷

  在Zookeeper運行過程當中,可能出現非Leader記錄的事務ID比Leader上大,這是非法運行狀態。此時,須要保證全部機器必須與該Leader的數據保持同步,即Leader會發送TRUNC命令給該機器,要求進行日誌截斷,Learner收到該命令後,就會刪除全部包含或大於該事務ID的事務日誌文件。

  2.3 snapshot-數據快照

  數據快照是Zookeeper數據存儲中很是核心的運行機制,數據快照用來記錄Zookeeper服務器上某一時刻的全量內存數據內容,並將其寫入指定的磁盤文件中。

  1. 文件存儲

  與事務文件相似,Zookeeper快照文件也能夠指定特定磁盤目錄,經過dataDir屬性來配置。若指定dataDir爲/home/admin/zkData/zk_data,則在運行過程當中會在該目錄下建立version-2的目錄,該目錄肯定了當前Zookeeper使用的快照數據格式版本號。在Zookeeper運行時,會生成一系列文件。

  2. 數據快照

  FileSnap負責維護快照數據對外的接口,包括快照數據的寫入和讀取等,將內存數據庫寫入快照數據文件實際上是一個序列化過程。針對客戶端的每一次事務操做,Zookeeper都會將他們記錄到事務日誌中,同時也會將數據變動應用到內存數據庫中,Zookeeper在進行若干次事務日誌記錄後,將內存數據庫的全量數據Dump到本地文件中,這就是數據快照。其步驟以下

  (1) 肯定是否須要進行數據快照。每進行一次事務日誌記錄以後,Zookeeper都會檢測當前是否須要進行數據快照,考慮到數據快照對於Zookeeper機器的影響,須要儘可能避免Zookeeper集羣中的全部機器在同一時刻進行數據快照。採用過半隨機策略進行數據快照操做。

  (2) 切換事務日誌文件。表示當前的事務日誌已經寫滿,須要從新建立一個新的事務日誌。

  (3) 建立數據快照異步線程。建立單獨的異步線程來進行數據快照以免影響Zookeeper主流程。

  (4) 獲取全量數據和會話信息。從ZKDatabase中獲取到DataTree和會話信息。

  (5) 生成快照數據文件名。Zookeeper根據當前已經提交的最大ZXID來生成數據快照文件名。

  (6) 數據序列化。首先序列化文件頭信息,而後再對會話信息和DataTree分別進行序列化,同時生成一個Checksum,一併寫入快照數據文件中去。

  2.4 初始化

  在Zookeeper服務器啓動期間,首先會進行數據初始化工做,用於將存儲在磁盤上的數據文件加載到Zookeeper服務器內存中。

  1. 初始化流程

  Zookeeper的初始化過程以下圖所示

  數據的初始化工做是從磁盤上加載數據的過程,主要包括了從快照文件中加載快照數據和根據實物日誌進行數據修正兩個過程。

  (1) 初始化FileTxnSnapLog。FileTxnSnapLog是Zookeeper事務日誌和快照數據訪問層,用於銜接上層業務和底層數據存儲,底層數據包含了事務日誌和快照數據兩部分。FileTxnSnapLog中對應FileTxnLog和FileSnap。

  (2) 初始化ZKDatabase。首先構建DataTree,同時將FileTxnSnapLog交付ZKDatabase,以便內存數據庫可以對事務日誌和快照數據進行訪問。在ZKDatabase初始化時,DataTree也會進行相應的初始化工做,如建立一些默認結點,如/、/zookeeper、/zookeeper/quota三個節點。

  (3) 建立PlayBackListener。其主要用來接收事務應用過程當中的回調,在Zookeeper數據恢復後期,會有事務修正過程,此過程會回調PlayBackListener來進行對應的數據修正。

  (4) 處理快照文件。此時能夠從磁盤中恢復數據了,首先從快照文件開始加載。

  (5) 獲取最新的100個快照文件。更新時間最晚的快照文件包含了最新的全量數據。

  (6) 解析快照文件。逐個解析快照文件,此時須要進行反序列化,生成DataTree和sessionsWithTimeouts,同時還會校驗Checksum及快照文件的正確性。對於100個快找文件,若是正確性校驗經過時,一般只會解析最新的那個快照文件。只有最新快照文件不可用時,纔會逐個進行解析,直至100個快照文件所有解析完。若將100個快照文件解析完後仍是沒法成功恢復一個完整的DataTree和sessionWithTimeouts,此時服務器啓動失敗。

  (7) 獲取最新的ZXID。此時根據快照文件的文件名便可解析出最新的ZXID:zxid_for_snap。該ZXID表明了Zookeeper開始進行數據快照的時刻。

  (8) 處理事務日誌。此時服務器內存中已經有了一份近似全量的數據,如今開始經過事務日誌來更新增量數據。

  (9) 獲取全部zxid_for_snap以後提交的事務。此時,已經能夠獲取快照數據的最新ZXID。只須要從事務日誌中獲取全部ZXID比步驟7獲得的ZXID大的事務操做。

  (10) 事務應用。獲取大於zxid_for_snap的事務後,將其逐個應用到以前基於快照數據文件恢復出來的DataTree和sessionsWithTimeouts。每當有一個事務被應用到內存數據庫中後,Zookeeper同時會回調PlayBackListener,將這事務操做記錄轉換成Proposal,並保存到ZKDatabase的committedLog中,以便Follower進行快速同步。

  (11) 獲取最新的ZXID。待全部的事務都被完整地應用到內存數據庫中後,也就基本上完成了數據的初始化過程,此時再次獲取ZXID,用來標識上次服務器正常運行時提交的最大事務ID。

  (12) 校驗epoch。epoch標識了當前Leader週期,集羣機器相互通訊時,會帶上這個epoch以確保彼此在同一個Leader週期中。完成數據加載後,Zookeeper會從步驟11中肯定ZXID中解析出事務處理的Leader週期:epochOfZxid。同時也會從磁盤的currentEpoch和acceptedEpoch文件中讀取上次記錄的最新的epoch值,進行校驗。

  2.5 數據同步

  整個集羣完成Leader選舉後,Learner會向Leader進行註冊,當Learner向Leader完成註冊後,就進入數據同步環節,同步過程就是Leader將那些沒有在Learner服務器上提交過的事務請求同步給Learner服務器,大致過程以下

  (1) 獲取Learner狀態。在註冊Learner的最後階段,Learner服務器會發送給Leader服務器一個ACKEPOCH數據包,Leader會從這個數據包中解析出該Learner的currentEpoch和lastZxid。

  (2) 數據同步初始化。首先從Zookeeper內存數據庫中提取出事務請求對應的提議緩存隊列proposals,同時完成peerLastZxid(該Learner最後處理的ZXID)、minCommittedLog(Leader提議緩存隊列commitedLog中最小的ZXID)、maxCommittedLog(Leader提議緩存隊列commitedLog中的最大ZXID)三個ZXID值的初始化。

  對於集羣數據同步而言,一般分爲四類,直接差別化同步(DIFF同步)、先回滾再差別化同步(TRUNC+DIFF同步)、僅回滾同步(TRUNC同步)、全量同步(SNAP同步),在初始化階段,Leader會優先以全量同步方式來同步數據。同時,會根據Leader和Learner之間的數據差別狀況來決定最終的數據同步方式。

  · 直接差別化同步(DIFF同步,peerLastZxid介於minCommittedLog和maxCommittedLog之間)。Leader首先向這個Learner發送一個DIFF指令,用於通知Learner進入差別化數據同步階段,Leader即將把一些Proposal同步給本身,針對每一個Proposal,Leader都會經過發送PROPOSAL內容數據包和COMMIT指令數據包來完成,

  · 先回滾再差別化同步(TRUNC+DIFF同步,Leader已經將事務記錄到本地事務日誌中,可是沒有成功發起Proposal流程)。當Leader發現某個Learner包含了一條本身沒有的事務記錄,那麼就須要該Learner進行事務回滾,回滾到Leader服務器上存在的,同時也是最接近於peerLastZxid的ZXID。

  · 僅回滾同步(TRUNC同步,peerLastZxid大於maxCommittedLog)。Leader要求Learner回滾到ZXID值爲maxCommittedLog對應的事務操做。

  · 全量同步(SNAP同步,peerLastZxid小於minCommittedLog或peerLastZxid不等於lastProcessedZxid)。Leader沒法直接使用提議緩存隊列和Learner進行同步,所以只能進行全量同步。Leader將本機的全量內存數據同步給Learner。Leader首先向Learner發送一個SNAP指令,通知Learner即將進行全量同步,隨後,Leader會從內存數據庫中獲取到全量的數據節點和會話超時時間記錄器,將他們序列化後傳輸給Learner。Learner接收到該全量數據後,會對其反序列化後載入到內存數據庫中。

3、總結

  本篇博文主要講解了Zookeeper的數據與存儲,包括內存數據,快照數據,以及如何進行數據的同步等細節,至此,Zookeeper的理論學習部分已經所有完成,以後會進行源碼分析,也謝謝各位園友的觀看~

相關文章
相關標籤/搜索