大数据运维及安全系统运维专家@IT·互联网

HBase 线上问题排查 - 为什么读写这么少还会触发巨量 IO

2017-07-27  本文已影响419人  大数据之心

前言

之前的文章都是偏环境的部署、搭建、测试以及一些官文翻译,实在是没有什么硬货,现在部分业务开始上生产,那么稳定性就是首当其冲的。最近负责排查了一个线上关于 HBase 的问题,特别来分享下思路。

我湾使用的是 Cloudera Manager 来管理 CDH 相关组件,所以以下涉及的操作都是在 Cloudera Manager 里完成的,包括监控、参数调优等。因为项目涉密所以很多线上环境的截图我就不方便发了,但是会尽量阐述的易懂。

问题发现

我湾的报警是通过 nagios 周期回调脚本进行的,上周开始频繁收到类似如下报警:

HBASE_REGION_SERVERS_HEALTHY=CONCERNING(服务健康处于关注状态)。

不得不说 Cloudera Manager 报警抽象的不错,我们可以通过整体健康度去关注一个服务。然后我去线上看了看,发现是 flush queue size 达到 warning 阈值,默认是 10。这个监控项的意义在于,告诉运维人员,memstore 刷内存太猛了,queue 积压了。

问题分析

只要你理解 memstore 刷内存的触发机制(不清楚的可以去补补课、看看代码),就可以知道导致这个问题的原因大致如下:

反观线上生产监控,我发现 TPS(写入吞吐)非常小,也就 10 不到,QPS(读吞吐)也很小,不到100,吞吐量也都很小,那么就排除了 A 情况。查看生产日志(regionserver的日志),发现有很多类似如下的输出:

... because info has and old edit so flush to free WALs after random delay ...

这是很明显的写入量很小,因为周期性 flush 线程触发的行为,比如某 store 很久没更新了而最新的 edit 距今超过阈值(默认 1小时),那么就会 delay 一个 random 时间去执行刷新。我通过如下关键字去看历次触发的 flush 产生的文件大小:

grep 'org.apache.hadoop.hbase.regionserver.HStore: Added hdfs' /home/admin/logs/hbase/hbase-cmf-hbase-REGIONSERVER-${FQDN_HOSTNAME}.log.out | awk -F 'filesize=' '{print$2}'

发现几乎每次刷出来的都是小文件,不到 100KB,由此可以排除情况 B。

好,接下来看看,是什么间接导致我们的 flush queue 积压呢?运维看监控有一个绝招是说,看对应时间点的指标趋势,我看到对应有高峰的时间点之前几分钟,都会来一波巨量的 compaction,同时做 compaction 的时候对应的日志里看到 flush 出来的文件的确有超过 100MB 的情况,并且是所有机器基本同时有这样的刷写(也就是基本达到了 hbase.hregion.memstore.flush.size,看起来个别 store 是符合情况 B的)。因为我们只有 3 个SATA 节点,所以 100MB 的刷写还是有点儿难受的,因为三份冗余,我们实际会产生 100MB 的读、300MB 的写,产生 400MB 的流量,每次触发高峰的时候都是 3 台机器基本同时 flush 出 100MB + 的文件,从而导致海量 compaction。

所以,问题的原因为:

多台机器同时刷出 100MB + 的 store file 文件 -> 多台机器同时因为文件数达到阈值(默认是 3 )触发 compaction -> 3份冗余产生 IO Storm -> flush queue 随之积压。

解决方案

知道问题的根源就好办事儿了,在这样的情况下,文件数量肯定不是问题(也就是不会影响读性能,或者说 QPS 这么少,担心啥呢?)我就简述一下我的方案,利用 PressureAwareCompactionThroughputController 限制业务高峰期(10:00-21:00)时的 compaction 流量,减少 IO 波峰,缓解 flush queue 积压。其中业务高峰期很好判断,查看 Cloudera 相关监控指标就知道,啥时候写入比较少了,我们的业务是在晚上21点后就基本没有流量了。

关于 PressureAwareCompactionThroughputController

相应的算法如下:

那么 compactionPressure 到底是怎么得到的?看了一下 Store.getCompactionPressure() 这个方法,是这么说的:

该值表述的是某个 store 需要做 compaction 的紧急程度。必须大于等于 0.0 ,也就是正数,任何大于 1.0 的数值表示我们有过多 store files。

  • 如果 getStorefilesCount <= getMinFilesToCompact 返回 0.0;
  • return (getStorefilesCount - getMinFilesToCompact) / (blockingFileCount - getMinFilesToCompact);

对于 striped stores,我们必须根据各个 strip 分区的最大值得到最终全局最大的值。

有关 stripe compaction 可以参考这里

getStorefilesCount() 返回的是某个 store 下的 store files 数量,getMinFilesToCompact() 返回的是 minor compaction 包含的文件数量下限,默认为 3;blockingFileCount 表示的是达到这个数值的话阻塞该 store 的写入直至 compaction 完成后文件数小余该数值,默认为 7。

看起来,某 store 只要文件数在 3 ~ 7 个的时候,就会触发限流,认为即使不做 compaction 或者慢慢做,都不会因为文件数过多,极大影响读性能。对于线上集群,getMinFilesToCompact 是 3、blockingFileCount 是 7,平均 store files 长期在 2.8 左右,可以考虑通过该方案 smooth compaction 流量

方案测试

调整 HBase 配置,分为三组限流进行写入压力测试(测试工具是 HBase Load Test Tool),目标是查看不同的阈值对 compact rate、disk io、network io 的影响。

测试环境:

公司日常测试集群,配置为 6core 16GB 内存 SATA。

测试指令如下:

hbase ltt -tn TestTable -write 10:1000 -num_keys 1000000

配置大致如下:

<property>
    <name>hbase.regionserver.throughput.controller</name>
    <value>org.apache.hadoop.hbase.regionserver.compactions.PressureAwareCompactionThroughputController</value>
    <discription>使用压力感知compaction限流策略控制器</discription>
</property>
<property>
    <name>hbase.hstore.compaction.throughput.higher.bound</name>
    <value>${HIGHER_BOUND}</value>
    <discription>限流上限阈值</discription>
</property>
<property>
    <name>hbase.hstore.compaction.throughput.lower.bound</name>
    <value>${LOWER_BOUND}</value>
    <discription>限流下限阈值</discription>
</property>
<property>
    <name>hbase.hstore.compaction.throughput.offpeak</name>
    <value>9223372036854775807</value>
    <discription>非高峰期阈值,为Long.MAX即不限流</discription>
</property>
<property>
    <name>hbase.offpeak.start.hour</name>
    <value>21</value>
    <discription>非高峰期开始小时时刻</discription>
</property>
<property>
    <name>hbase.offpeak.end.hour</name>
    <value>8</value>
    <discription>非高峰期结束小时时刻</discription>
</property>
<property>
    <name>hbase.hstore.compaction.throughput.tune.period</name>
    <value>60000</value>
    <discription>限流调整周期,单位毫秒</discription>
</property>

对比组有三个:

我们比较三组的 compact rate、disk io、network io,分别如下:

compact rate(A B C 自上而下):

compact_rate_1M_512KB compact_rate_10M_5M compact_rate_100M_50M

disk io(A B C 自上而下):

disk_io_1M_512KB disk_io_10M_5M disk_io_100M_50M

network io(A B C 自上而下):

network_io_1M_512KB network_io_10M_5M network_io_100M_50M

测试结论

在对读影响较小的(文件数不超过 blockingFileCount)情况下,PressureAwareCompactionThrottle 可以有效平滑 compaction 带来的流量、磁盘 IOPS 冲击。

测试的三组里,A、B 组 有部分情况下没有符合限流预期是因为局部 store 文件数过多导致,属于合理。B、C 组没有明显区别是因为三台虚拟机节点写入吞吐已经在 B 组基本达到瓶颈,所以有价值的比较是 A、B 两组。

小结

我坚持授人以渔,运维去解决线上问题的时候,必须要确定问题的根源对症下药,而非一知半解去看待,否则这个雷还在,只会越治理越乱。我们必须对开源产品要有足够的把控力,看源码、理解架构是非常有必要的,对自己的定位切记不能只是一个只会部署、重启的 ops,那最终只会被 devops 替代。同样的,我也不认为运维只有 devops 或者 SRE 一条路去走,只要熟悉生态,勤于学习不同的优秀架构和数据结构,一通则全通,甚至在开发工程师没有解决思路的时候提供有效解决方案,在架构设计的时候提出自己有效的建议,至少我会坚持在架构这条路上,当初选择放弃钻研代码,那么现在爬也会爬完(当然,读代码的能力是必须要有的)。

上一篇下一篇

猜你喜欢

热点阅读