一、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年最弱小数据面试宝典,五万字面试八股文!