写点什么

HBase 优化如何操作

作者:编程江湖
  • 2022 年 1 月 05 日
  • 本文字数:4272 字

    阅读完需:约 14 分钟

 在介绍读流程之后,我们再结合有赞业务上的实践来介绍如何优化读请求,既然谈到优化,就要先知道哪些点可会影响读请求的性能,我们依旧从客户端和服务端两个方面来深入了解优化的方法。

1、客户端层面

HBase 读数据共有两种方式,Get 与 Scan。

在通用层面,在客户端与服务端建连需要与 zookeeper 通信,再通过 meta 表定位到 region 信息,所以在初次读取 HBase 的时候 rt 都会比较高,避免这个情况就需要客户端针对表来做预热,简单的预热可以通过获取 table 所有的 region 信息,再对每一个 region 发送一个 Scan 或者 Get 请求,这样就会缓存 region 的地址;

rowkey 是否存在读写热点,若出现热点则失去分布式系统带来的优势,所有请求都只落到一个或几个 HRegion 上,那么请求效率一定不会高;

读写占比是如何的。如果写重读轻,浏览服务端 RegionServer 日志发现很多 MVCC STUCK 这样的字样,那么会因为 MVCC 机制因为写 Sync 到 WAL 不及时而阻塞读,这部分机制比较复杂,考虑之后分享给大家,这里不详细展开。

Get 请求优化

  • 将 Get 请求批量化,减少 rpc 次数,但如果一批次的 Get 数量过大,如果遇到磁盘毛刺或者 Split 毛刺,则 Get 会全部失败(不会返回部分成功的结果),抛出异常。

  • 指定列族,标识符。这样可以服务端过滤掉很多无用的 scanner,减少 IO 次数,提高效率,该方法同样适用于 Scan。

Scan 请求优化

  • 设定合理的 startRow 与 stopRow 。如果 scan 请求不设置这两个值,而只设置 filter,则会做全表扫描。

  • 设置合理的 caching 数目, scan.setCaching(100)。 因为 Scan 潜在会扫描大量数据,因此客户端发起一次 Scan 请求,大数据培训实际并不会一次就将所有数据加载到本地,而是分成多次 RPC 请求进行加载。默认值是 100。用户如果确实需要扫描海量数据,同时不做逻辑分页处理,那么可以将缓存值设置到 1000,减少 rpc 次数,提升处理效率。如果用户需要快速,迭代地获取数据,那么将 caching 设置为 50 或者 100 就合理。

2、服务端优化

相对于客户端,服务端优化可做的比较多,首先我们列出有哪些点会影响服务端处理读请求。

  • gc 毛刺

  • 磁盘毛刺

  • HFile 文件数目

  • 缓存配置

  • 本地化率

  • Hedged Read 模式是否开启

  • 短路读是否开启

  • 是否做 高可用

gc 毛刺没有很好的办法避免,通常 HBase 的一次 Young gc 时间在 20~30ms 之内。磁盘毛刺发生是无法避免的,通常 SATA 盘读 IOPS 在 150 左右,SSD 盘随机读在 30000 以上,所以存储介质使用 SSD 可以提升吞吐,变向降低了毛刺的影响。HFile 文件数目因为 flush 机制而增加,因 Compaction 机制减少,如果 HFile 数目过多,那么一次查询可能经过更多 IO ,读延迟就会更大。这部分调优主要是优化 Compaction 相关配置,包括触发阈值,Compaction 文件大小阈值,一次参与的文件数量等等,这里不再详细展开。读缓存可以设置为为 CombinedBlockCache,调整读缓存与 MemStore 占比对读请求优化同样十分重要,这里我们配置 hfile.block.cache.size 为 0.4,这部分内容又会比较艰深复杂,同样不再展开。下面结合业务需求讲下我们做的优化实践

我们的在线集群搭建伊始,接入了比较重要的粉丝业务,该业务对 RT 要求极高,为了满足业务需求我们做了如下措施。

2.1 异构存储

HBase 资源隔离+异构存储。SATA 磁盘的随机 iops 能力,单次访问的 RT,读写吞吐上都远远不如 SSD,那么对 RT 极其敏感业务来说,SATA 盘并不能胜任,所以我们需要 HBase 有支持 SSD 存储介质的能力。

为了 HBase 可以支持异构存储,首先在 HDFS 层面就需要做响应的支持,在 HDFS 2.6.x 以及之后的版本,提供了对 SSD 上存储文件的能力,换句话说在一个 HDFS 集群上可以有 SSD 和 SATA 磁盘并存,对应到 HDFS 存储格式为 [ssd] 与 [disk]。然而 HBase 1.2.6 上并不能对表的列族和 RegionServer 的 WAL 上设置其存储格式为 [ssd], 该功能在社区 HBase 2.0 版本之后才开放出来,所以我们从社区 backport 了对应的 patch ,打到了我们有赞自己的 HBase 版本之上。支持 [ssd] 的 社区 issue 如下:

https://issues.apache.org/jira/browse/HBASE-14061?jql=text%20~%20%22storage%20policy%22

添加 SSD 磁盘之后,HDFS 集群存储架构示意图如图 5 所示:



图 5 混合机型下 HDFS 集群存储逻辑示意图

理想的混合机型集群异构部署,对于 HBase 层面来看,文件存储可选三种策略:HOT, ONE_SSD, ALL_SSD,其中 ONE_SSD 存储策略既可以把三个副本中的两个存储到便宜的 SATA 磁盘介质之上来减少 SSD 磁盘存储成本的开销,同时在数据读取访问本地 SSD 磁盘上的数据可以获得理想的 RT ,是一个十分理想的存储策略。HOT 存储策略与不引入异构存储时的存储情况没有区别,而 ALL_SSD 将所有副本都存储到 SSD 磁盘上。 在有赞我们目前没有这样的理想混合机型,只有纯 SATA 与 纯 SSD 两种大数据机型,这样的机型对应的架构与之前会有所区别,存储架构示意图如图 6 所示。


图 6 纯 SATA 与纯 SSD 机型 HDFS 集群存储架构逻辑示意图

基于这样的场景,我们做了如下规划:

  1. 将 SSD 机器规划成独立的组,分组的 RegionServer 配置 hbase.wal.storage.policy=ONE_SSD, 保证 wal 本身的本地化率;

  2. 将 SSD 分组内的表配置成 ONE_SSD 或者 ALL_SSD;

  3. 非 SSD 分组内的表存储策略使用默认的 HOT

具体的配置策略如下:在 hdfs-site.xml 中修改

<property>
<name>dfs.datanode.data.dir</name>
<value>[SSD]file:/path/to/dfs/dn1</value>
</property>
复制代码


在 SSD 机型 的 RegionServer 中的 hbase-site.xml 中修改

<property>
<name>hbase.wal.storage.policy</name>
<value>ONE_SSD</value>
</property>
复制代码


其中 ONE_SSD 也可以替代为 ALL_SSD。 SATA 机型的 RegionServer 则不需要修改或者改为 HOT。

2.2 HDFS 短路读

开启 HDFS 的短路读模式。该特性由 HDFS-2246 引入。我们集群的 RegionServer 与 DataNode 混布,这样的好处是数据有本地化率的保证,数据第一个副本会优先写本地的 Datanode。在不开启短路读的时候,即使读取本地的 DataNode 节点上的数据,也需要发送 RPC 请求,经过层层处理最后返回数据,而短路读的实现原理是客户端向 DataNode 请求数据时,DataNode 会打开文件和校验和文件,将两个文件的描述符直接传递给客户端,而不是将路径传递给客户端。客户端收到两个文件的描述符之后,直接打开文件读取数据,该特性是通过 UNIX Domain Socket 进程间通信方式实现,流程图如图 7 所示。


图 7 短路读流程图

该特性内部实现比较复杂,设计到共享内存段通过 slot 放置副本的状态与计数,这里不再详细展开。

开启短路读需要修改 hdfs-site.xml 文件:

    <property>        <name>dfs.client.read.shortcircuit</name>       <value>true</value>    </property>
<property> <name>dfs.domain.socket.path</name>
<value>/var/run/hadoop/dn.socket</value> </property>
复制代码


2.3 HDFS Hedged Read 

开启 Hedged Read 模式。当我们通过短路读读取本地数据因为磁盘抖动或其他原因读取数据一段时间内没有返回,去向其他 DataNode 发送相同的数据请求,先返回的数据为准,后到的数据抛弃,这也可以减少磁盘毛刺带来的影响。默认该功能关闭,在 HBase 中使用此功能需要修改 hbase-site.xml

   <property>       <name>dfs.client.hedged.read.threadpool.size</name><value>50</value>    </property>
<property> <name>dfs.client.hedged.read.threshold.millis</name> <value>100</value> </property>
复制代码


线程池大小可以与读 handler 的数目相同,而超时阈值不适宜调整的太小,否则会对集群和客户端都增加压力。同时可以通过 Hadoop 监控查看 hedgedReadOps 与 hedgedReadOps 两个指标项,查看启用 Hedged read 的效果,前者表示发生了 Hedged read 的次数,深圳大数据培训后者表示 Hedged read 比原生读要快的次数。

2.4 高可用读

高可用读。HBase 是一个 CP 系统,同一个 region 同一时刻只有一个 regionserver 提供读写服务,这保证了数据的一致性,即不存在多副本同步的问题。但是如果一台 regionserver 发声宕机的时候,系统需要一定的故障恢复时间 deltaT, 这个 deltaT 时间内,region 是不提供服务的。这个 deltaT 时间主要由宕机恢复中需要回放的 log 的数目决定。集群复制原理图如下图 8 所示。


图 8 HBase Replication 原理图

HBase 提供了 HBase Replication 机制,用来实现集群间单方向的异步数据复制我们线上部署了双集群,备集群 SSD 分组和主集群 SSD 分组有相同的配置。当主集群因为磁盘,网络,或者其他业务突发流量影响导致某些 RegionServer 甚至集群不可用的时候,就需要提供备集群继续提供服务,备集群的数据可能会因为 HBase Replication 机制的延迟,相比主集群的数据是滞后的,按照我们集群目前的规模统计,平均延迟在 100ms 以内。所以为了达到高可用,粉丝业务可以接受复制延迟,放弃了强一致性,选择了最终一致性和高可用性,在第一版采用的方案如下:


图 9 客户端高可用方案原理图

粉丝业务方是不想感知到后端服务的状态,也就是说在客户端层面,他们只希望一个 Put 或者 Get 请求正常送达且返回预期的数据即可,那么就需要高可用客户端封装一层降级,熔断处理的逻辑,这里我们采用 Hystrix 做为底层熔断处理引擎,在引擎之上封装了 HBase 的基本 API,用户只需要配置主备机房的 ZK 地址即可,所有的降级熔断逻辑最终封装到 ha-hbase-client 中,原理类似图 9,这里不再赘述。

2.5 预热失败问题修复

应用冷启动预热不生效问题。该问题产生的背景在于应用初始化之后第一次访问 HBase 读取数据时候需要做寻址,具体流程见图 2,这个过程涉及多次 RPC 请求,所以耗时较长。在缓存下所有的 Region 地址之后,客户端与 RegionServer 就会做点对点通信,这样 RT 就有所保证。所以我们会在应用启动的时候做一次预热操作,而预热操作我们通常做法是调用方法 getAllRegionLocations 。在 1.2.6 版本(后来经过笔者调研,1.3.x,以及 2.x 版本)getAllRegionLocations 存在 bug ,该方案预期返回所有的 Region locations 并且缓存这些 Region 地址,但实际上,该方法只会缓存 table 的第一个 Region, 笔者发现此问题之后反馈给社区,并提交了 patch 修复了此问题,issue 连接:https://issues.apache.org/jira/browse/HBASE-20697?filter=-2 。这样通过调用修复 bug 之后的 getAllRegionLocations 方法,即可在应用启动之后做好预热,在应用第一次读写 HBase 时便不会产生 RT 毛刺。

粉丝业务主备超时时间都设置为 300ms。经过这些优化,其批量 Get 请求 99.99% 在 20ms 以内,99.9999% 在 400ms 以内。

用户头像

编程江湖

关注

IT技术分享 2021.11.23 加入

还未添加个人简介

评论

发布
暂无评论
HBase 优化如何操作