关于hbase:万字长文详解HBase读写性能优化

10次阅读

共计 9605 个字符,预计需要花费 25 分钟才能阅读完成。

一、HBase 读优化

1. HBase 客户端优化

和大多数零碎一样,客户端作为业务读写的入口,姿态应用不正确通常会导致本业务读提早较高实际上存在一些应用姿态的举荐用法,这里个别须要关注四个问题:

1) scan 缓存是否设置正当?

优化原理:在解释这个问题之前,首先须要解释什么是 scan 缓存,通常来讲一次 scan 会返回大量数据,因而客户端发动一次 scan 申请,理论并不会一次就将所有数据加载到本地,而是分成屡次 RPC 申请进行加载,这样设计一方面是因为大量数据申请可能会导致网络带宽重大耗费进而影响其余业务,另一方面也有可能因为数据量太大导致本地客户端产生 OOM。在这样的设计体系下用户会首先加载一部分数据到本地,而后遍历解决,再加载下一部分数据到本地解决,如此往返,直至所有数据都加载实现。数据加载到本地就寄存在 scan 缓存中,默认 100 条数据大小。

本文参考 HBase 保姆级教程:HBase 常识体系保姆级教程,五万字好文!

通常状况下,默认的 scan 缓存设置就能够失常工作的。然而在一些大 scan(一次 scan 可能须要查问几万甚至几十万行数据)来说,每次申请 100 条数据意味着一次 scan 须要几百甚至几千次 RPC 申请,这种交互的代价无疑是很大的。因而能够思考将 scan 缓存设置增大,比方设为 500 或者 1000 就可能更加适合。笔者之前做过一次试验,在一次 scan 扫描 10w+ 条数据量的条件下,将 scan 缓存从 100 减少到 1000,能够无效升高 scan 申请的总体提早,提早根本升高了 25% 左右。

优化倡议:大 scan 场景下将 scan 缓存从 100 增大到 500 或者 1000,用以缩小 RPC 次数

2) get 申请是否能够应用批量申请?

优化原理:HBase 别离提供了单条 get 以及批量 get 的 API 接口,应用批量 get 接口能够缩小客户端到 RegionServer 之间的 RPC 连接数,进步读取性能。另外须要留神的是,批量 get 申请要么胜利返回所有申请数据,要么抛出异样。

优化倡议:应用批量 get 进行读取申请

3) 申请是否能够显示指定列族或者列?

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

优化倡议:能够指定列族或者列进行准确查找的尽量指定查找

4) 离线批量读取申请是否设置禁止缓存?

优化原理:通常离线批量读取数据会进行一次性全表扫描,一方面数据量很大,另一方面申请只会执行一次。这种场景下如果应用 scan 默认设置,就会将数据从 HDFS 加载进去之后放到缓存。可想而知,大量数据进入缓存必将其余实时业务热点数据挤出,其余业务不得不从 HDFS 加载,进而会造成显著的读提早毛刺

优化倡议:离线批量读取申请设置禁用缓存,scan.setBlockCache(false)

2. HBase 服务器端优化

个别服务端端问题一旦导致业务读申请提早较大的话,通常是集群级别的,即整个集群的业务都会反映读提早较大。能够从 4 个方面动手:

1) 读申请是否平衡?

优化原理:极其状况下如果所有的读申请都落在一台 RegionServer 的某几个 Region 上,这一方面不能施展整个集群的并发解决能力,另一方面势必造成此台 RegionServer 资源重大耗费(比方 IO 耗尽、handler 耗尽等),落在该台 RegionServer 上的其余业务会因而受到很大的波及。可见,读申请不平衡不仅会造成自身业务性能很差,还会重大影响其余业务。当然,写申请不平衡也会造成相似的问题,可见负载不平衡是 HBase 的大忌。

察看确认:察看所有 RegionServer 的读申请 QPS 曲线,确认是否存在读申请不平衡景象

优化倡议:RowKey 必须进行散列化解决(比方 MD5 散列),同时建表必须进行预分区解决

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 的到来!

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

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 列族设计对读性能影响也至关重要,其特点是只影响单个业务,并不会对整个集群产生太大影响。列族设计次要从以下方面查看:

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 的读写性能。这里次要关注如下三个方面:

1) Short-Circuit Local Read 性能是否开启?

优化原理:以后 HDFS 读取数据都须要通过 DataNode,客户端会向 DataNode 发送读取数据的申请,DataNode 承受到申请之后从硬盘中将文件读出来,再通过 TPC 发送给客户端。Short Circuit 策略容许客户端绕过 DataNode 间接读取本地数据。(具体原理参考此处)

优化倡议:开启 Short Circuit Local Read 性能,具体配置戳这里

2) Hedged Read 性能是否开启?

优化原理:HBase 数据在 HDFS 中个别都会存储三份,而且优先会通过 Short-Circuit Local Read 性能尝试本地读。然而在某些非凡状况下,有可能会呈现因为磁盘问题或者网络问题引起的短时间本地读取失败,为了应答这类问题,社区开发者提出了弥补重试机制 – Hedged Read。该机制根本工作原理为:客户端发动一个本地读,一旦一段时间之后还没有返回,客户端将会向其余 DataNode 发送雷同数据的申请。哪一个申请先返回,另一个就会被抛弃。

优化倡议:开启 Hedged Read 性能,具体配置参考这里

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 读性能优化演绎

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


二、HBase 写优化

和读相比,HBase 写数据流程倒是显得很简略:数据先程序写入 HLog,再写入对应的缓存 Memstore,当 Memstore 中数据大小达到肯定阈值(128M)之后,零碎会异步将 Memstore 中数据 flush 到 HDFS 造成小文件。

HBase 数据写入通常会遇到两类问题,一类是写性能较差,另一类是数据基本写不进去。这两类问题的切入点也不尽相同,如下图所示:

1. 写性能优化切入点

1) 是否须要写 WAL?WAL 是否须要同步写入?

优化原理:数据写入流程能够了解为一次程序写 WAL+ 一次写缓存,通常状况下写缓存提早很低,因而晋升写性能就只能从 WAL 动手。WAL 机制一方面是为了确保数据即便写入缓存失落也能够复原,另一方面是为了集群之间异步复制。默认 WAL 机制开启且应用同步机制写入 WAL。首先思考业务是否须要写 WAL,通常状况下大多数业务都会开启 WAL 机制(默认),然而对于局部业务可能并不特地关怀异常情况下局部数据的失落,而更关怀数据写入吞吐量,比方某些举荐业务,这类业务即便失落一部分用户行为数据可能对举荐后果并不形成很大影响,然而对于写入吞吐量要求很高,不能造成数据队列阻塞。这种场景下能够思考敞开 WAL 写入,写入吞吐量能够晋升 2x~3x。退而求其次,有些业务不能承受不写 WAL,但能够承受 WAL 异步写入,也是能够思考优化的,通常也会带来 1x~2x 的性能晋升。

优化举荐:依据业务关注点在 WAL 机制与写入吞吐量之间做出抉择

其余留神点:对于应用 Increment 操作的业务,WAL 能够设置敞开,也能够设置异步写入,办法同 Put 相似。置信大多数 Increment 操作业务对 WAL 可能都不是那么敏感~

2) Put 是否能够同步批量提交?

优化原理:HBase 别离提供了单条 put 以及批量 put 的 API 接口,应用批量 put 接口能够缩小客户端到 RegionServer 之间的 RPC 连接数,进步写入性能。另外须要留神的是,批量 put 申请要么全副胜利返回,要么抛出异样。

优化倡议:应用批量 put 进行写入申请

3) Put 是否能够异步批量提交?

优化原理:业务如果能够承受异常情况下大量数据失落的话,还能够应用异步批量提交的形式提交申请。提交分为两阶段执行:用户提交写申请之后,数据会写入客户端缓存,并返回用户写入胜利;当客户端缓存达到阈值(默认 2M)之后批量提交给 RegionServer。须要留神的是,在某些状况下客户端异样的状况下缓存数据有可能失落。

优化倡议:在业务能够承受的状况下开启异步批量提交

应用形式:setAutoFlush(false)

4) Region 是否太少?

优化原理:以后集群中表的 Region 个数如果小于 RegionServer 个数,即 Num(Region of Table) < Num(RegionServer),能够思考切分 Region 并尽可能散布到不同 RegionServer 来进步零碎申请并发度,如果 Num(Region of Table) > Num(RegionServer),再减少 Region 个数成果并不显著。

优化倡议 :在 Num(Region of Table) < Num(RegionServer) 的场景下切分局部申请负载高的 Region 并迁徙到其余 RegionServer;

5) 写入申请是否不平衡?

优化原理:另一个须要思考的问题是写入申请是否平衡,如果不平衡,一方面会导致系统并发度较低,另一方面也有可能造成局部节点负载很高,进而影响其余业务。分布式系统中特地胆怯一个节点负载很高的状况,一个节点负载很高可能会拖慢整个集群,这是因为很多业务会应用 Mutli 批量提交读写申请,一旦其中一部分申请落到该节点无奈失去及时响应,就会导致整个批量申请超时。因而不怕节点宕掉,就怕节点气息奄奄!

优化倡议:查看 RowKey 设计以及预分区策略,保障写入申请平衡。

6) 写入 KeyValue 数据是否太大?

KeyValue 大小对写入性能的影响微小,一旦遇到写入性能比拟差的状况,须要思考是否因为写入 KeyValue 数据太大导致。KeyValue 大小对写入性能影响曲线图如下:

图中横坐标是写入的一行数据(每行数据 10 列)大小,左纵坐标是写入吞吐量,右坐标是写入均匀提早(ms)。能够看出随着单行数据大小一直变大,写入吞吐量急剧下降,写入提早在 100K 之后急剧增大。

说到这里,有必要和大家分享两起在生产线环境因为业务 KeyValue 较大导致的重大问题,一起是因为大字段业务写入导致其余业务吞吐量急剧下降,另一起是因为大字段业务 scan 导致 RegionServer 宕机。

案件一:大字段写入导致其余业务吞吐量急剧下降

局部业务反馈集群写入突然变慢、数据开始沉积的状况,查看集群表级别的数据读写 QPS 监控,发现问题的第一个关键点:业务 A 开始写入之后整个集群其余局部业务写入 QPS 都简直断崖式上涨,初步狐疑黑手就是业务 A。

下图是过后业务 A 的写入 QPS(预先发现脑残忘了截取其余表 QPS 断崖式上涨的惨象),然而第一感觉是 QPS 并不高啊,凭什么去影响他人!


于是就持续查看其余监控信息,首先确认系统资源(次要是 IO)并没有达到瓶颈,其次确认了写入的均衡性,直至看到下图,才追踪到影响其余业务写入的第二个关键点:RegionServer 的 handler(配置 150)被残忍耗尽:


比照下面两张图,是不是发现出奇的统一,那就能够根本确认是因为该业务写入导致这台 RegionServer 的 handler 被耗尽,进而其余业务拿不到 handler,天然写不进去。那问题来了,为什么会这样?失常状况下 handler 在解决完客户端申请之后会立马开释,惟一的解释是这些申请的提早切实太大。

试想,咱们去汉堡店排队买汉堡,有 150 个窗口服务,失常状况下大家买一个很快,这样 150 个窗口可能只须要 50 个服务。假如突然来了一批大汉,要定制超大汉堡,好了,所有的窗口都工作起来,而且因为大汉堡不好制作导致服务很慢,这样必然会导致其余排队的用户长时间期待,直至超时。

可回头一想这可是写申请啊,怎么会有这么大的申请提早!和业务方沟通之后确认该表次要存储语料库文档信息,都是均匀 100K 左右的数据,是不是曾经猜到了后果,没错,就是因为这个业务 KeyValue 太大导致。KeyValue 太大会导致 HLog 文件写入频繁切换、flush 以及 compaction 频繁触发,写入性能急剧下降。

目前针对这种较大 KeyValue 写入性能较差的问题还没有间接的解决方案,好在社区曾经意识到这个问题,在接下来行将公布的下一个大版本 HBase 2.0.0 版本会针对该问题进行深刻优化,详见 HBase MOB,优化后用户应用 HBase 存储文档、图片等二进制数据都会有极佳的性能体验。

案件二:大字段 scan 导致 RegionServer 宕机

案件现场:有段时间有个 0.98 集群的 RegionServer 常常频繁宕机,查看日志是因为”java.lang.OutOfMemoryError: Requested array size exceeds VM limit”,如下图所示:

起因剖析:通过查看源码以及相干文档,确认该异样产生在 scan 后果数据回传给客户端时因为数据量太大导致申请的 array 大小超过 JVM 规定的最大值(Interge.Max_Value-2)。造成该异样的两种最常见起因别离是:

  • 表列太宽(几十万列或者上百万列),并且 scan 返回没有对列数量做任何限度,导致一行数据就可能因为蕴含大量列而数据超过 array 大小阈值
  • KeyValue 太大,并且 scan 返回没有对返回后果大小做任何限度,导致返回数据后果大小超过 array 大小阈值

有的童鞋就要发问啦,说如果曾经对返回后果大小做了限度,在表列太宽的状况下是不是就能够不对列数量做限度呢。这里须要廓清一下,如果不对列数据做限度,数据总是一行一行返回的,即便一行数据大小大于设置的返回后果限度大小,也会返回残缺的一行数据。在这种状况下,如果这一行数据曾经超过 array 大小阈值,也会触发 OOM 异样。

解决方案:目前针对该异样有两种解决方案,其一是降级集群到 1.0,问题都解决了。其二是要求客户端拜访的时候对返回后果大小做限度(scan.setMaxResultSize(210241024))、并且对列数量做限度(scan.setBatch(100)),当然,0.98.13 版本当前也能够对返回后果大小在服务器端进行限度,设置参数 hbase.server.scanner.max.result.size 即可

2. 写异样问题检查点

上述几点次要针对写性能优化进行了介绍,除此之外,在一些状况下还会呈现写异样,一旦产生须要思考上面两种状况(GC 引起的不做介绍):

Memstore 设置是否会触发 Region 级别或者 RegionServer 级别 flush 操作?

问题解析:以 RegionServer 级别 flush 进行解析,HBase 设定一旦整个 RegionServer 上所有 Memstore 占用内存大小总和大于配置文件中 upperlimit 时,零碎就会执行 RegionServer 级别 flush,flush 算法会首先依照 Region 大小进行排序,再依照该程序顺次进行 flush,直至总 Memstore 大小低至 lowerlimit。这种 flush 通常会 block 较长时间,在日志中会发现“Memstore is above high water mark and block 7452 ms”,示意这次 flush 将会阻塞 7s 左右。

问题检查点

  • Region 规模与 Memstore 总大小设置是否正当?如果 RegionServer 上 Region 较多,而 Memstore 总大小设置的很小(JVM 设置较小或者 upper.limit 设置较小),就会触发 RegionServer 级别 flush。集群布局相干内容能够参考文章《HBase 最佳实际-集群布局》
  • 列族是否设置过多,通常状况下表列族倡议设置在 1~3 个之间,最好一个。如果设置过多,会导致一个 Region 中蕴含很多 Memstore,导致更容易触到高水位 upperlimit

Store 中 HFile 数量是否大于配置参数 blockingStoreFile?

问题解析:对于数据写入很快的集群,还须要特地关注一个参数:hbase.hstore.blockingStoreFiles,此参数示意如果以后 hstore 中文件数大于该值,零碎将会强制执行 compaction 操作进行文件合并,合并的过程会阻塞整个 hstore 的写入。通常状况下该场景产生在数据写入很快的状况下,在日志中能够发现”Waited 3722ms on a compaction to clean up‘too many store files“

问题检查点

  • 参数设置是否正当?hbase.hstore.compactionThreshold 示意启动 compaction 的最低阈值,该值不能太大,否则会积攒太多文件,个别倡议设置为 5~8 左右。hbase.hstore.blockingStoreFiles 默认设置为 7,能够适当调大一些。

参考

HBase 常识体系保姆级教程,五万字好文!

2022 年最弱小数据面试宝典,五万字面试八股文!

正文完
 0