HBase的compact分析

Posted cynchanpin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HBase的compact分析相关的知识,希望对你有一定的参考价值。

    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产生的暂时文件,仅仅是简单的引用文件。一般必须在compact过程中删除。

  • 推断是否major compact。满足用户指定的force major。或者太长时间没有进行compact(CompactionChecker的推断2)且候选文件数小于hbase.hstore.compaction.max(默认10),或者有Reference文件,满足上面三个条件之中的一个则是major compact。

  • minor compact继续排除操作。 1、排除在metadata里设置不进行minor compact的HFile(bulkLoad的时候设置) 2、applyCompactionPolicy(后面详述) 3、候选文件数小于hbase.hstore.compaction.min(默认3)则排除所有的候选文件
  • 排除候选文件数里超过hbase.hstore.compaction.max(默认10)的部分。假设是major compact则跳过这步。注意从最新的HFile開始进行排除,也就是假设有12个候选文件,则排除掉最后2个最新的HFile。

    compact的选择过程中,主要是推断major和minor,然后在配置的最大最小相关限制下进行选择。整个步骤的重点在applyCompactionPolicy,用户能够实现自己的选择策略。HBase主要有两个策略RatioBasedCompactionPolicy和ExploringCompactionPolicy。我们首先如果一个现象:当写请求许多,导致不断生成HFile。但compact的速度远远跟不上HFile生成的速度,这样就会使HFile的数量会越来越多,导致读性能急剧下降。为了避免这样的情况。在HFile的数量过多的时候会限制写请求的速度:在每次运行MemStore flush的操作前。假设HStore的HFile数超过hbase.hstore.blockingStoreFiles (默认7),则会堵塞flush操作hbase.hstore.blockingWaitTime时间,在这段时间内。假设compact操作使得HStore文件数下降到回这个值,则停止堵塞。

另外堵塞超过时间后,也会恢复运行flush操作。这样做就能够有效地控制大量写请求的速度,但同一时候这也是影响写请求速度的主要原因之中的一个。

两者实现例如以下:

  •     RatioBasedCompactionPolicy。 从最旧文件開始遍历到最新候选文件,找到小于[hbase.hstore.compaction.min.size(默觉得memstore的flush大小,128M)和compact文件总大小*ratio 的最大值]的符合条件文件,假设发现不符合则立即停止搜索。

    ratio是一个可变的比例,能够通过设置高峰期的时间来改变这个比例,在高峰期时ratio为1.2。非高峰期为5,也就是非高峰期同意compact更大的文件(非高峰期能够耗费更大IO)。

     目的是尽可能找到小文件进行minor compact。假设推断这个compact操作后文件数仍然过多会堵塞flush操作。则仅仅是简单选择从最老的文件起,候选文件数减去hbase.hstore.compaction.min(默认3)个文件。

  •     ExploringCompactionPolicy。

     从最旧文件開始遍历全部的候选文件,找出符合[compact文件大小 小于 hbase.hstore.compaction.max.size(默认Long最大值)且全部文件的大小都不会超过其他文件大小*ratio]而且效率最高[compact文件数最多或compact大小最小]。

    ratio是高峰期比例。

    注意,因为存在限制,因此可能候选文件被排除到为0个,这时假设推断这个compact操作后文件数仍然过多会堵塞flush操作,则会选择hbase.hstore.compaction.min(默认3)个文件起。符合最大(Long最大值)最小compact大小(128MB)的总大小最小的一个子集合。

    可见ExploringCompactionPolicy是基于全部候选文件考虑,而RatioBasedCompactionPolicy则是遍历找到就停止。ExploringCompactionPolicy是新版本号的策略,旧版本号的RatioBasedCompactionPolicy当时仅仅考虑到最大的文件往往是最老的,但对于bulk-loaded的文件等某些情况则会破坏这个规则,RatioBasedCompactionPolicy的算法就不是最优的压缩策略。

    完毕compact文件选择后,HStore保存这次compact的结果,并返回给CompactSplitThread。

compact的运行

    CompactSplitThread接下来会要求HRegion进行compact请求,HRegion会添加compact的计数值表明正在运行的compact操作。这样能够防止compact过程中。HRegion被关闭。然后HRegion调用详细HStore的compact方法运行真正的compact操作。

    HStore的compact操作步骤过程,主要就是把这些HFile写成一个HFile。

主要过程为:

  • 对全部文件创建相应的scanner。Reference有特殊的scanner。scanner的层次能够參考之前的读请求,终于得到的是一个StoreScanner对象,另外假设是major compact,则会指定在scanner的时候忽略Delete的KeyValue。
  • 创建一个暂时文件,循环调用scanner的next()方法,把获得的有序的KeyValue写入到暂时文件里。然后把这些KeyValue最大的SequenceId写入metadata里。

  • 把写到暂时文件的compact文件移动到HStore相应的存储文件夹。
  • 把compact的结果写入WAL。RS宕机时就能够根据WAL运行删除旧storeFile
  • 用新的compact文件更新HStore内部的数据
  • 通知运行读请求中的scanners更新读的HFile。删除旧文件(实际上将其归档),又一次计算全部HFile总大小

    能够看到在整个compact操作里,仅仅有最后完毕compact过程才会对读请求有影响。

    完毕了HStore的compact操作后,HRegion就会减去之前compact的计数值。

返回到CompactSplitThread流程,假设hbase.hstore.blockingStoreFiles(默认7)减去当前的HStore的HFile数。

假设<0则表示HRegion将会堵塞兴许的memstore flush操作,处于stuck状态则继续调用requestSystemCompaction。否则运行requestSplit查看是否须要split。

    至此,就完毕了HStore的compact操作。

总结

    HBase的compact操作可以通过牺牲当前的IO来获得后来的读性能提高,为了可以减轻compact对系统带来的影响,每次的compact操作都应尽可能选择IO最少,且能提升读性能最大(文件数最多),另外对于major compact,最好应该在集群空暇时间手动触发。

以上是关于HBase的compact分析的主要内容,如果未能解决你的问题,请参考以下文章

compact处理流程分析

hbase Compaction

HBASE Compaction 简介

HBase MetaStore和Compaction剖析

Hbase合并流程分析

HBase原理 | HBase Compaction介绍与参数调优