优化的目的
我们线上hbase集群使用了group分组功能,但没有针对不同业务分组的特点做特殊优化,hbase服务能力没有彻底激发出来。
本文记录了对某个业务分组参数优化的探索,借此机会深入了解不同配置对regionserver监控指标和机器负载的影响。 优化后,单台regionserver查询延迟更低,磁盘IO降低,系统更稳定。从而提高吞吐能力,进而减少机器,提升资源利用率的能力,节约成本。

要解决那些问题
目前该业务分组发现的问题主要在写入。虽然资源利用率不高,但时不时会rpcp99变慢。从监控可以看到,队列等待时间会间隔性变长。看regionserver日志,hlogs上限触发同时刷太多region,flush和compact压力集中。刷了好多小文件,写放大效果明显。

问题分析
分组有17台regionserver,单台有大概520个region,白天请求量较为平稳。分析一台的使用情况。

物理资源使用情况
机器类型
2E5-2650v4(212核),832G,2150G(120G) SATA SSD+12*800G(960G) SATA SSD
机器负载
cpu和load并不高。

内存看,由于应用内存分配较为保守,regionserver堆内堆外共150g,datanode 4g,很大一块给了操作系统读cache。但regionserver本身也配置了100g的读cache,操作系统读cache没什么用。

磁盘读流量较高,写流量50MB/s。网络In 20MB,Out 40到80MB波动。

磁盘读流量主要是本地regionserver 业务读和compact读,写流量主要是本地regionserver写wal,flush,compact,以及其他datanode的写副本。
网络in主要是业务写入请求和其他datanode写副本请求,网络out主要是业务查询响应和regionserver 写wal,flush,compact写其他datanode副本的请求。
由于业务查询主要走内存缓存(95%),hifile又有很高的压缩比(1:5),如果不考虑服务端数据过滤的情况,业务读引起的磁盘IO应该只是网络查询响应IO的百分之一。
当然服务端数据过滤的影响是需要考虑的,但相比网络out磁盘读流量太高了不正常。compact 读小文件写大文件压缩率提高了,这也是磁盘读流量大于网络OUT的可能。 磁盘读流量很大应该是compact读引起的,这个时间段又没有major compacted,说明可能小文件很多引起了写放大。

regionserver使用情况
regionserver配置
版本0.98.21,配置见附录一。
指标监控
队列等待时间p99最多有150ms,执行时间p99(ProcessCallTime_99th_percentile)在45ms内。

读缓存命中率95%,还ok

有一些慢写入

大概20分钟memstore整体刷一次,slowput的出现在刷memstore时

compaction排队的频率和memstore刷新基本一致,无major,说明是memstore定期大flush引起。

flush排队也发生在定期memstore flush时

看regionserver日志,固定间隔就会hlogs上限触发同时刷很多region,22MB的memstore刷5MB hifile,同时刷了好多小文件,又马上compact,写放大效果明显。
09:34:04,075 INFO [regionserver60020.logRoller] wal.FSHLog: Too many hlogs: logs=53, maxlogs=52; forcing flush of 69 regions(s) …
09:34:27,339 INFO [regionserver60020.logRoller] wal.FSHLog: Too many hlogs: logs=53, maxlogs=52; forcing flush of 222 regions(s) …

09:34:27,601 INFO [MemStoreFlusher.1] regionserver.DefaultStoreFlusher: Flushed, sequenceid=311232252, memsize=22.6 M, hasBloomFilter=true, into tmp file …
09:34:27,608 INFO [MemStoreFlusher.1] regionserver.HStore: Added …, entries=27282, sequenceid=311232252, filesize=5.4 M
09:34:27,608 INFO [MemStoreFlusher.1] regionserver.HRegion: Finished memstore flush of ~23.6 M/24788698, currentsize=0/0 for region … in 267ms, sequenceid=311232252, compaction requested=true
堆内存和gc情况,波动主要发生在周期刷memstore时。

问题总结
1.hbase.regionserver.maxlogs太少,
按照hlog和memstore的关系公式
hbase.regionserver.hlog.blocksize * hbase.reionserver.logroll.mutiplier*hbase.regionserver.maxlogs

= hbase.regionserver.global.memstore.lowerLimit * HBASE_HEAPSIZE
至少应该是95。配置太低频繁由于hlog上限触发几百个region flush,flush的都是小文件,flush和compact压力都大。
2.内存空闲太多,预留20%空闲就可以了。但要注意g1内存过大可能导致gc时间变长。
3.region数太多,大部分region的memstore没有128MB就强制flush了。正常应尽量保证每个 region 能够分配到 Flushsize 大小的内存,尽可能的 flush 大文件,从而减少后续 Compaction 开销。region数可以通过region合并减少,但这次先不优化,通过增大内存方式缓解。

参数优化探索
第一次修改
期望优化hlogs触发强刷memstore。
hbase.regionserver.maxlogs 52改为95.
重启regionserver。
变成47分钟触发hlog上限刷一次memstore了。

日志看,47分钟左右hlogs上限触发257 region同时刷新。和之前强刷一样,很多memstore20MB,压缩后的hfile5MB。总memstore峰值最多18.35GB,部分region 128MB提前flush了,所以到不了理论上限22.5GB。
compaction排队的频率降低了

队列等待时间波峰的间隔变长,有所改善。

gc次数和时间平常值略有降低,波峰提高。

slowput略有减少。

优化有一定效果。

第二次修改
由于region数太多,单个region可能不到128MB就触发hlog的强刷了。调大内存从而增加memstore和hlogs上限。计算预计在hlog上限前肯定能触发每小时一刷。调大了handler线程数。
hbase-env.sh 1.jvm XX:MaxDirectMemorySize 调为110GB
2.堆内Xmx调为80g
hbase-site.xml
3.hbase.bucketcache.size 112640 110g
hfile.block.cache.size 0.14
4.hbase.regionserver.global.memstore.upperLimit 0.56 44.8g,
hbase.regionserver.global.memstore.lowerLimit 0.5 40g,
5.hbase.regionserver.hlog.blocksize 268435456 256MB 显式配置,默认和hdfs块大小一致,也是配置的这个值。
hbase.regionserver.maxlogs 177
6.hbase.regionserver.handler.count 384
重启regionserver,观察监控。
memstore1小时一降,并不是hlog上限触发的,是每小时定时刷新触发的。

看日志,16:31陆续有region128MB flush。16:59 memstore 17.5GB, hlogs 125时,大部分region触发1小时刷新,delay几秒到几十秒。刷新时间还是较为集中,有flush和compact排队。其中6点的compactsize大且持续,应该是很多region需要compact的hfile变大了。

慢put变多,rpc执行时间峰值变高了,队列等待时间p99最长居然到2s!

原因是gc峰值变高了,影响regionserver业务了。

堆内存变大了,gc回收的变多,gc时间就变长了。

优化有些副作用,要消除。

第三次修改
期望优化增大堆内存引起的gc问题。memstore刷新间隔改为2小时,刷新size改为256MB
1.XX:InitiatingHeapOccupancyPercent=65 调整为XX:InitiatingHeapOccupancyPercent=75 之前hmaxlogs52限制实际memstore最大12g左右,堆内读写缓存加起来0.45左右。现在读写缓存加起来最多0.7了,所以调大触发gc的上限(bad modify)
2.-XX:ConcGCThreads=8,-XX:ParallelGCThreads=30,-XX:G1MixedGCCountTarget=32 增大gc并发,文档说建议逻辑核的5/8,48核30。并发标记跟着调大。拆解gc次数降低单次时间。
3.hbase.regionserver.optionalcacheflushinterval 改 7200000
4.hbase.hregion.memstore.flush.size 268435456 128MB的压缩会也就22MB hfile没多大,所以单个memstore内存改为256MB。
重启观察。
发现由于同时调大间隔时间和flush size,又变成1小时40分钟hlog上限触发强刷了

看日志,同时刷的region数少了,170个,flush和compact排队有所改善。

rpc和队列时间p99峰值还是太高,慢put不少

整体gc count减少,gc时间峰值变多变高

看gc的日志,调整gc并发,gc的系统时间依然很长,需要继续调整。
第四五次修改
gc触发阈值降低,提前gc。gc并发改小,一次最大10%的region。间隔时间改回1小时。改了两次,最终修改如下:
1.XX:InitiatingHeapOccupancyPercent=65, 又改到60 提前
2.-XX:ConcGCThreads=4,-XX:ParallelGCThreads=16,-XX:G1OldCSetRegionThresholdPercent=10 提高一次gc最多回收的region数。
3.hbase.regionserver.optionalcacheflushinterval 去掉,改回一小时。
重启观察。
memstore又是1小时一刷很多region,由于启动时间较短只执行了几个周期,还没有分散开。

flush和compact排队还是按小时出现

慢put少了,rpc和队列等待p99降低了,但希望能更低一些。

gc变多了,而时间少了一些但峰值依然挺高,看gc日志young gc时间也有挺长的,mixed gc时间依然挺长。

需要继续调优。

第六次修改
继续调gc参数,young gc内存控制大小。继续增大memstore配置让其能2小时一刷。由于region数太多,去掉flush 256MB设置。
-XX:G1MixedGCCountTarget=16 增大一次全局标记后mixed gc最多执行的次数
-XX:G1HeapWastePercent=10 允许保留10%垃圾
-XX:G1MaxNewSizePercent=20 young 上限20%,避免young区太大gc慢
hbase.regionserver.global.memstore.upperLimit 0.6 调大上限
hbase.regionserver.global.memstore.lowerLimit 0.54
hbase.regionserver.maxlogs 200 调大上限
hbase.regionserver.optionalcacheflushinterval 7200000 2小时一刷
hbase.hregion.memstore.flush.size 去掉用默认128MB

regionserver指标
2小时一刷memstore

flush和compact排队都少了

慢put减少,rpc和队列等待时间p99整体降低,峰值也降了很多

gc更平衡,峰值时间更短

机器负载
cpu load依然很低

磁盘读IO下降到1/4,网络out有所下降。

内存还有20%+的空闲,还可以多用点。

优化有效果。

总结
优化成果
本次优化通过调整regionserver参数和gc参数,减少小文件flush,降低compact写放大。平均响应时间降低,响应时间高峰次数减少。磁盘读写IO减少很多,从而增加了磁盘的使用时间。
后续优化方向
减少region数
单台regionserver上的region偏多。
region都是预分区,预分区时按预估总数据大小计算的分区数,但没考虑hfile压缩。几张大表单region的存储量都是region hfile上限容量的几分之一,可以合并region,减少region数。
增大内存配置
保留10%空闲即可
gc深入调优
这次gc参数调优没有明确目标,没有优化方法论,双盲优化,事倍功半,要深入gc参数细节,用明确的方法论,朝明确的目标优化。敬请期待。
减少服务器
瓶颈可能在存储容量上。hdfs逻辑存储共36.0 T,17台regionserver可以支持50TB逻辑存储容量,可以迭代减少几台,达到节约成本的目的。

附录
hbase-site.xml原始配置

hbase-env.sh原始配置

export HBASE_REGIONSERVER_OPTS="-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps
 -Xloggc:{HBASE_LOG_DIR}/hbase.heapdump -XX:ErrorFile=${HBASE_LOG_DIR}/hs_err_pid%p.log
 -XX:+PrintAdaptiveSizePolicy -XX:+PrintFlagsFinal -XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:+UnlockExperimentalVMOptions
 -XX:+ParallelRefProcEnabled -XX:ConcGCThreads=4 -XX:ParallelGCThreads=16 -XX:G1NewSizePercent=5
 -XX:G1MaxNewSizePercent=60 -XX:MaxTenuringThreshold=1 -XX:G1HeapRegionSize=32m -XX:G1MixedGCCountTarget=8 -XX:InitiatingHeapOccupancyPercent=65
 -XX:MaxDirectMemorySize=100g -XX:G1OldCSetRegionThresholdPercent=5 -Xmx50g -Xms50g"


g1参考资料