HBase+Spark社区 + 关注
手机版

技术篇-HBase 最佳实践-读性能优化策略

  1. 云栖社区>
  2. HBase+Spark社区>
  3. 博客>
  4. 正文

技术篇-HBase 最佳实践-读性能优化策略

hbase小能手 发布时间:2019-01-10 13:50:53 浏览631 评论0

摘要: 任何系统都会有各种各样的问题,有些是系统本身设计问题,有些却是使用姿势问题。HBase也一样,在真实生产线上大家或多或少都会遇到很多问题,有些是 HBase 还需要完善的,有些是我们确实对它了解太少。总结起来,大家遇到的主要问题无非是 Full GC 异常导致宕机问题、RIT 问题、写吞吐量太低以及读延迟较大。

Full GC 问题之前在一些文章里面已经讲过它的来龙去脉,主要的解决方案目前 主要有两方面需要注意,一方面需要查看 GC 日志确认是哪种 Full GC,根据 Full GC 类型对 JVM 参数进行调优,另一方面需要确认是否开启了 BucketCache 的 offheap 模式,建议使用 LRUBlockCache 的童鞋尽快转移到 BucketCache 来。当然我们还是很期待官方 2.0.0 版本发布的更多 offheap 模块。

RIT 问题,我相信更多是因为我们对其不了解,具体原理可以戳这里,解决方案目前有两个,优先是使用官方提供的 HBCK 进行修复(HBCK 本人一直想拿出来分享,但是目前案例还不多,等后面有更多案例的话再拿出来说),使用之后还 是解决不了的话就需要手动修复文件或者元数据表。

而对于写吞吐量太低以及读延迟太大的优化问题,笔者也和很多朋友进行过探讨,这篇文章就以读延迟优化为核心内容展开,具体分析 HBase 进行读延迟优化的 那些套路,以及这些套路之后的具体原理。希望大家在看完之后能够结合这些套路剖析自己的系统。

一般情况下,读请求延迟较大通常存在三种场景,分别为:

  1. 仅有某业务延迟较大,集群其他业务都正常
  2. 整个集群所有业务都反映延迟较大
  3. 某个业务起来之后集群其他部分业务延迟较大

这三种场景是表象,通常某业务反应延迟异常,首先需要明确具体是哪种场景, 然后针对性解决问题。下图是对读优化思路的一点总结,主要分为四个方面:客户端优化、服务器端优化、列族设计优化以及 HDFS 相关优化,下面每一个小点 都会按照场景分类,文章最后进行归纳总结。下面分别进行详细讲解:

_2019_01_10_1_32_04

1. HBase 客户端优化

和大多数系统一样,客户端作为业务读写的入口,姿势使用不正确通常会导致本业务读延迟较高实际上存在一些使用姿势的推荐用法,这里一般需要关注四个问题:

1.1 scan 缓存是否设置合理?
优化原理:在解释这个问题之前,首先需要解释什么是 scan 缓存,通常来讲一次 scan 会返回大量数据,因此客户端发起一次 scan 请求,实际并不会一次就将 所有数据加载到本地,而是分成多次 RPC 请求进行加载,这样设计一方面是因为大量数据请求可能会导致网络带宽严重消耗进而影响其他业务,另一方面也有 可能因为数据量太大导致本地客户端发生OOM。在这样的设计体系下用户会首 先加载一部分数据到本地,然后遍历处理,再加载下一部分数据到本地处理,如此往复,直至所有数据都加载完成。数据加载到本地就存放在 scan 缓存中,默 认 100 条数据大小。

通常情况下,默认的 scan 缓存设置就可以正常工作的。但是在一些大 scan(一 次 scan 可能需要查询几万甚至几十万行数据)来说,每次请求 100 条数据意味着一次 scan 需要几百甚至几千次 RPC 请求,这种交互的代价无疑是很大的。因此可以考虑将 scan 缓存设置增大,比如设为 500 或者 1000 就可能更加合适。 笔者之前做过一次试验,在一次 scan 扫描 10w+条数据量的条件下,将 scan 缓存从 100 增加到 1000,可以有效降低 scan 请求的总体延迟,延迟基本降低了 25% 左右。

优化建议:大 scan 场景下将 scan 缓存从 100 增大到 500 或者 1000,用以减少RPC 次数。

1.2 get 请求是否可以使用批量请求?

优化原理:HBase 分别提供了单条 get 以及批量 get 的 API 接口,使用批量 get 接口可以减少客户端到 RegionServer 之间的 RPC 连接数,提高读取性能。另外需要注意的是,批量 get 请求要么成功返回所有请求数据,要么抛出异常。

优化建议:使用批量 get 进行读取请求

1.3 请求是否可以显示指定列族或者列?

优化原理:HBase 是典型的列族数据库,意味着同一列族的数据存储在一起,不 同列族的数据分开存储在不同的目录下。如果一个表有多个列族,只是根据 Rowkey 而不指定列族进行检索的话不同列族的数据需要独立进行检索,性能必 然会比指定列族的查询差很多,很多情况下甚至会有 2 倍~3 倍的性能损失。

优化建议:可以指定列族或者列进行精确查找的尽量指定查找

1.4 离线批量读取请求是否设置禁止缓存?

优化原理:通常离线批量读取数据会进行一次性全表扫描,一方面数据量很大, 另一方面请求只会执行一次。这种场景下如果使用 scan 默认设置,就会将数据从 HDFS 加载出来之后放到缓存。可想而知,大量数据进入缓存必将其他实时业务热点数据挤出,其他业务不得不从 HDFS 加载,进而会造成明显的读延迟毛刺优化建议:离线批量读取请求设置禁用缓存,scan.setBlockCache(false)

2. HBase 服务器端优化

一般服务端端问题一旦导致业务读请求延迟较大的话,通常是集群级别的,即 整个集群的业务都会反映读延迟较大。可以从 4 个方面入手:

2.1 读请求是否均衡?

优化原理:极端情况下假如所有的读请求都落在一台 RegionServer 的某几个 Region 上,这一方面不能发挥整个集群的并发处理能力,另一方面势必造成此 台 RegionServer 资源严重消耗(比如 IO 耗尽、handler 耗尽等),落在该台 RegionServer 上的其他业务会因此受到很大的波及。可见,读请求不均衡不仅会 造成本身业务性能很差,还会严重影响其他业务。当然,写请求不均衡也会造成 类似的问题,可见负载不均衡是 HBase 的大忌。

观察确认:观察所有 RegionServer 的读请求 QPS 曲线,确认是否存在读请求不均衡现象

优化建议:RowKey 必须进行散列化处理(比如 MD5 散列),同时建表必须进行预分区处理

2.2 BlockCache 是否设置合理?

优化原理:BlockCache 作为读缓存,对于读性能来说至关重要。默认情况下 BlockCache 和 Memstore 的配置相对比较均衡(各占 40%),可以根据集群业务进行修正,比如读多写少业务可以将 BlockCache 占比调大。另一方面,BlockCache 的策略选择也很重要,不同策略对读性能来说影响并不是很大,但是对 GC 的影响却相当显著,尤其 BucketCache的offheap 模式下 GC 表现很优越。另外, HBase 2.0 对 offheap 的改造(HBASE-11425)将会使 HBase 的读性能得到 2~4 倍的提升,同时 GC 表现会更好!

观察确认:观察所有 RegionServer 的缓存未命中率、配置文件相关配置项一级 GC 日志,确认 BlockCache 是否可以优化

优化建议:JVM 内存配置量 < 20G,BlockCache 策略选择 LRUBlockCache;否 则选择 BucketCache 策略的 offheap 模式;期待 HBase 2.0 的到来!

2.3 HFile 文件是否太多?

优化原理:HBase 读取数据通常首先会到 Memstore 和 BlockCache 中检索(读取最近写入数据&热点数据),如果查找不到就会到文件中检索。HBase的类 LSM 结构会导致每个 store 包含多数 HFile 文件,文件越多,检索所需的 IO 次数必然越多,读取延迟也就越高。文件数量通常取决于 Compaction 的执行策略,一般和两个配置参数有 关 : hbase.hstore.compactionThreshold 和hbase.hstore.compaction.max.size,前者表示一个 store 中的文件数超过多少就应该进行合并,后者表示参数合并的文件大小最大是多少,超过此大小的文件不能参与合并。这两个参数不能设置太’松’(前者不能设置太大,后者不能设置太小),导致 Compaction 合并文件的实际效果不明显,进而很多文件得不到合并。 这样就会导致 HFile 文件数变多。

观察确认:观察 RegionServer 级别以及 Region 级别的 storefile 数,确认 HFile 文件是否过多优化建议:hbase.hstore.compactionThreshold 设置不能太大,默认是 3 个;设 置需要根据 Region 大小确定,通常可以简单的认为 hbase.hstore.compaction.max.size = RegionSize / hbase.hstore.compactionThreshold

2.4 Compaction 是否消耗系统资源过多?

优化原理:Compaction 是将小文件合并为大文件,提高后续业务随机读性能,但是也会带来 IO 放大以及带宽消耗问题(数据远程读取以及三副本写入都会消耗系统带宽)。正常配置情况下 Minor Compaction 并不会带来很大的系统资源消耗,除非因为配置不合理导致 Minor Compaction 太过频繁,或者 Region 设置 太大情况下发生 Major Compaction。

观察确认:观察系统 IO 资源以及带宽资源使用情况,再观察Compaction 队列 长度,确认是否由于 Compaction 导致系统资源消耗过多

优化建议:

  1. Minor Compaction 设置:hbase.hstore.compactionThreshold 设置不能太 小,又不能设置太大,因此建议设置为 5~6; hbase.hstore.compaction.max.size = RegionSize / hbase.hstore.compactionThreshold
  2. Major Compaction 设置:大 Region 读延迟敏感业务( 100G 以上)通常不 建议开启自动Major Compaction,手动低峰期触发。小Region或者延迟不 敏感业务可以开启 Major Compaction,但建议限制流量;
  3. 期待更多的优秀 Compaction 策略,类似于 stripe-compaction 尽早提供稳定服务

3. HBase 列族设计优化

HBase 列族设计对读性能影响也至关重要,其特点是只影响单个业务,并不会对整个集群产生太大影响。列族设计主要从两个方面检查:

3.1 Bloomfilter 是否设置?是否设置合理?

优化原理:Bloomfilter 主要用来过滤不存在待检索 RowKey 或者 Row-Col 的 HFile 文件,避免无用的 IO 操作。它会告诉你在这个 HFile 文件中是否可能存在待检 索的 KV,如果不存在,就可以不用消耗 IO 打开文件进行 seek。很显然,通过设置 Bloomfilter 可以提升随机读写的性能。

Bloomfilter 取值有两个,row 以及 rowcol,需要根据业务来确定具体使用哪种。 如果业务大多数随机查询仅仅使用 row 作为查询条件,Bloomfilter 一定要设置 为 row,否则如果大多数随机查询使用 row+cf 作为查询条件,Bloomfilter 需要 设置为 rowcol。如果不确定业务查询类型,设置为 row。

优化建议:任何业务都应该设置 Bloomfilter,通常设置为 row 就可以,除非 确认业务随机查询类型为 row+cf,可以设置为 rowcol

4. HDFS 相关优化

HDFS 作为 HBase 最终数据存储系统,通常会使用三副本策略存储 HBase 数据文 件以及日志文件。从 HDFS 的角度望上层看,HBase 即是它的客户端,HBase 通 过调用它的客户端进行数据读写操作,因此 HDFS 的相关优化也会影响 HBase 的 读写性能。这里主要关注如下三个方面:

4.1 Short-Circuit Local Read 功能是否开启?

优化原理:当前 HDFS 读取数据都需要经过 DataNode,客户端会向 DataNode 发送读取数据的请求,DataNode 接受到请求之后从硬盘中将文件读出来,再通 过 TPC 发送给客户端。Short Circuit 策略允许客户端绕过 DataNode 直接读取本 地数据。(具体原理参考引用[1])
优化建议:开启 Short Circuit Local Read 功能,具体配置参考引用[2]

4.2 Hedged Read 功能是否开启?

优化原理:HBase 数据在 HDFS 中一般都会存储三份,而且优先会通过 Short- Circuit Local Read 功能尝试本地读。但是在某些特殊情况下,有可能会出现因为磁盘问题或者网络问题引起的短时间本地读取失败,为了应对这类问题,社区开 发者提出了补偿重试机制 –HedgedRead。该机制基本工作原理为:客户端发起一个本地读,一旦一段时间之后还没有返回,客户端将会向其他 DataNode 发送相同数据的请求。哪一个请求先返回,另一个就会被丢弃。

优化建议:开启 Hedged Read 功能,具体配置参考引用[3]

4.3 数据本地率是否太低?

数据本地率:HDFS 数据通常存储三份,假如当前 RegionA 处于 Node1上,数据 a 写入的时候三副本为(Node1,Node2,Node3),数据 b 写入三副本是 (Node1,Node4,Node5),数据 c 写入三副本(Node1,Node3,Node5),可以看出来所有数据写入本地 Node1 肯定会写一份,数据都在本地可以读到,因此数据本 地率是 100%。现在假设 RegionA 被迁移到了 Node2 上,只有数据 a 在该节点上,其他数据(b 和 c)读取只能远程跨节点读,本地率就为 33%(假设 a,b 和 c 的数据大小相同)。

优化原理:数据本地率太低很显然会产生大量的跨网络 IO 请求,必然会导致读请求延迟较高,因此提高数据本地率可以有效优化随机读性能。数据本地率低 的原因一般是因为 Region 迁移(自动 balance 开启、RegionServer 宕机迁移、 手动迁移等),因此一方面可以通过避免 Region 无故迁移来保持数据本地率, 另一方面如果数据本地率很低,也可以通过执行 major_compact 提升数据本地率到 100%。

优化建议:避免 Region 无故迁移,比如关闭自动 balance、RS 宕机及时拉起并迁回飘走的 Region 等;在业务低峰期执行 major_compact 提升数据本地率

5. HBase 读性能优化归纳

在本文开始的时候提到读延迟较大无非三种常见的表象,单个业务慢、集群随机读慢以及某个业务随机读之后其他业务受到影响导致随机读延迟很大。了解完常见的可能导致读延迟较大的一些问题之后,我们将这些问题进行如下归类,读者可以在看到现象之后在对应的问题列表中进行具体定位:

_2019_01_10_1_49_40

结束语

性能优化是任何一个系统都会遇到的话题,每个系统也都有自己的优化方式。 HBase 作为分布式 KV 数据库,优化点又格外不同,更多得融入了分布式特性以 及存储系统优化特性。文中总结了读优化的基本突破点,有什么不对的地方还望指正,有补充的也可以一起探讨交流!

作者:范欣欣 网易杭州研究院后台技术中心 数据库技术组

【云栖快讯】云栖专辑 | 阿里开发者们的20个感悟,一通百通  详情请点击

网友评论