每一个Regionserver中都有一个HLog,一般情况下除非设置了SETWAL(false)否则对hbase的写操作在put到内存之前会append到log中以防止机器down 掉造成的数据丢失。
首先来看RS启动后对HLog的处理
private HLog setupWALAndReplication() throws IOException { final Path oldLogDir = new Path(rootDir, HConstants.HREGION_OLDLOGDIR_NAME); Path logdir = new Path(rootDir, HLog.getHLogDirectoryName(this.serverInfo)); if (this.fs.exists(logdir)) {// 若hbase/.logs存在则表明在该台机器上已经有regionserver启动了 throw new RegionServerRunningException("Region server already " + "running at " + this.serverInfo.getServerName() + " because logdir " + logdir.toString() + " exists"); } ..... return instantiateHLog(logdir, oldLogDir); }
看看instantiateHLog(logdir, oldLogDir);省略一些不重要的步骤,主要的步骤如下
if (!fs.mkdirs(dir)) { //创建.log目录下该regionserver目录,即为/hbase/.log/regionserver throw new IOException("Unable to mkdir " + dir); } this.oldLogDir = oldLogDir; if (!fs.exists(oldLogDir)) {//若/habse/.oldlogs目录不存在则创建之 if (!fs.mkdirs(this.oldLogDir)) { throw new IOException("Unable to mkdir " + this.oldLogDir); } }
rollWriter();
rollWriter()函数主要完成以下功能:
1.新建一个 /regionserver目录下的文件
2.把旧文件移到/.oldlogs目录下
3.返回需要进行flush的regions
那么什么是旧文件呢?
hbase认为,如果一个文件的所有数据都已经刷到磁盘上了,该文件认为是旧文件,可以将其移除。如何判断一个文件的所有数据刷到磁盘了,用到的就是一个AtomicLong变量 seqnum来判定,这是一个递增的Long并且线程安全。一个文件的seqnum就是该文件最后写入entry的seqnum。
hbase默认一个小时会新建一个文件,并且会清除原先的旧文件。
a) Hlog维护一个ConcurrentSkipListMap<byte [], Long> lastSeqWritten的map其中byte是region的ecodedname,Long是每个entry写入Hlog的seqNum,当有新的entry写入Hlog 是putifabsent,而当memstore中的数据flush结束以后该map就会清除该region。因此该map维护的是最旧写入Hlog的entry的regionname和seqnum映射;
b) Hlog同时维护一个SortedMap<Long, Path> outputfiles,其中Path表示的是regionserver下的path,而Long指的是该Path的seqnum及path要关闭时此时的seqNum。
rollWriter()函数首先会创建一个新的文件,然后把原先的文件加入到outputfiles Map中,如果此时lastSeqWritten为空则说明当前所有的region都已经flush结束了,故可以把所有的outputfiles删除掉
否则需要选取合适的outputfiles进行删除,选取的是outputfiles中最老的且比lastSeqWritten中所有的value都小的path进行rename到oldlogs中。在整个过程中对该Hlog上锁防止在cleanoldlog是进行cacheflush
举个例子说明一下:
比如现在该rs下有4个文件,储存的是4个region的信息:
file1: RegionA,4表示的是文件名是file1,其中存储了一个entry其regionname是reginA,其seqNum是4,seqnum是递增的且新建的文件的seq比原先的大
file1: RegionA,4 RegionB,5 RegionC,6 RegionD,7
file2: RegionA,8 RegionB,9 RegionC,10
file3: RegionA,11 RegionB,12 RegionD,13
file4: RegionB,14
如上面所示,假设上面4个文件都存在,且假设此时lastSeqWritten 中存储的是RegionB,14
因为lastSeqWritten储存的是最新加入的entries且并没有被flush,这说明Region A,C,D都已经flush结束的,而file1,2,3,由于其seqnum都小于14,认为这些file都已经flush成功了,可以移除了,因为A,C,D都已经flush成功,而如果1,2,3中的Region B未flush那么此时lastSeqWritten存储的肯定不是14了而是小于十四的未flush的seq