前面的文章介绍了当达到一定的条件后memstore会flush生成hfile,随着hfile文件逐渐增多,查询可能需要更多的IO操作,为了合并这些hfile文件从而衍生出了compaction操作。compact分为两种:Minor Compaction、Major Compaction。
Minor Compaction:选择部分小的、相邻的文件合并为一个大文件。
Major Compation:将store中所有的hfile文件合并成一个大的文件,并且在这个阶段将过期的数据、已删除的数据和超出版本的数据进行物理上的清除。
compaction流程
compact流程分为四个步骤:compaction触发条件、hfile文件选取策略、选取合适的线程池、执行compaction操作。如下图所示:
compact流程图compaction触发条件
compaction的触发条件可以分为如下三种:
1)memstore flush:在进行memstore flush前后都会进行判断是否触发compact,flush之前,先判断该region上是否有store中的hfile文件个数大于hbase.hstore.blockingStoreFiles,有则触发compact操作。flush之后,对当前store中的文件数进行判断,是否满足文件选取策略中规定的触发条件,满足则触发compaction操作。具体的代码查看:MemStoreFlusher.flushRegion()方法。
2)定期检查线程:周期性检查是否需要进行compaction操作,周期为:hbase.server.thread.wakefrequency*hbase.server.compactchecker.interval.interval.multiplier,具体的代码查看:CompactionChecker.chore()方法。
3)手动执行compaction命令:手动触发大多都是major compaction,避开业务高峰期进行major compaction,或修改了表的属性需要立即生效,或需要物理删除已删除的数据和过期的数据。
hfile文件选择策略
hfile文件选择策略是compaction的核心,怎样才能选择尽量多的合并小文件的同时也减少IO,同时合并这些文件之后对读的性能会有显著的提升。下面介绍两种经典的策略。compaction源码入口可以从CompactSplitThread.requestCompactionInternal方法进行查看。
不管选择哪种策略,首先都会对store上的hfile进行逐一排查,排除不满足条件的文件,条件如下:
1)排除正在进行compaction的文件以及比这些文件更新的文件。
2)排除hfile大小大于hbase.hstore.compaction.max.size或在高峰期时大于hbase.hstore.compaction.max.size.offpeak的hfile。
RatioBasedCompactionPolicy
从老到新逐一扫描所有候选的文件,满足下面其中条件之一遍停止扫描:
1)当前文件大小<比当前文件更新的所有文件的大小总和*ratio。
2)当前所剩候选文件数<=hbase.store.compaction.min。
该策略有5个重要的配置如下:
hbase.hstore.compaction.ratio:该参数就是上面条件1)中的ratio。
hbase.hstore.compaction.min:进行compaction最少的hfile文件数。
hbase.hstore.compaction.max:进行compaction最多的hfile文件数。
hbase.hstore.compaction.min.size:进行compaction的最小hfile大小,小于该值的hfile会直接被放入候选文件中。
hbase.hstore.compaction.max.size:进行compaction的最大hfile大小,大于该值的hfile不会被放入候选文件中。
下面通过几个例子来加深对上面几个参数的理解
例一:
hbase.hstore.compaction.ratio = 1.0f
hbase.hstore.compaction.min = 3 (files)
hbase.hstore.compaction.max = 5 (files)
hbase.hstore.compaction.min.size = 10 (bytes)
hbase.hstore.compaction.max.size = 1000 (bytes)
store中hfile大小由老到新为:100, 50, 23, 12,12
100:sum(50, 23, 12, 12) * 1.0 = 97,不满足条件1。
50:sum(23, 12, 12) * 1.0 = 47,不满足条件1。
23 : sum(12, 12) * 1.0 = 24,满足条件。
12:前面23的文件满足条件,而该文件小于hbase.hstore.compaction.max.size,所以满足条件。
12:前面23的文件满足条件,而该文件小于hbase.hstore.compaction.max.size,所以满足条件。
例二:
hbase.hstore.compaction.ratio = 1.0f
hbase.hstore.compaction.min = 3 (files)
hbase.hstore.compaction.max = 5 (files)
hbase.hstore.compaction.min.size = 10 (bytes)
hbase.hstore.compaction.max.size = 1000 (bytes)
store中hfile大小由老到新为:100, 25, 12,12
100:sum(25, 12, 12) * 1.0 = 47,不满足条件1。
25:sum(12, 12) * 1.0 = 24,不满足条件1。
12:满足条件。
12:满足条件。
本例进入候选集的文件为:12、12,由于候选集文件个数小于hbase.hstore.compaction.min,所以不会进行compaction操作。
例三:
hbase.hstore.compaction.ratio = 1.0f
hbase.hstore.compaction.min = 3 (files)
hbase.hstore.compaction.max = 5 (files)
hbase.hstore.compaction.min.size = 10 (bytes)
hbase.hstore.compaction.max.size = 1000 (bytes)
store中hfile大小由老到新为:7, 6, 5, 4, 3, 2, 1
7, 6, 5, 4, 3:都小于hbase.hstore.compaction.min.size值,都放入候选集中。
2, 1:虽然都小于hbase.hstore.compaction.min.size值,但是候选集中的文件已经等于hbase.hstore.compaction.max,所以不能被放入候选集进行compaction。
ExploringCompactionPolicy
该策略的思想和RatioBasedCompactionPolicy的基本一致,不同的在于RatioBasedCompactionPolicy策略找到一个合适的文件集之后就停止扫描了。而ExploringCompactionPolicy策略会记录所有合适的文件,并在合适的文件中选择最优的集合。最优集合:待合并文件数最多或者待合并文件数相同的情况下文件小。
选择合适的线程池
hbase中使用一个专门的类CompactSplitThread用来接收compact请求和split请求,在内部创建compact线程池和split线程池,而compact线程池分为两种longCompactions和shortCompactions。当compaction候选集中的文件大小总和大于hbase.regionserver.thread.compaction.throttle则使用longCompactions线程池进行compaction,否则使用shortCompactions线程池。shortCompactions线程池线程个数由参数hbase.regionserver.thread.compaction.small指定,默认1。longCompactions线程池线程个数由参数hbase.regionserver.thread.compaction.large指定,默认1。
执行compaction操作
执行compaction步骤如下:
1)读取出待合并hfile的keyvalue,进行归并排序,写入./tmp目录下的临时文件。
2)将临时文件移动到对应的store数据目录下。
3)将compaction的输入文件路径和输出文件路径封装为kv写入hlog中,并打上compaction标记,最后强制执行sync。
4)将对应store数据目录下的compaction输入文件删除。
今天的分享就到这,有看不明白的地方一定是我写的不够清楚,所有欢迎提任何问题以及改善方法。
网友评论