Hbase---Compaction

数据写入、读取流程使用了LSM树体系架构,HBase中的用户数据在LSM树体系架构中最终会形成一个一个小的HFile文件。我们知道,HFile小文件如果数量太多会导致读取低效。为了提高读取效率,LSM树体系架构设计了一个非常重要的模块——Compaction。Compaction核心功能是将小文件合并成大文件,提升读取效率。一般基于LSM树体系架构的系统都会设计Compaction,比如LevelDB、RocksDB以及Cassandra等,都可以看到Compaction的身影。

我们首先介绍Compaction的基本工作原理,包括分类、在LSM树架构中的核心作用以及副作用、Compaction的触发时机,让读者能够对Compaction有一个直观的认识。然后基于HBase内核代码深入介绍HBase系统中Compaction实现的基本流程和常用策略。

1 Compaction基本工作原理

Compaction是从一个Region的一个Store中选择部分HFile文件进行合并。合并原理是,先从这些待合并的数据文件中依次读出KeyValue,再由小到大排序后写入一个新的文件。之后,这个新生成的文件就会取代之前已合并的所有文件对外提供服务。

HBase根据合并规模将Compaction分为两类:Minor Compaction和Major Compaction。

·Minor Compaction是指选取部分小的、相邻的HFile,将它们合并成一个更大的HFile。

·Major Compaction是指将一个Store中所有的HFile合并成一个HFile,这个过程还会完全清理三类无意义数据:被删除的数据、TTL过期数据、版本号超过设定版本号的数据。

一般情况下,Major Compaction持续时间会比较长,整个过程会消耗大量系统资源,对上层业务有比较大的影响。因此线上部分数据量较大的业务通常推荐关闭自动触发Major Compaction功能,改为在业务低峰期手动触发(或设置策略自动在低峰期触发)。

在HBase的体系架构下,Compaction有以下核心作用:

·合并小文件,减少文件数,稳定随机读延迟。

·提高数据的本地化率。

·清除无效数据,减少数据存储量。

随着HFile文件数不断增多,查询可能需要越来越多的IO操作,读取延迟必然会越来越大。

Compaction的另一个重要作用是提高数据的本地化率。本地化率越高,在HDFS上访问数据时延迟就越小;相反,本地化率越低,访问数据就可能大概率需要通过网络访问,延迟必然会比较大。

Compaction合并小文件的同时会将落在远程DataNode上的数据读取出来重新写入大文件,合并后的大文件在当前DataNode节点上有一个副本,因此可以提高数据的本地化率。极端情况下,Major Compaction可以将当前Region的本地化率提高到100%。这也是最常用的一种提高数据本地化率的方法。

Compaction在执行过程中有个比较明显的副作用:Compaction操作重写文件会带来很大的带宽压力以及短时间IO压力。这点比较容易理解,要将小文件的数据读出来需要IO,很多小文件数据跨网络传输需要带宽,读出来之后又要写成一个大文件,因为是三副本写入,必然需要网络开销,当然写入IO开销也避免不了。因此可以认为,Compaction就是使用短时间的IO消耗以及带宽消耗换取后续查询的低延迟。虽然数据读取延迟相比图一稳定了一些,但是读取响应时间有了很大的毛刺,这是因为Compaction在执行的时候占用系统资源导致业务读取性能受到一定波及。

总的来说,Compaction操作是所有LSM树结构数据库所特有的一种操作,它的核心操作是批量将大量小文件合并成大文件用以提高读取性能。另外,Compaction是有副作用的,它在一定程度上消耗系统资源,进而影响上层业务的读取响应。因此Compaction通常会设计各种措施,并且针对不同场景设置不同的合并策略来尽量避免对上层业务的影响。

1.1 Compaction基本流程

HBase中Compaction只有在特定的触发条件才会执行,比如部分flush操作完成之后、周期性的Compaction检查操作等。一旦触发,HBase会按照特定流程执行Compaction,如图所示。

HBase会将该Compaction交由一个独立的线程处理,该线程首先会从对应Store中选择合适的HFile文件进行合并,这一步是整个Compaction的核心。选取文件需要遵循很多条件,比如待合并文件数不能太多也不能太少、文件大小不能太大等,最理想的情况是,选取那些IO负载重、文件小的文件集。实际实现中,HBase提供了多种文件选取算法,如RatioBasedCompactionPolicy、ExploringCompactionPolicy和StripeCompactionPolicy等,用户也可以通过特定接口实现自己的Compaction策略。选出待合并的文件后,HBase会根据这些HFile文件总大小挑选对应的线程池处理,最后对这些文件执行具体的合并操作。

为了更加详细地说明Compaction的整个工作流程将Compaction分解成多个子流程分别进行说明,子流程包括:Compaction触发时机,待合并HFile集合选择策略,挑选合适的执行线程池,以及HFile文件合并执行。

1.2 Compaction触发时机

HBase中触发Compaction的时机有很多,最常见的时机有如下三种:MemStore Flush、后台线程周期性检查以及手动触发。

·MemStore Flush:应该说Compaction操作的源头来自flush操作,MemStore Flush会产生HFile文件,文件越来越多就需要compact执行合并。因此在每次执行完flush操作之后,都会对当前Store中的文件数进行判断,一旦Store中总文件数大于hbase.hstore.compactionThreshold,就会触发Compaction。需要说明的是,Compaction都是以Store为单位进行的,而在flush触发条件下,整个Region的所有Store都会执行compact检查,所以一个Region有可能会在短时间内执行多次Compaction。

·后台线程周期性检查:RegionServer会在后台启动一个线程CompactionChecker,定期触发检查对应Store是否需要执行Compaction,检查周期为hbase.server.thread.wakefrequency*hbase.server.compactchecker.interval.multiplier。和flush不同的是,该线程优先检查Store中总文件数是否大于阈值hbase.hstore.compactionThreshold,一旦大于就会触发Compaction;如果不满足,接着检查是否满足Major Compaction条件。简单来说,如果当前Store中HFile的最早更新时间早于某个值mcTime,就会触发Major Compaction。mcTime是一个浮动值,浮动区间默认为[7-7×0.2,7+7×0.2],其中7为hbase.hregion.majorcompaction,0.2为hbase.hregion.majorcompaction.jitter,可见默认在7天左右就会执行一次Major Compaction。用户如果想禁用Major Compaction,需要将参数hbase.hregion.majorcompaction设为0。

·手动触发:一般来讲,手动触发Compaction大多是为了执行Major Compaction。使用手动触发Major Compaction的原因通常有三个——其一,因为很多业务担心自动Major Compaction影响读写性能,因此会选择低峰期手动触发;其二,用户在执行完alter操作之后希望立刻生效,手动触发Major Compaction;其三,HBase管理员发现硬盘容量不够时手动触发Major Compaction,删除大量过期数据。

1.3 待合并HFile集合选择策略

选择合适的文件进行合并是整个Compaction的核心,因为合并文件的大小及其当前承载的IO数直接决定了Compaction的效果以及对整个系统其他业务的影响程度。理想的情况是,选择的待合并HFile文件集合承载了大量IO请求但是文件本身很小,这样Compaction本身不会消耗太多IO,而且合并完成之后对读的性能会有显著提升。然而现实中可能大部分HFile文件都不会这样,在HBase早期的版本中,分别提出了两种选择策略:RatioBasedCompactionPolicy以及ExploringCompactionPolicy,后者在前者的基础上做了进一步修正。但是无论哪种选择策略,都会首先对该Store中所有HFile逐一进行排查,排除不满足条件的部分文件:

·排除当前正在执行Compaction的文件以及比这些文件更新的所有文件。

·排除某些过大的文件,如果文件大于hbase.hstore.compaction.max.size(默认Long.MAX_VALUE),则被排除,否则会产生大量IO消耗。

经过排除后留下来的文件称为候选文件,接下来HBase再判断侯选文件是否满足Major Compaction条件,如果满足,就会选择全部文件进行合并。判断条件如下所列,只要满足其中一条就会执行Major Compaction:

·用户强制执行Major Compaction。

·长时间没有进行Major Compaction(上次执行Major Compaction的时间早于当前时间减去hbase.hregion.majorcompaction)且候选文件数小于hbase.hstore.compaction.max(默认10)。

·Store中含有reference文件,reference文件是region分裂产生的临时文件,一般必须在Compaction过程中清理。

如果满足Major Compaction条件,文件选择这一步就结束了,待合并HFile文件就是Store中所有HFile文件。如果不满足Major Compaction条件,就必然为Minor Compaction,HBase主要有两种Minor Compaction文件选择策略,一种是RatioBasedCompactionPolicy,另一种是ExploringCompactionPolicy。

1.4 HFile文件合并执行

选出待合并的HFile集合,再选出合适的处理线程,接下来执行合并流程。合并流程主要分为如下几步:

1)分别读出待合并HFile文件的KeyValue,进行归并排序处理,之后写到./tmp目录下的临时文件中。

2)将临时文件移动到对应Store的数据目录。

3)将Compaction的输入文件路径和输出文件路径封装为KV写入HLog日志,并打上Compaction标记,最后强制执行sync。

4)将对应Store数据目录下的Compaction输入文件全部删除。

上述4个步骤看起来简单,但实际是很严谨的,具有很强的容错性和幂等性:

·如果RegionServer在步骤2)之前发生异常,本次Compaction会被认定为失败,如果继续进行同样的Compaction,上次异常对接下来的Compaction不会有任何影响,也不会对读写有任何影响。唯一的影响就是多了一份多余的数据。

·如果RegionServer在步骤2)之后、步骤3)之前发生异常,同样,仅仅会多一份冗余数据。

·如果在步骤3)之后、步骤4)之前发生异常,RegionServer在重新打开Region之后首先会从HLog中看到标有Compaction的日志,因为此时输入文件和输出文件已经持久化到HDFS,因此只需要根据HLog移除Compaction输入文件即可。

 

1.5 Compaction相关注意事项

对文件进行Compaction操作可以提升业务读取性能,然而,如果不对Compaction执行阶段的读写吞吐量进行限制,可能会引起短时间大量系统资源消耗,使得用户业务发生读写延迟抖动。HBase社区意识到了这个问题,并提出了一些优化方案,下面分别介绍。

1.Limit Compaction Speed

该优化方案通过感知Compaction的压力情况自动调节系统的Compaction吞吐量,在压力大的时候降低合并吞吐量,压力小的时候增加合并吞吐量。基本原理如下:

·在正常情况下,用户需要设置吞吐量下限参数"hbase.hstore.compaction.throughput.lower.bound"和上限参数"hbase.hstore.compaction.throughput.higher.bound",而HBase实际工作在吞吐量为lower+(higher-lower)*ratio的情况下,其中ratio是一个取值(0,1)的小数,它由当前Store中待参与Compaction的HFile数量决定,数量越多,ratio越小,反之越大。

·如果当前Store中HFile的数量太多,并且超过了参数blockingFileCount,此时所有写请求就会被阻塞以等待Compaction完成,这种场景下上述限制会自动失效。

Compaction除了带来严重的IO放大效应之外,在某些情况下还会因为大量消耗带宽资源而严重影响其他业务。Compaction大量消耗带宽资源主要有以下两个原因:

·正常请求下,Compaction尤其是Major Compaction会将大量数据文件合并为一个大HFile,读出所有数据文件的KV,重新排序之后写入另一个新建的文件。如果待合并文件都在本地,那么读是本地读,不会出现跨网络的情况;如果待合并文件并不都在本地,则需要跨网络进行数据读取。一旦跨网络读取就会有带宽资源消耗。

·数据写入文件默认都是三副本写入,不同副本位于不同节点,因此写的时候会跨网络执行,必然会消耗带宽资源。

跨网络读是可以通过一定优化措施避免的,而跨网络写却是不可能避免的。因此优化Compaction带宽消耗,一方面需要提升本地化率,减少跨网络读;另一方面,虽然跨网络写不可避免,但也可以通过控制手段使得资源消耗控制在一定范围。HBase在这方面参考Facebook引入了Compaction BandWith Limit机制。

2.Compaction BandWidth Limit

与Limit Compaction Speed思路基本一致,Compaction BandWidth Limit方案中主要涉及两个参数:compactBwLimit和numOfFilesDisableCompactLimit,作用分别如下:

·compactBwLimit:一次Compaction的最大带宽使用量,如果Compaction所使用的带宽高于该值,就会强制其sleep一段时间。

·numOfFilesDisableCompactLimit:在写请求非常大的情况下,限制Compaction带宽的使用量必然会导致HFile堆积,进而会影响读请求响应延时。因此该值意义很明显,一旦Store中HFile数量超过该设定值,带宽限制就会失效。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值