首先说一下Compact的作用:
在Memstore超过一定的阈值的时候,就要新开一个进程将Memstore flush到storefile中,新的Memstore继续写入接受到的数据,当storefile越来越多时,就会降低读的性能,为了提高读的性能,可以对这些storefile进行compact操作,形多个storefile合并成一个大的storefile,那么compact就需要对HBase的数据进行多次的重新读写,这将产生大量的IO操作,所以Compact操作就是以IO操作来换取后面读的性能。
Compact的两种方式及区别:
<1>、Minor:
Minor操作只用来做部分文件的合并操作以及包括minVersion=0并且设置ttl的过期版本清理,不做任何删除数据、多版本数据的清理工作。
<2>、Major:
Major操作是对Region下的Store下的所有StoreFile执行合并操作,最终的结果是整理合并出一个文件。
一般情况下都是做Minor合并,Major不少集群都是禁止,然后在集群负载较小时,进行手动Major合并,在企业中,一般也是配置了一个datacube.hregion.majorcompaction0,这是配置major的合并周期(默认为7天),很多集群配置成一天,如果配置成0即关闭Major合并。本文重点讨论的是minor合并,因此就不在此多说Major合并,只需要注意,既然Major合并是把所有HFile都合并成一个文件,可想对集群负载不可小觑。
Minor则只会选择数个HFile文件compact为一个HFile,minor的过程一般较快,而且IO相对较低。在日常任务时间,都会禁止major操作,只在空闲的时段定时执行(因为太占用集群资源)。
Compact的触发条件:
在源码中有这么一段函数:
public boolean needs Compaction(final Collection storeFiles, final List filesCompacting) {
//store下的storefile的数量减去正在compact的storefile数量
int numCandidates = storeFiles.size() - filesCompacting.size();
//如果相减的值大于等于3时就表示需要做compact
return numCandidates >= comConf.getMinFilesToCompact();//hbase.hstore.compaction.min=3
表示至少需要3个满足条件的store file时,minor compaction才会启动
}
如果返回true,就需要做minor,如果返回false,就进一步判断是否需要做major,主要是查看一下是否太久没有做compact。
具体的判断过程:
<1>、获得compact时间间隔。hbase.hregion.majorcompaction(默认7天)为base基准时间,hbase.hregion.majorcompaction.jitter(默认5.0)为jitter,公式base +jitter - Math.round(2 * jitter * randomNum) 计算出一个会每次自动抖动的数值作为majorcompact的时间间隔。之所以要一个自动抖动,就是避免在HRegionServer重启的时候大量的major compact出现造成大量的IO。
<2>、所有storefile最老(时间戳最小)的那个storefile的时间间隔大于这个majorcompact的时间间隔,则执行major compact。另外如果HRegion只有一个storefile,并且这个storefile的所有KeyValue的时间戳都没有超过TTL,则表示无须进行major compact,会跳过这次major compact。
第一个条件是异步的操作compact,第二个条件是同步操作compact
MinorCompact的过程:
当确定为需要MinorCompact时,便开始storefile的合并过程。
1、选出待执行Compact的storefiles。由于在Store中的文件可能已经在进行Compacting,因此,这里取出未执行Compacting的文件,将其加入到Candidates(候选)中。
2、执行compactSelection算法,在Candidates中选出需要进行compact的文件,并封装成CompactSelection对象当中(这里面还是很复杂的)。
这一步主要是过滤掉过期的storefiles。过滤minVersion=0,并且storefile.maxTimeStamp+ store.ttl < now_timestamp。这意味着整个文件最大的时间戳的kv,都已经过期了,从而证明整个storefile都已经过期了。CompactSelection如果发现这样的storefile,会优先选择出来,作为Min然后提交给Store进行处理。
3、判断fileToCompact队列中的文件是否超过了maxCompactSize,如果超过,则过滤掉该文件,避免对于大文件进行compaction。(这一步可以忽略,因为默认maxCompactSize为Long.MaxValue,很少有文件大于这个值。
4、如果确定Minor Compaction方式执行,会检查经过过滤过的fileToCompact的大小是否满足minFilesToCompact最低标准(默认为3,即超过3个storefile文件则启动合并),如果不满足,忽略本次操作。确定执行的Minor Compaction的操作时,会使用一个smart算法,从filesToCompact当中选出匹配的storefiles。
hbase会将队列中的storefile 按照文件年龄排序(older to younger),minor compaction总是从older store file开始选择,
(1)如果该文件小于hbase.hstore.compaction.min.size(为memestoreFlushSize)则一定会被添加到合并队列中。
(2)如果该文件大于hbase.hstore.compaction.max.size(Long.MAX_VALUE)则一定会被排除,这个值很大,一般不会有。
(3)如果该文件的size 小于它后面hbase.hstore.compaction.max(默认为10) 个store file size 之和乘以一个ratio(配置项是hbase.hstore.compaction.ratio,默认为1.2),则该storefile 也将加入到minor compaction 中。当然,如果他后面不足10个文件,那么也就是取他后面几个文件总和*ratio了。
如此,最终选择下来的文件就将进入Minor合并。