1. 背景
由于种种原因,最近将核心业务生产使用的HBase迁移到了云上的弹MapReduce(EMR)集群上,并使用了EMR的HBase组件默认参数配置。
结果在流量高峰期出现了宿主机故障,挂掉了两个core节点(部署了region server和datanode),大量region rit,花了15分钟才自动恢复,硬生生吃了一个P0故障。
复盘的时候发现,由于云上EMR对hdfs的socket超时参数默认设置了900000(15min),导致了region重新上线读取故障节点WAL日志的时候足足等待了15分钟才去重试下个节点。这样的自愈时间显然是不满足「在线业务」的需求的,需要将这个超时时间调整到60000(1min),实现快速自愈的目的。
因此,结合HBase自身组件特性与 「在线业务」高可用、低抖动 诉求,全面整理了HBase参数调优的最佳实践。
2.先回顾下HBase基础架构
这里只是简单回顾下整体架构,方便对照各个组件聊一聊需要优化的参数。更详细内容可以参考我过去整理的《全面认识HBase架构(建议收藏)》
2.1整体架构
从物理结构上,HBase包含了三种类型的server,zookeeper、HMaster、RegionServer,从而形成了一种主从模式的结构。
- RegionServer主要用来服务读和写操作。当用户通过client访问数据时,client会和HBase RegionServer 进行直接通信。
- HMaster主要进行RegionServer的管理、DDL(创建、删除表)操作等。
- Zookeeper是HDFS(Hadoop Distributed File System)的一部分,主要用来维持整个集群的存活,保障了HA,故障自动转移。
- 底层的存储,还是依赖于HDFS的。Hadoop的DataNode存储了RegionServer所管理的数据,所有HBase的数据都是存在HDFS中的。Hadoop的NameNode维护了所有物理数据块的metadata。
2.2RegionServer组成
一个RegionServer运行在一个HDFS的DataNode上,并且拥有以下组件:
- WAL:全称Write Ahead Log, 属于分布式系统上的文件。主要用来存储还未被持久化到磁盘上的新数据。如果新数据还未持久化,节点发生宕机,那么就可以用WAL来恢复这些数据。
- BlockCache:是一个读缓存。它存储了被高频访问的数据。当这个缓存满了后,会清除最近最少访问的数据。
- MenStore: 是一个写缓存。它存储了还未被写入磁盘的数据。它会在写入磁盘前,对自身数据进行排序,从而保证数据的顺序写入。每个region的每个colum family会有一份对应的memstore。
- HFiles:按照字典序存储各个row的键值。
3.读优化
3.1优化度/写内存比例
一个RegionServer上有一个BlockCache和N个Memstore,它们的大小之和必须小于HeapSize* 0.8,否则HBase不能启动,因为仍然要留有一些内存保证其它任务的执行。
BlockCache作为读缓存,对于读的性能比较重要,如果读比较多,建议内存使用1:4的机器,比如:8cpu32g或者16pu64g的机器。
读多写少的场景下,可以调高BlockCache的数值,降低Memstore的数值来提高读场景性能。
核心调整参数如下:
- hfile.block.cache.size = 0.5 - hbase.regionserver.global.memstore.size = 0.3
3.2减少GFile数量
因为HBase读取时没有命中缓存,就需要打开HFile。如果HFile文件越多,IO次数就越多,读取的延迟就越高。
因此,HBase通过compaction机制来合并HFile。
但是,对于「在线业务」来说,白天流量高峰做compact会严重影响磁盘IO,造成读写毛刺,因此需要对compact限速。
3.3开启短路度特性
HBase数据是存储在HDFS,从HDFS读取数据需要经过DataNode,开启Short-Circuit Local Read后,客户端可以直接读取本地数据。
假设现有两个用户User1和User2,User1拥有访问HDFS目录上/appdata/hbase1文件的权限,而User2用户没有该权限,但是User2用户又需要访问这个文件,那么可以借助UNIX中「文件描述符传递」的机制,可以让User1用户打开文件得到一个文件描述符,然后把文件描述符传递给User2用户,那么User2用户就可以读取文件里面的内容了,即使User2用户没有权限。
这种关系映射到HDFS中,可以把DataNode看作User1用户,客户端DFSClient看作User2用户,需要读取的文件就是DataNode目录中的/appdata/hbase1文件。实现如下图所示:
核心参数如下:
- dfs.client.read.shortcircuit = true
3.4开启对冲读特性(需要评估磁盘IO)
当我们开启「短路读」特性后,优先会通过Short-Circuit Local Read功能尝试本地读。但是在某些特殊情况下,有可能会出现因为磁盘问题或者网络问题引起的短时间本地读取失败。
为了应对这类问题,HBase实现了「对冲读」特性Hedged Read。
该机制基本工作原理为:
客户端发起一个本地读,一旦一段时间之后还没有返回,客户端将会向其他DataNode发送相同数据的请求。哪一个请求先返回,另一个就会被丢弃。
当然,这个特性显然会放大磁盘IO的压力,需要谨慎评估使用。
核心参数如下:(根据实际环境对参数进行调整)
- dfs.client.hedged.read.threadpool.size = 10 //指定有多少线程用于服务hedged reads。如果此值设置为0(默认),则hedged reads为disabled状态 - dfs.client.hedged.read.threshold.millis:默认为500(0.5秒):在spawning 第二个线程前,等待的时间。