Zookeeper數據存儲總結
Zookeeper快照文件和事物操作文件以文件的形式存儲在硬盤上,以快照文件為主,日誌文件為輔。因為當對內存數據進行變更的時候,會保證將事務操作記入log日誌,而snapshot只是內存某一個時刻影像,為了性能takeSnapshot生成snapshot並不是實時的,而是由後臺線程根據一定規則處理的。詳細可參考上一篇文章。
快照文件和事物操作文件在磁盤上如下所示:
-rw-rw-r-- 1 ysl ysl 67108880 10月 23 17:43 log.1 -rw-rw-r-- 1 ysl ysl 67108880 11月 7 16:45 log.9b6 -rw-rw-r-- 1 ysl ysl 67108880 1月 15 17:22 log.c99a -rw-rw-r-- 1 ysl ysl 67108880 1月 16 09:10 log.ca33 -rw-rw-r-- 1 ysl ysl 67108880 1月 17 11:09 log.ca45 -rw-rw-r-- 1 ysl ysl 67108880 1月 18 12:00 log.ca4c -rw-rw-r-- 1 ysl ysl 67108880 1月 24 17:59 log.ca52 -rw-rw-r-- 1 ysl ysl 67108880 11月 8 16:34 log.ca8 -rw-rw-r-- 1 ysl ysl 67108880 11月 9 17:32 log.d16 -rw-rw-r-- 1 ysl ysl 67108880 1月 30 15:44 log.d172 -rw-rw-r-- 1 ysl ysl 67108880 2月 1 11:52 log.d18d -rw-rw-r-- 1 ysl ysl 67108880 2月 2 10:10 log.d1aa -rw-rw-r-- 1 ysl ysl 67108880 11月 10 16:20 log.d88 -rw-rw-r-- 1 ysl ysl 296 10月 23 12:20 snapshot.0 -rw-rw-r-- 1 ysl ysl 6746 11月 13 09:14 snapshot.104d -rw-rw-r-- 1 ysl ysl 6746 11月 14 11:00 snapshot.1461 -rw-rw-r-- 1 ysl ysl 5059 10月 24 12:11 snapshot.14f -rw-rw-r-- 1 ysl ysl 5349 10月 25 10:04 snapshot.20a -rw-rw-r-- 1 ysl ysl 5277 10月 25 10:21 snapshot.210 -rw-rw-r-- 1 ysl ysl 5277 10月 27 14:10 snapshot.21c -rw-rw-r-- 1 ysl ysl 5349 10月 30 09:17 snapshot.30d -rw-rw-r-- 1 ysl ysl 5277 10月 30 11:21 snapshot.313
以上文件名是以log.或者snapshot.加上一串long的16進制數字組成,這個long值就是zxid服務器端事務id。Snapshot文件名生成邏輯在 FileTxnSnapLog.save方法中,如下:
public void save(DataTree dataTree, ConcurrentHashMap<Long, Integer> sessionsWithTimeouts) throws IOException { long lastZxid = dataTree.lastProcessedZxid; File snapshotFile = new File(snapDir, Util.makeSnapshotName(lastZxid)); ........ }
Util.makeSnapshotName用於生成文件名稱
public static String makeSnapshotName(long zxid) {
//返回文件名稱
return "snapshot." + Long.toHexString(zxid);
}
日誌Log文件生成,在FileTxnLog.apend方法中,如果被執行了rollLog方法,那麽文件輸入流會被清空,這裏會創建一個新的文件
if (logStream==null) { if(LOG.isInfoEnabled()){ LOG.info("Creating new log file: log." + Long.toHexString(hdr.getZxid())); } logFileWrite = new File(logDir, ("log." + Long.toHexString(hdr.getZxid()))); fos = new FileOutputStream(logFileWrite); logStream=new BufferedOutputStream(fos); ......... }
當客戶端請求一個事物操作時,leader的PrepRequestProcessor處理器會對請求進行預處理包括生成zxid設置到請求中去,zxid的生成是通過調用ZookeeperServer.getNextZxid生成:
long getNextZxid() {
return hzxid.incrementAndGet();
}
它是hzxid一個自增的long值,有沒有奇怪這個變量取名叫做hzixd多了一個h, h我的理解是high的縮寫代表64位long的高32位。Zxid的分為兩部分高32位用來存儲每次選舉的時代epoch,低32位用來存儲事務請求的自增序列。所謂選舉時代就是一個數值,標記代表一次選舉,跟年份一樣是自增的。每次服務器啟動或者zookeeper異常導致重新選舉都會在原來epoch值加一代表一個新的時代,工具類ZxidUtils用來操作前32或者後32位。比如現在epoch=4代表經歷了4次選舉,如果重新選舉後epoch值為5,通過工具類的zxid=hzxid=ZxidUtils.makeZxid(5,0)= 21474836480,此時低32重新開始值為0, 如果這時來了新的請求值為zxid=21474836481=21474836480+ 1 = ZxidUtils.makeZxid(5, 1)。
public class ZxidUtils {
static public long getEpochFromZxid(long zxid) {
return zxid >> 32L;
}
static public long getCounterFromZxid(long zxid) {
return zxid & 0xffffffffL;
}
static public long makeZxid(long epoch, long counter) {
return (epoch << 32L) | (counter & 0xffffffffL);
}
static public String zxidToString(long zxid) {
return Long.toHexString(zxid);
}
}
Zookeeper數據存儲總結