Labs 导读
HBase是一种分布式的、面向列的开源数据库,底层基于LSM树构建实现,通过顺序写操作,写性能大幅提升,读取时需要将内存中的数据和磁盘中的数据合并,牺牲了一部分读性能,适用于写多读少的场景。
Part 01、 读写流程
RegionServer是HBase系统中最核心的组件,主要负责用户数据写入、读取等基础操作,其内部结构如下所示:
图片
HBase通过Client连接RegionServer进行数据读写,一张表会被水平切分成多个Region,每个Region负责自己区域的数据读写请求。一个Region由多个Store组成,每个Store存放对应列簇的数据,比如一个表中有两个列簇,这个表的所有Region就都会包含两个Store。
HBase数据从RegionServer的读写位置,在HBase0.96版本后,只依赖于hbase:meta表,hbase:meta表存储了所有用户HRegion的位置信息,hbase:meta的一个rowkey就对应一个Region,meta表只有一个列簇info,每一行数据又分为4列,如下所示。
图片
每条MetaData的数据信息约1KB左右,假如HRegion设置为2GB,可以支持2^21个Region,最多可以支持存储4PB的数据,对于一般的集群来说已经够用。Meta表在整个HBase集群中只会存在一个,不会分裂split,位置在ZooKeeper的/hbase/meta-region-server节点上,在第一次读取后缓存在客户端。HBase读写优化,需要从RegionServer和Client端同时进行优化,分析读写流程,资源消耗,以及参数调优配置。
Part 02、 写性能优化
先简要分析下HBase数据写入的流程,如下图所示:
图片
当客户端发起一个Put请求时,首先它从hbase:meta表中查出该Put数据最终需要去的HRegionServer。然后客户端将Put请求发送给相应的HRegionServer,在HRegionServer中它首先会将该Put操作写入WAL日志文件中(Flush
到磁盘中)。
写完WAL日志文件后,HRegionServer根据Put中的TableName和RowKey找到对应的HRegion,并根据Column
Family找到对应的HStore,并将Put写入到该HStore的MemStore中。此时写成功,并返回通知客户端。
在MemStore
Flush过程中,还会在尾部追加一些meta数据,其中就包括Flush时最大的WAL
Sequence值,以告诉HBase这个StoreFile写入的最新数据的序列,那么在Recover时就直到从哪里开始。在HRegion启动时,这个sequence会被读取,并取最大的作为下一次更新时的起始Sequence。
在HBase数据写入的过程中,会遇到写性能较差、数据根本写入异常问题,可以考虑从下图的思路进行优化:
图片
2.1 客户端写优化
2.1.1 WAL写入优化
数据写入流程可以理解为一次顺序写WAL(Write
Ahead
Log)加上一次写缓存,如果业务上能够忍受小分部数据丢失,且需要极限提高写入速度,可以考虑禁用WAL,缺点就是系统crash的时候会丢一部分数据,且无法做跨集群的replication。WAL
的持久化分为四个等级:
● SKIP_WAL:只写缓存不写HLog日志,数据有丢失的风险。
● ASYNC_WAL:异步将数据写入HLog日志中。
● SYNC_WAL:同步将数据写入日志文件中,并没有真正落盘。
● FSYNC_WAL:同步将数据写入日志文件并强制落盘。
默认如果用户没有指定持久化等级,HBase使用SYNC_WAL等级持久化数据,可根据业务关注点,在WAL机制和写入吞吐量之间作出选择,用户可以通过客户端设置WAL持久化策略。除了在创建表的时候直接设置WAL存储级别,也可以通过客户端设置WAL持久化等级,put.setDurability(Durability.SYNC_WAL);
2.1.2 PUT批量优化
HBase分别提供了单条PUT以及批量PUT的API接口,使用批量PUT接口可以减少客户端到RegionServer之间的RPC连接数,提高写入吞吐量。如果对数据实时性要求不是特别严格,可以考虑开启异步批量提交,用户可以设置setAutoFlush(false),客户端缓存达到阈值(默认2M)后批量提交给RegionServer。
能够减少RPC调用的次数,将数据从client端提交给server端的任务交给HBase来处理,提高吞吐量;缺点是如果客户端异常,缓存数据可能丢失。
2.1.3 大KeyValue优化
KeyValue大小对写入性能影响巨大。如果太大,会对性能产生很大的影响,会导致集群写入忽然变慢、数据堆积,影响集群整体的业务。
RowKey的最大长度限制为64KB,但在实际应用中最多不会超过100B。这是由于HBase的Rowkey会按照rowkey+columnFamily+qualifier+timestamp组成的cell被多次冗余存储,RowKey越大,浪费的内存和硬盘资源也会越多。Value过大也会对性能产生很大的影响,也会影响到HBase的响应速度。一般有下面的2种解决方法:
● Value过大,建议拆成多列存储,每次返回需要的值或者将Value存储到HDFS上,在HBase中存储URL。
● 使用HBase2.0后的MOB特性,将Meta数据和MOB数据分开放到不同的文件中,存储文档、图片等二进制数据有极佳的性能。
2.1.4 Bulkload导入优化
对于离线导入数据的业务场景,可使用Bulkload导入。Bulkload是一个MapReduce程序,输出HFile文件,虽然实时性差,但是吞吐量大,效率高,可减少对HBase服务器压力,提升集群整体的性能。
2.2 服务端写优化
2.2.1 Region数量过少优化
通过业务的数据量大小预估Region分区,在建表时进行预分区,达到充分利用多server并行处理的能力。在预分区如果发现部分Region负载以及请求量不均匀,需要切分部分请求到负载高的Region,然后等待HBase集群进行负载均衡。如果想立刻解决,则可以使用命令将部分Region迁移到其他RegionServer节点上,以达到充分利用服务器资源,负载均衡。
2.2.2 写入请求均衡优化
检查RowKey设计以及预分区策略,保证写入请求均衡。针对Get查询为主的表,可以使用Hash预分区策略;针对Scan为主的表,可使用分段预分区的策略。
2.2.3 使用SSD存储WAL优化
影响写的性能就是WAL的写,SSD能很大的降低其响应时间,将WAL文件写到SSD上,对于写性能会有非常大的提升。使用HDFS
Archival
Storage机制,配置HDFS的部分文件目录为SSD介质。hbase.wal.storage.policy默认为none,用户可以指定ONESSD(WAL一个副本写入SSD介质)或者ALLSSD(WAL的三个副本全部写入SSD介质)。
Part 03、 读性能优化
HBase数据查询链路,相对写链路比较复杂,在HBase写数据时,相同的Cell(RowKey
/ColumnFamily /Column
相同)并不保证在一起,删除一个Cell也只是写入一个新的Cell,标记为Delete,需要从BlockCache、MemStore、StoreFile(HFile)中依次扫描,再将将结果合并即可(Merge
Read),流程如下图所示:
图片
其中StoreFile的扫描,先会使用Bloom
Filter过滤那些不可能符合条件的HFile,然后使用Block
Index快速定位Cell,并将其加载到BlockCache中,然后从BlockCache中读取。我们知道一个HStore可能存在多个StoreFile(HFile),此时需要扫瞄多个HFile,如果HFile过多会引起性能问题。在HBase数据查询的过程,会遇到数据查询过慢问题,可从下图思路进行优化:
图片
3.1客户端读优化
3.1.1 Get/Scan读请求优化
对于Get使用批量请求,HBase分别提供了单条Get以及批量Get的API接口,使用批量Get接口可以减少客户端到RegionServer之间的RPC连接数,提高读取吞吐量。
对于Scan设置客户端缓存,在HBase总RPC次数调整到比较合理的前提下,可以考虑将大Scan场景下将Scan缓存从100增大到500或者1000,用以减少RPC次数。
3.1.2指定列簇或列优化
在客户端查询时尽量指定列簇或者列进行精确查询,过滤不必要的列族或者列,减少Region的数据查询和网络数据传输。
3.1.3离线读禁止缓存优化
离线批量读取请求设置禁用缓存,scan.setCacheBlocks(false),适用于离线的全表扫秒,如MapReduce,此时使用缓存不仅无法提升性能,可能还会适得其反。
3.1.4布隆过滤器使用优化
在任何业务都应该设置布隆过滤器,用空间换取时间。默认设置为row,除非确定业务随机查询类型为row+column,这是布隆过滤器设置为rowcol(适合大量指定column的场景,这种场景下占用的缓存内存以及磁盘的量会增加)。
3.2服务端读优化
3.2.1读请求负载均衡优化
对于数据吞吐量较大,且一次查询返回的数据量较大的场景,则Rowkey必须进行散列化处理,同时建表必须进行预分区处理。针对Get查询为主的表,可以使用Hash预分区策略,表数据均匀分布;针对Scan扫描为主的表,可使用分段预分区的策略,在兼顾业务场景的情况下,设计Rowkey,在满足查询需求的前提下尽量对数据打散并进行负载均衡。
3.2.2 BlockCache缓存优化
如果JVM内存配置量小于20G,BlockCache策略选择LRUBlockCache;否则选择
BucketCache策略的offhea模式。如果是offheap模式,也可以根据业务场景的读写比例来配置堆中读写heap的比例,默认堆中读写缓存均占heap的40%,即读写均衡。
3.2.3 HFile数量控制优化
一个Store中包含多个HFile文件,文件越多检索所需的IO次数越多,读取延迟也越高。文件数量通常取决于minorCompaction的执行策略,一般和两个配置参数有关hbase.hstore.compactionThreshold
和
hbase.hstore.compaction.max.size,前者表示一个store中的文件数超过阈值就应该进行合并;后者表示参与合并的文件大小最大是多少,超过此大小的文件不能参与合并。可以查看RegionServer级别以及Region级别的HFile数量,确认HFile文件是否过多。hbase.hstore.compactionThreshold设置不能太大,默认为3个。
3.2.4 MajorCompaction优化
MajorCompaction可将一个Store下的所有文件合并成一个,并在合并的过程中将修改和删除的数据一共处理完成,释放硬盘资源。由于配置文件中默认的MajorCompaction是定时按表执行,且消耗资源很大,对系统性能影响同样很大,所以对于读取延迟以及系统性能波动敏感的业务通常不建议开启自动MajorCompaction,而是利用脚本定时或者手动在业务低峰期触发;对于延迟不敏感的业务可以开启自动MajorCompaction,但是建议限制流量。
3.2.5 数据本地化
Hbase的数据在写的时候是本地化,但是当Region被迁移的时候,数据可能就不再满足本地化性了,直到完成Compaction,才能恢复数据本地化。尽量避免Region无故迁移。对于本地化率较低的节点,可以在业务低峰期执行MajorCompaction。
Part 04、 总结
HBase读写的优化,需要考虑业务的使用场景,预先评估好集群的合理规模,对读写的场景进行资源消耗量化分析,从而更好的进行RowKey设计,对Region的预分区,尽量将数据分散到各个Region上,保证读写请求均衡,降低Compactiont对业务的影响,降低数据存储容量和网络资源消耗。在HBase众多配置参数中,选择合理的配置组合,达到读写的最优配置。