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
次数.
2. 批量get
优化原理:HBase
分别提供了单条get
以及批量get
的API
接口,使用批量get
接口可以减少客户端到RegionServer
之间的RPC
连接数,提高读取性能。另外需要注意的是,批量get
请求要么成功返回所有请求数据,要么抛出异常。
优化建议:使用批量get
进行读取请求
3. 显示指定列族或者列
优化原理:HBase
是典型的列族数据库,意味着同一列族的数据存储在一起,不同列族的数据分开存储在不同的目录下。如果一个表有多个列族,只是根据rowkey
而不指定列族进行检索的话不同列族的数据需要独立进行检索,性能必然会比指定列族的查询差很多,很多情况下甚至会有2倍~3倍
的性能损失。
优化建议:可以指定列族或者列进行精确查找的尽量指定查找
4. 离线批量读取时设置禁止缓存
优化原理:通常离线批量读取数据会进行一次性全表扫描,一方面数据量很大,另一方面请求只会执行一次。这种场景下如果使用scan
默认设置,就会将数据从HDFS
加载出来之后放到缓存。可想而知,大量数据进入缓存必将其他实时业务热点数据挤出,其他业务不得不从HDFS
加载,进而会造成明显的读延迟
优化建议:离线批量读取请求设置禁用缓存,scan.setBlockCache(false)
5. 读请求负载均衡
优化原理:极端情况下假如所有的读请求都落在一台RegionServer
的某几个Region
上,这一方面不能发挥整个集群的并发处理能力,另一方面势必造成此台RegionServer
资源严重消耗(比如I/O
耗尽、handler
耗尽等),落在该台RegionServer
上的其他业务会因此受到很大的波及。可见,读请求不均衡不仅会造成本身业务性能很差,还会严重影响其他业务。当然,写请求不均衡也会造成类似的问题,可见负载不均衡是HBase的大忌。
优化建议:rowkey
必须进行散列化处理(比如MD5
散列),同时建表必须进行预分区处理
6. 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 Memory < 20GB
,BlockCache
策略选择LRU
;否则选择BucketCache
策略的offheap
模式;
7. HFile文件数量
优化原理:HBase
读取数据通常首先会到Memstore
和BlockCache
中检索(读取最近写入数据和热点数据),如果查找不到就会到文件中检索。HBase的类LSM
结构会导致每个store
包含多数HFile
文件,文件越多,检索所需的I/O
次数必然越多,读取延迟也就越高。文件数量通常取决于Compaction
的执行策略,一般和两个配置参数有关:hbase.hstore.compaction.min
和hbase.hstore.compaction.max.size
,前者表示一个store
中的文件数超过多少就应该进行合并,后者表示参数合并的文件大小最大是多少,超过此大小的文件不能参与合并。这两个参数不能设置太’松’(前者不能设置太大,后者不能设置太小),导致Compaction
合并文件的实际效果不明显,进而很多文件得不到合并。这样就会导致HFile
文件数变多。观察RegionServer
级别以及Region
级别的storefile
数,确认HFile
文件是否过多。
优化建议:hbase.hstore.compaction.min
设置不能太大,默认是3个;设置需要根据Region
大小确定,通常可以简单的认为hbase.hstore.compaction.max.size = RegionSize / hbase.hstore.compaction.min
。
8. 控制Compaction消耗系统资源情况
优化原理:Compaction
是将小文件合并为大文件,提高后续业务随机读性能,但是也会带来I/O
放大以及带宽消耗问题(数据远程读取以及三副本写入都会消耗系统带宽)。正常配置情况下Minor Compaction
并不会带来很大的系统资源消耗,除非因为配置不合理导致Minor Compaction
太过频繁,或者Region
设置太大情况下发生Major Compaction
。观察系统I/O
资源以及带宽资源使用情况,再观察Compaction
队列长度,确认是否由于Compaction
导致系统资源消耗过多。
优化建议:Minor Compaction
设置:hbase.hstore.compaction.min
设置不能太小,又不能设置太大,因此建议设置为5~6
;hbase.hstore.compaction.max.size = RegionSize / hbase.hstore.compaction.min
; Major Compaction
设置:大Region
读延迟敏感业务( 100G以上)通常不建议开启自动Major Compaction
,手动低峰期触发。小Region
或者延迟不敏感业务可以开启Major Compaction
,但建议限制流量。
9. 调整数据本地率
数据本地率: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
的数据大小相同)。
优化原理:数据本地率太低很显然会产生大量的跨网络I/O
请求,必然会导致读请求延迟较高,因此提高数据本地率可以有效优化随机读性能。数据本地率低的原因一般是因为Region
迁移(自动balance
开启、RegionServer
宕机迁移、手动迁移等),因此一方面可以通过避免Region
无故迁移来保持数据本地率,另一方面如果数据本地率很低,也可以通过执行major_compact
提升数据本地率到100%。
优化建议:避免Region
无故迁移,比如关闭自动balance
、RegionServer
宕机及时拉起并迁回飘走的Region
等;在业务低峰期执行major_compact
提升数据本地率。