1. 內存數據
DataTree
「樹」的數據結構,代表內存中的一份完整數據。(無耦合,非常獨立的組件)
nodes = ConcurrentHashMap<String, DataNode>存放All節點,所有對ZK的操作,就是對這個Map的操作KEY = pathVALUE = DataNodeephemerals = ConcurrentHashMap<Long, HashSet<String>>()臨時節點,單獨存儲DataNode
data[]ACL列表Stat節點狀態parent節點children節點ZKDatabase
zk的內存資料庫
管理zk的sessionDataTree存儲(定時dump快照--->磁碟)事務日誌啟動時,事務日誌+dump快照 = 恢復內存資料庫2. 事務日誌
日誌文件存儲
配置項:
dataDir 默認存儲dataLogDir 單獨存儲路徑帶版本號 ./事務日誌目錄/version-2/...事務日誌文件特點
文件名: log.xxxxxxxxxx文件大小一致:64M文件擴展名非常規律,16進位數字,越修改越大(擴展名其實就:ZXID,且是該日誌文件的第一條事務記錄的ZXID)日誌文件格式
格式化日誌文件工具 org.apache.zookeeper.Server.LogFormatterjava LogFormatter log.xxxxxxx
文件頭,主要是事務日誌產的DBID及版本號Zookeeper Transactional Log File with dbid 0 txnlog format version 2事務日誌..11:07:41 session 0x123124124142 cxid 0x0 zxid 0x1234567890 createSession 30000......(具體可參見:org.apache.zookeeper.Server.LogFormatter源碼)日誌文件寫入
FileTxnLog負責維護事務日誌對外的接口public synchronized boolean append(TxnHeader hdr, Record txn)
寫入的6個步驟:
1. 事務日誌文件,是否可寫FileTxnLog是否與一個可寫的日誌文件「關聯」 ,有就用,沒有則創建 (不關聯的原因:日誌文件滿了,或zk啟動後需要進行第一次的日誌寫入)
2. 日誌文件 「預分配」 及 「擴容」
目的:提高I/O效率,不忙的時候先創建好文件,填充0擴容:小於4KB時,再填充64M的0配置:zookeeper.preAllocSize可設置默認大小64M3. 事務序列化
TxnHeader 事務頭序列化Record 事務體序列化CreateSessionTxnCreateTxnDeleteTxnSetDataTxn4. 生成Checksum通過第3步的序列化二進位結果,計算Checksum,默認使用 Adler32算法
5. 寫入事務日誌FileStream使用BufferedOutputStream,還並沒有真正寫入磁碟
6. 事務日誌刷入磁碟第1步有streamToFlush流,從它取出FileStream,並調用FileChannel.force() 強制刷入磁碟
日誌截斷
peerLastZxid 非Leader上記錄的事務ID任務 peerLastZxid 不能比 Leader上的最後事務ID大TRUNC命令,強制非leader進行日誌截斷3. snapshot數據快照
快照:某一時刻,全量內存數據內容,並將其寫入磁碟文件名:snapshot.xxxxxxxxxx(本次快照的伺服器最新ZXID,很重要,恢復時用) 格式化工具: org.apache.zookeeper.server.SnapshotFormatter使用: java SnapshotFormatter snapshot.xxxxxx
數據快照操作
FileSnap來維護,維護過程如下:
「判斷」 是否需要進行快照每次FileTxnLog後,需判斷是否需要snap,有個snapCount,並採取「過半隨機」策略 logCount > (snapCount / 2 + randRoll)logCount = 已記錄的TxnLog數量 randRoll = 1 ~ snapCount/2 之間的隨機數 意思是:當snapCount設置為10w,那麼zk會在 5w~10w之間,進行一次快照(過半隨機是也)「切換」 事務日誌文件創建快照 「異步線程」「獲取」 全量數據 + Session信息「生成文件名」 (快照數據的文件名)數據 「序列化」4. 初始化
初始化 FileTxnSnapLog初始化 FileTxnLog初始化 FileSnap初始化 ZKDatabase創建 PlayBackListener 監聽器事務應用過程的回調,用於數據的訂正處理快照文件獲取最新100個snap文件解析snap文件逐個解析,從最新的開始,當遇到能用的,就不再解析了獲取最新的ZXID處理事務日誌獲取所有 zxid_for_snap之後提交的事務事務應用獲取最新zxid檢驗epoch5. 數據同步
Leader選舉後,非Leader向 Leader伺服器進行 「註冊」,完成註冊即開始同步操作
獲取Learner狀態Learner發送ACKEPOCH數據包,Leader解析出:currentEpochlastZxid數據同步初始化同步前,先初始化,從ZKDatabase中提取出「事務請求」對應的「提議緩存隊列」,完成三個ZXID值的初始化peerLastZxid:該Learner最後處理的zxidminCommittedLog:Leader「提議緩存隊列」的最小zxidmaxCommittedLog:~最大zxid同步方式DIFF(直接差異化同步)場景:場景:minCommittedLog < peerLastZxid < maxCommittedLog 就是把Leader有的,Learner沒有的,發給Learner(Proposal包 + commit包)TRUNC + DIFF(先回滾,再差異化同步)場景:Leader寫入TxnLog,但沒有發送就掛掉,等恢復後,開始同步時出現 領導說了,以我的為準,把你有的,我沒有的先咔嚓掉,再走DIFF方式處理TRUNC(僅回滾同步)場景:peerLastZxid > maxCommittedLogSNAP(全量同步) 場景1:peerLastZxid < minCommittedLog 場景2:Leader上沒有「提議緩存隊列」,peerLastZxid != lastProcessedZxid(Leader數據恢復後得到的最大zxid)