线上状况
服务器信息
本人使用的各组件版本如下: 操作系统版本:centos7 CM版本:6.3.1 CDH版本:6.3.2(5.16.2版本实测也可以) JDK版本:openjdk version “1.8.0_262”
服务器配置:
服务器 | CPU | 内存 | 磁盘 |
---|
cmserver | 8核 | 24G | /home/app 挂载300G | master01 | 4核 | 24G | /home/app 挂载300G | master02 | 4核 | 24G | /home/app 挂载300G | slave01 | 4核 | 24G | /home/hdfsdata/data1 挂载100T | slave02 | 4核 | 24G | /home/hdfsdata/data1 挂载100T | slave03 | 4核 | 24G | /home/hdfsdata/data1 挂载100T | slave04 | 4核 | 24G | /home/hdfsdata/data1 挂载100T |
线上的hbase集群运行时发现,当数据量写入比较大时,经常会报:OutOfMemory errors 的错误 报错截图如下:
报错时的集群负载情况如下: HDFS写入情况如下,集群连续几天处于高负荷状态,每个DataNode平均读写总量约为200M/s。
集群网络IO情况如下,各网络接口中的总传送的字节数约为187M/s,各网络接口中的总接收的字节数约为232M/s。
可以看到,一天时间内,RegionServer 多次因内存不足而退出。
各RegionServer 的web响应时间如下,从图中可以看出响应时间有一定的波动,并且跟上图 RegionServer 因内存不足而退出在时间上是相对应的,当发生因内存不足而退出时,web响应时间也变的很高,会导致客户度读写超时。
Region Server占用的内存如下,基本稳定在4G左右,因为配置的最大内存就是4G。在发生因内存不足而退出的时间点上,驻留内存会有一些的降低。
hbase数据存储机制
要对HBASE集群进行调优之前,需要先了解HBASE的数据存储机制。
hbase compaction机制
hbase compaction参考文章: https://blog.csdn.net/u011598442/article/details/90632702
Compaction机制的由来 HBase基于LSM-Tree(Log-Structured Merge Tree)存储模型,写入数据时是先写入WAL(Write-Ahead-Log)即预写日志,再写入Memstore缓存,满足一定条件后执行flush操作将缓存数据吸入到磁盘,每次flush就会生成一个HFile数据文件。HFile文件数过多会导致查询的数据来源分散到很多不同的HFile中,增加了查询数据的IO次数,影响HBase查询性能。因此HBASE设计了Compaction机制,将小的HFile文件合并成大文件减少HFile文件数量,来提高查询性能。
hbase compaction原理图:
Compaction分类
-
Mini Compaction 选取一些小的、相邻的HFile将他们合并成一个更大的HFile。minor compaction会删除选取HFile中的TTL过期的数据。 -
Major Compaction 将一个Store中所有HFile合并成一个HFile。major compaction会清理被删除的数据、TTL过期的数据、版本号超过设定版本号的数据。 Major Compaction持续时间较长,并消耗大量系统资源,生产环境建议关闭自动触发Major Compaction,改为在业务低峰期手动触发。
Compaction触发条件
- MemStore Flush
每次MemStore Flush都会产生HFile文件,当HFile文件数超过 hbase.hstore.compactionThreshold 设置的值,就会触发Compaction; - 后台线程定期检查
默认7天触发一次Major Compaction,生产环境建议关闭自动触发Major Compaction,关闭需将参数 hbase.hregion.majorcompection置为0; - 手动触发
生产环境推荐业务低峰期手动触发Major Compaction;
集群参数调优
要解决该问题需要先对 Hbase集群的参数有些了解,重要的一些参数如下:
-
hbase.regionserver.handler.count: regionserver上用于等待响应用户表级请求的线程数,CDH安装时的默认值是30。这个值的大小取决于每次读写的数据量大小,当请求内容很大(上MB,比如大的put、使用缓存的scans)的时候,该值设置过大则会占用过多的内存,导致频繁的GC,或者出现OutOfMemory,因此该值需要根据实际业务作判断。 -
hbase.client.write.buffer: 写入缓冲区大小(以字节为单位),默认值为2M。较大缓冲区需要客户端和服务器中有较大内存,因为服务器将实例化已通过的写入缓冲区并进行处理,这会降低远程过程调用 (RPC) 的数量。为了估计服务器已使用内存的数量,请用值“hbase.client.write.buffer”乘以“hbase.regionserver.handler.count”。
大量数据写入阻塞相关优化
磁盘IO写入性能优化参数
-
hbase.hregion.max.filesize: HStoreFile 最大大小。如果列组的任意一个 HStoreFile 超过此值,则托管 HRegion 将分割成两个,默认大小是10G。region的大小与集群支持的总数据量有关系,如果总数据量小,则单个region太大,不利于并行的数据处理,如果集群需支持的总数据量比较大,region太小,则会导致region的个数过多,导致region的管理等成本过高。 如果一个Region Server配置的磁盘时12TX3=36T,3台Region Server服务器,数据备份3份,则每台Region Server最多存储12T的数据,HStoreFile 最大大小为10G,每台Region Server最多管理1200个region。通常region的数量不要超过1000个,过多的region。如果Region Server配置的磁盘比36T更大,可以考虑调大HStoreFile 的最大大小。 -
hbase.hstore.compactionThreshold: HStore 压缩阈值,默认值为3。如在任意一个 HStore 中有超过此数量的 HStoreFiles,则将触发mini compaction以将所有 HStoreFiles 文件作为一个 HStoreFile 重新写入。(每次 memstore 刷新都会生成一个 HStoreFile)您可通过指定更大数量延长压缩,但压缩将运行更长时间。在压缩期间,更新无法刷新到磁盘。长时间压缩需要足够的内存,以在压缩的持续时间内记录所有更新。否则压缩期间客户端会超时。 -
hbase.regionserver.thread.compaction.small: RegionServer 完成Mini Compact的线程数,增大可以加快Mini compact速度,默认值为1。对于写入压力比较大的业务,可以调大到8。 -
hbase.hstore.blockingStoreFiles: HStore 阻塞存储文件数,默认值16。如在任意 HStore 中有超过此数量的 HStoreFiles,则会阻止对此 HRegion 的数据写入更新操作,直到完成压缩或直到超过为 ‘hbase.hstore.blockingWaitTime’ 指定的值。对于写入压力比较大的业务,强烈建议调大该值,但该值越大数据读取耗时也越长,建议设置到500,避免当写入大量数据时,memstore不能及时flush,触发memstore的block,从而阻塞写操作。
内存写入性能优化参数
-
HBase RegionServer 的 Java 堆栈大小: Region Server java堆大小会限制memstore内存的上限,内存充足的情况下,该值要尽可能设置大一些,有条件的可以设置成10G或者以上。 -
hbase.regionserver.global.memstore.upperLimit: RS所有memstore占用内存在总内存中的上限比例,默认值0.4。当达到该值,会从整个RS中找出最需要flush的region进行flush,直到总内存比例降至该数限制以下,并且在降至限制比例以下前将阻塞所有的写memstore的操作。如果集群写入量比较大可以调高该参数。 一个regionserver上有一个blockcache和N个memstore,它们的大小之和必须小于heapsize(HBase RegionServer 的 Java 堆栈大小)* 0.8,否则hbase不能启动,因为仍然要留有一些内存保证其它任务的执行。因此 hbase.regionserver.global.memstore.upperLimit + hfile.block.cache.size 之和必须小于0.8,因此需要根据业务偏向于读操作还是偏向于写操作调整两者的比例。兼顾读和写的场景可以两者都设置为0.4。 -
hfile.block.cache.size: Region Server中的block cache的内存大小限制,默认值0.4。在偏向读的业务中,可以适当调大该值。但是该值和hbase.regionserver.global.memstore.upperLimit之和不能超过0.8。 -
hbase.hregion.memstore.block.multiplier: HBase Memstore 块乘法器,默认值为4。如 memstore 的内存大小超过 ‘hbase.hregion.block.memstore’ 乘以 ‘hbase.hregion.memstore.flush.size’ 字节的值,则会阻塞该Menstore的写入操作,避免大量数据写入内存导致内存溢出。同时该内存大小也受到hbase.regionserver.global.memstore.upperLimit上限的限制。内存充足的情况下建议 hbase.hregion.block.memstore 设置为8,hbase.hregion.memstore.flush.size 设置为256M。 -
hbase.hregion.memstore.flush.size: HBase Memstore 刷新大小,默认值128M,如果 memstore 大小超过此值,Memstore 中的数据将写入到磁盘中。该值比较适中,一般不需要调整,Region Server的jvm内存比较充足(10G以上),可以调整为256M,避免频繁触发flush,导致频繁触发compact操作。
解决方案
-
增大RegionServer的内存 HBASE官方推荐的RegionServer内存大小为32G,我分配的线上服务器是24G是有点不够用的; 官方文档地址:https://hbase.apache.org/book.html#regionserver_sizing_rules_of_thumb -
集群参数调优 参数调整:
hbase.regionserver.handler.count=100
HBase RegionServer 的 Java 堆栈大小=9G
hbase.hregion.memstore.flush.size=256M
hbase.regionserver.global.memstore.upperLimit=0.4
hfile.block.cache.size=0.4
hbase.hregion.memstore.block.multiplier=8
hbase.regionserver.thread.compaction.small=8
hbase.hstore.blockingStoreFiles=500
-
增加RegionServer节点数量 我线上部署的RegionServer节点数是4台, -
HBASE写入流量管控 还需要结合业务层面对HBASE写入的流量进行控制。我是通过NGINX限流对最前端的数据上传作流量管控,并且使用线程池控制业务上处理数据的并发数两个方面来控制集群写入的流量。 NGINX限流配置参考文章:https://blog.csdn.net/weixin_39338423/article/details/125317886
调整后效果 调整后集群发生 OutOfMemory errors 的情况大幅降低,在长时间持续写入大量数据的情况下偶尔还是会有发生,此时需要增加RegionServer节点或者给RegionServer添加额外的磁盘(必须是物理磁盘,RegionServer下挂载多块物理磁盘可以增加并发写入的速度)。
调整过后的集群内存占用情况如下:
平均每个 RegionServer 压缩队列情况:
平均每个 RegionServer 刷新队列情况:
|