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);
	}
}
posted @ 2018-02-05 11:14  木易森林  阅读(3527)  评论(0编辑  收藏  举报