1.compact
我们首先假设一个现象:当写请求非常多,导致不断生成HFile,但compact的速度远远跟不上HFile生成的速度,这样就会使HFile的数量会越来越多,导致读性能急剧下降。为了避免这种情况,在HFile的数量过多的时候会限制写请求的速度:在每次执行MemStore flush的操作前,如果HStore的HFile数超过hbase.hstore.blockingStoreFiles (默认7),则会阻塞flush操作hbase.hstore.blockingWaitTime时间,在这段时间内,如果compact操作使得HStore文件数下降到回这个值,则停止阻塞。另外阻塞超过时间后,也会恢复执行flush操作。这样做就可以有效地控制大量写请求的速度,但同时这也是影响写请求速度的主要原因之一。
- minor compact
HStore的storeFile数量 >=hbase.hstore.compactionThreshold配置的值,则可能会进行compact,默认值为3,可以调大,比如设置为6,在定期的major compact中进行剩下文件的合并。 - major compact
hbase.hregion.majorcompaction:配置major合并的间隔时间,默认为1天,可设置为0,禁止自动的major合并,可手动或者通过脚本定期进行major合并 -
选择合适的HFile合并
选取候选文件(排除不满足条件的部分文件)
1.排除当前正在执行compact的文件及其比这些文件更新的所有文件(sequenceld更大)
2.排除某些过大的单个文件,如果文件大小大于hbase.hstore.compaction.max.size(默认Long最大值),则被排除,否则会产生大量IO消耗-
判断是否满足major compaction条件
1.用户强制执行major compaction
2.长时间没有进行(compactCompactionChecker的判断条件2)且候选文件数小于hbase.hstore.compaction.max(默认10)- Store中含有Reference文件,Reference文件是split region产生的临时文件,只是简单的引用文件,一般必须在compact过程中删除
如果不满足major compaction条件,就必然为minor compaction,HBase主要有两种minor策略:RatioBasedCompactionPolicy和ExploringCompactionPolicy,下面分别进行介绍:
RatioBasedCompactionPolicy
从老到新逐一扫描所有候选文件,满足其中条件之一便停止扫描:
(1)当前文件大小 < 比它更新的所有文件大小总和 * ratio,其中ratio是一个可变的比例,在高峰期时ratio为1.2,非高峰期为5,也就是非高峰期允许compact更大的文件。那什么时候是高峰期,什么时候是非高峰期呢?用户可以配置参数hbase.offpeak.start.hour和hbase.offpeak.end.hour来设置高峰期
(2)当前所剩候选文件数 <= hbase.store.compaction.min(默认为3)
停止扫描后,待合并文件就选择出来了,即为当前扫描文件+比它更新的所有文件
ExploringCompactionPolicy
该策略思路基本和RatioBasedCompactionPolicy相同,不同的是,Ratio策略在找到一个合适的文件集合之后就停止扫描了,而Exploring策略会记录下所有合适的文件集合,并在这些文件集合中寻找最优解。最优解可以理解为:待合并文件数最多或者待合并文件数相同的情况下文件大小较小,这样有利于减少compaction带来的IO消耗。
2.hbase.hregion.max.filesize
- 默认值:256M
- 说明:在当前RegionServer上单个Region的大小,单个Region超过这个阀值时,这个Region会被自动split成更小的region
- 调优:当hbase.hregion.max.filesize比较小时,触发split的几率更大,而split的时候会将region offline,因为在split结束的时间前,访问该region的请求将被block(阻塞)住,客户端block的时间默认为1s。当大量的region同时发生split时,系统的整体访问服务将大受影响。因此容易出现吞吐量及响应时间的不稳定现象。
当hbase.hregion.max.filesize比较大时,单个region中触发split的几率较小,大量region同时触发split的几率也较小,因此吞吐量较之设置比较小的额时候更加稳定些。但是由于长期得不到split,因此同一个region内发生多次compaction的机会增加了,由于region比较大,所以做一次compact和split会产生较长时间的停顿,对应用的读写性能冲击非常大,另外大的region意味着大的storefile,compaction时对内存也是一个挑战。
compaction的原理是将原有数据读一遍并重写一遍到hdfs上,然后再删除原有数据。这种行为无疑会降低以IO为瓶颈的系统的速度,因此平均吞吐量会受到一些影响而下降
所以,我们应该自己管理split,将阈值设置高一些,然后每天在访问量小的时候定时进行split。 - 调整:自己管理split,首先禁用split,讲阈值设置成100G(一般单个region是不可能达到100G的,看自身业务情况来定),然后使用regionSplitter工具每天在访问量小的时候(凌晨)定时进行split。
3.zookeeper.session.timeout
- 默认值:3分钟
- 说明:RegionServer与Zookeeper间的连接超时时间。当超过时间后,RegionServer会被Zookeeper从集群清单中移除,HMaser收到移除通知后,会对这台server负责的regions重新balance,让其他存活的RegionServer接管
- 调优:这个timeout决定了RegionServer是否能够及时的failover。设置成一分钟或者更低,可以减少因等待超时而被延长的failover时间。
不过需要注意的是,对于一些online应用,RegionServer的宕机到恢复时间本身就很短的(网络闪退,crash(死机)等故障,运维可快速介入),如果这种情况下调低timeout时间,就会得不偿失。因为当RegionServer被正式从集群中移除时,HMaser就开始做balance,当故障的RegionServer快速恢复后,这个balance动作是毫无意义的,反而会使负载不均匀,给集群带来更多负担。
4.hbase.regionserver.handler.count
- 默认值:10
- 说明:RegionServer端开启的RPC监听器实例个数,也即RegionServer能够处理的IO请求线程数
- 调优:这个参数的调优与内存息息相关。较少的IO线程,适用于处理单词请求内存消耗较高的big put场景(大容量单词put或设置了较大cache的scan,均属于big put)或RegionServer的内存比较紧张的场景。
较多的IO线程,适用于单词请求内存消耗低,TPS要求非常高的场景。
这里需要注意的是如果server的region数量较少,大量的请求都会落到一个region上,因快速充满memstore触发flush导致的读写锁会影响全局TPS,不是IO线程数越高越好。 - 调整: 50
5.hbase.regionserver.global.memstore.upperLimit/lowerLimit
- 默认值:0.4/0.35
- upperlimit说明:hbase.hregion.memstore.flush.size这个参数的作用是当单个memstore达到指定值时,flush该memstore。但是,一台RegionServer可能有成百上千个memstore,每个memstore也许未达到flush.size,但是jvm的heap就不够用。该参数就是为了限制memstore占用的总内存。
- lowerlimit说明:同upperlimit,只不过当全局memstore的内存达到35%时,它不会flush所有的memstore,它会找一些内存占用较大的memstore,个别flush,当然更新时还是会被block。lowerlimit算是一个在全局flush前的补救措施。可以想象一下,如果memstore需要在一段时间内全部flush,且这段时间内无法接受写请求,对hbase集群的性能影响是很大的。
- 调优:这个一个Heap内存保护参数,默认值已经能适用于大多数场景。它的调整一般是为了配合某些专属优化,比如读密集型应用,将读缓存开大,降低该值,腾出更多内存给其他模块使用。
这个参数会给使用者带来什么影响?
比如,10G内存,100个region,每个memstore 64M,假设每个region只有一个memstore,那么当100个memstore平均占用到50%左右,就会达到lowerlimit的限制。假设此时,其他memstore同样有很多的写请求进来,在哪些大的region未flush完,就可能又超过upperlimit,则所有region都会被block,开始触发全局flush
6.hfile.block.cache.size
- 默认值:0.2
- 说明:storefile的读缓存占用heap的大小百分比,0.2表示20%,该值直接影响数据读的性能
- 调优:当然是越大越好,如果读比写多,可以设置到0.4到0.5。如果读写均衡,可以设置0.3左右,如果写比读多,保持默认。设置这个值的时候,同时要参考hbase.regionserver.global.memstore.upperlimit,该值是memstore占heap的最大百分比,两个参数一个影响读,一个影响写。如果两值加起来超过80%~90%,会有OOM的风险,谨慎设置。
7.hbase.hstore.blockingStoreFiles
- 默认值:7
- 说明:在compaction时,如果一个store(column family)内有超过7个storefile需要合并,则block(阻塞)所有的写请求进行flush操作前进行split或者compact,除非超过hbase.hstore.blockingWaitTime配置的时间,限制storefile数量增长过快。
compaction执行结束之后会生成临时文件,临时文件的意义在于,在compaction执行期间,对原数据的访问没有影响。compaction执行合并操作生成的文件生效过程,需要对store的写操作加锁,阻塞store内的更新操作,直到更新store的storefile完成为止 - 调优:block请求会影响大年region的读写性能,最好可以将值设为单个region可以支撑的最大storefile数量。最大storefile数量可通过region size/memstore size来计算。如果你将region size 设为无限大,那么你需要预估一个region可能产生的最大storefile数。
8.hbase.hregion.memstore.block.multiplier
- 默认值:2
- 说明:当一个region里的memstore超过单个memstore.size两倍的大小时,block该region的所有请求,进行flush,释放内存。虽然我们设置了memstore的总大小,比如64M,但是如果在即将达到64M的时候,我们put了一个100M的数据或写请求量暴增,此时memstore的大小会瞬间暴涨到超过预期的memstore.size。这个参数的作用是当memstore的大小增至超过memstore.size时,block所有请求,遏制风险进一步扩大。
- 调优:这个参数的默认值是比较靠谱的。如果你预估你正常场景下不会出现突发写或写的量可控,那么保持默认值即可。如果正常情况下,你的写量就会经常暴增,那么你应该调大这个倍数并调整其他参数值,比如hfile.block.cache.size和hbase.regionserver.global.memstore.upperlimit/lowerlimit,以预留更多内存,防止hbase server OOM