HBase是基于LSM树存储模型的分布式NoSQL
数据库。LSM树对比普遍的B+树来说,能够获得较高随机写性能的同时,也能保持可靠的随机读性能(可参考这里)。在进行读请求的时候,LSM树要把多个子树(类似B+树结构)进行归并查询,对于HBase来说,这些子树就是HFile(还包括内存上的树结构MemStore)。因此归并查询的子树数越少,查询的性能就越高。
Compact的作用 在写请求的这篇文章里,已经介绍过对于每个写请求,都必须写入MemStore以及HLog才算完成事务提交。当MemStore超过阀值的时候,就要flush到HDFS上生成一个HFile。因此随着不断写入,HFile的数量将会越来越多,根据前面所述,HFile数量过多会降低读性能。为了避免对读性能的影响,可以对这些HFile进行compact操作,把多个HFile合并成一个HFile。compact操作需要对HBase的数据进行多次的重新读写,因此这个过程会产生大量的IO。可以看到compact操作的本质就是以IO操作换取后续的读性能的提高。
Compact的流程 HBase的compact是针对HRegion的HStore进行操作的。compact操作分为major和minor两种,major会把HStore所有的HFile都compact为一个HFile,并同时忽略标记为delete的KeyValue(被删除的KeyValue只有在compact过程中才真正被"删除"),可以想象major会产生大量的IO操作,对HBase的读写性能产生影响。minor则只会选择数个HFile文件compact为一个HFile,minor的过程一般较快,而且IO相对较低。在日常任务时间,都会禁止mjaor操作,只在空闲的时段定时执行。
compact入口 可以请求compact的地方有很多,包括在open region、MemStore flush等都会判断是否需要进行compact操作(单个HStore的MemStore flush之后,如果触发compact操作,则会对所属HRegion下的所有HStore分别进行compact)。除此之外,HRegionServer.CompactionChecker负责定期10 * 1000s针对所有HRegion的HStore检测是否需要进行compact操作。
CompactionChecker判断是否需要进行compact操作的条件如下:
1、HStore下还没有进行compact的HFile的总数 >= hbase.hstore.compaction.min(默认为3),则需要进行compact。
2、如果1不成立,则判断是否需要执行major compact。主要是查看一下是否太久没有执行compact操作。具体判断过程:
1)获得compact时间间隔。hbase.hregion.majorcompaction(默认7天)为base基准时间,hbase.hregion.majorcompaction.jitter(默认5.0)为jitter,公式base + jitter - Math.round(2 * jitter * randomNum) 计算出一个会每次自动抖动的数值作为major compact的时间间隔。之所以要一个自动抖动,就是避免在HRegionServer重启的时候大量的major compact出现造成大量的IO。
2)所有HFile最老(时间戳最小)的那个HFile的时间间隔大于这个major compact的时间间隔,则执行major compact。另外如果HRegion只有一个HFile,并且这个HFile的所有KeyValue的时间戳都没有超过TTL,则表示无须进行major compact,会跳过这次major compact。
当1或2成立都会分别对CompactSplitThread发送compact请求,不同的是,1会异步选择需要进行compact的HFile,2则会进行同步选择。
compact请求 CompactSplitThread是HRegionServer内负责专门执行minor compact、major compact、split、merge操作的线程池。其内部对应4个操作有不同的线程池执行对应的请求。把这些耗时较大的操作放到各自的线程池里有助于提高
系统整个吞吐量,同时可以避免某个操作阻塞影响其它操作。
对于每个compact请求,CompactionChecker需要区分出major和minor,然后分配到对应的线程池执行。条件是进行compact的文件总大小 > hbase.regionserver.thread.compaction.throttle(默认2*maxFileCompacts*memstoreFlushSize=2*10*128MB),则为major compact,否则为minor compact。
选择compact的文件操作由对应的HStore进行。CompactionChecker的2会同步选择compact文件,这样就可以马上确定是哪个线程池执行具体的compact操作。但1会异步选择compact进行的HFile时,由于不知道文件总大小,HBase会首先在minor compact的线程池进行compact文件选择操作,选择操作后如果判断为需要进行major compact,则会重新把请求发送到major的线程池进行后续的compact操作。
HStore的compact文件选择 compact文件的选择首先要判断是major还是minor,如果是major,则整个HStore的所有HFile都被选中,否则就选择部分文件进行minor compact。考虑到compact操作都会耗费大量的IO,因此minor compact操作的目标就是以最少的IO代价换取最大的读性能提高。目前在新版本里,HStore的compact文件选择策略能够充分考虑了整体情况去选择最佳的方案。整个过程如下:
删除无效文件。 把超过TTL的HFile选择为compact文件。把这些文件compact记录写入WAL,通知所有执行读请求的scanner更新,更新HStore的总文件大小等。 选择compact文件。 根据选择compact文件更新内部数据。
其中选择compact文件过程是主要步骤,具体如下: 把当前HStore所有的HFile作为候选compact文件进行排除操作。排除候选HFile中比正在compact的最新文件还要老的文件。判断文件新老是比较HFile里保存的最大SequenceId(在HLog replay的过程可以判断哪些记录已经写入HFile)决定。SequenceId是HRegion把插入的KeyValue记录写入HLog时作为key一部分的单调递增ID,因此SequenceId越大,则记录越新,也就是HFile越新。排除候选HFile中超过hbase.hstore.compaction.max.size(默认Long最大值)以及非Reference文件。如果不是forceMajor则跳过这步。Reference文件是split region产生的临时文件,只是简单 |