一个系统上线以后,开发和调优将会一直伴随在系统的整个生命周期中,HBase也不例外。今天咱们要学习如何进行HBase读写性能调优,以获取最大的读写效率。算法
采用批量写,能够减小客户端到RegionServer之间的RPC的次数,提升写入性能。批量写请求要么所有成功返回,要么抛出异常。数据库
HTable.put(List<Put>);复制代码
若是业务能够接受异常状况下丢失少许数据,可使用异步批量提交方式提交请求。缓存
用户提交写请求以后,数据会先写入客户端缓存,并返回用户写入成功(此时数据并为提交到RegionServer),当客户端缓存达到阈值(默认2M,可经过hbase.client.write.buffer
配置)时才会批量提交给RegionServer。须要注意的是,在某些状况下客户端异常的状况下缓存数据有可能丢失。性能优化
HTable.setWriteBufferSize(writeBufferSize); // 设置缓存大小HTable.setAutoFlush(false);复制代码
客户端开启多个HTable写线程,每一个写线程负责一个HTable对象的flush操做,这样结合定时flush和写buffer,能够即保证在数据量小的时候,数据能够在较短期内被flush,同时又保证在数据量大的时候,写buffer一满就即便进行flush。bash
在HBase中数据都是以HFile形式保存在HDFS中的,当有大量数据须要写入到HBase的时候,能够采用BulkLoad方式完成。服务器
使用MapReduce或者Spark直接生成HFile格式的数据文件,而后再经过RegionServer将HFile数据文件移动到相应的Region上去。网络
配置数据的压缩算法,这里的压缩是HFile中block级别的压缩。对于能够压缩的数据,配置压缩算法能够有效减小磁盘的IO,从而达到提升性能的目的。可是并非全部数据均可以进行有效压缩,如图片,由于图片通常是已经压缩后的数据,因此压缩效果有限。经常使用的压缩算法是SNAPPY,由于它有较好的压缩和解压速度和能够接受的压缩率。多线程
配置表的数据优先缓存在内存中,这样能够有效提高读取的性能。适合小表,并且须要频繁进行读取操做的。并发
在HBase中数据是分布在各个Region中的,每一个Region都负责一个起始RowKey和结束Rowkey的范围,在向HBase中写数据的时候,会根据RowKey请求到对应的Region上,若是写请求都集中在某一个Region或某几个Region上的时候,性能确定不如写请求均匀分布在各个Region上好。默认状况下,建立的HBase的只有一个Region分区,会随着数据量的变大,进行split,拆分红多个Region,最开始的性能确定会很很差异步
建议在设计HBase的的时候,进行预分区,并设计一个良好的Rowkey生成规则(关于RowKey设计,能够参考《一篇文章带你快速搞懂HBase RowKey设计》),尽可能将数据分散到各个Region上,那样在进行HBase的读写的时候,对性能会有很好的改善。
数据在写入HBase的时候,先写WAL,再写入缓存。一般状况下写缓存延迟很低,WAL机制一方面是为了确保数据即便写入缓存后数据丢失也能够经过WAL恢复,另外一方面是为了集群之间的复制。默认WAL机制是开启的,而且使用的是同步机制写WAL。
若是业务不特别关心异常状况下部分数据的丢失,而更关心数据写入吞吐量,可考虑关闭WAL写,这样能够提高2~3倍数据写入的吞吐量。
若是业务不能接受不写WAL,可是能够接受WAL异步写入,这样能够带了1~2倍性能提高。
HBase中能够经过设置WAL的持久化等级决定是否开启WAL机制、以及HLog的落盘方式。
WAL的持久化等级分为以下四个等级:
SKIP_WAL:只写缓存,不写HLog日志。这种方式由于只写内存,所以能够极大的提高写入性能,可是数据有丢失的风险。在实际应用过程当中并不建议设置此等级,除非确认不要求数据的可靠性。
ASYNC_WAL:异步将数据写入HLog日志中。
SYNC_WAL:同步将数据写入日志文件中,须要注意的是数据只是被写入文件系统中,并无真正落盘,默认。
FSYNC_WAL:同步将数据写入日志文件并强制落盘。最严格的日志写入等级,能够保证数据不会丢失,可是性能相对比较差。
一样,除了在建立表的时候直接设置WAL存储级别,也能够经过客户端设置WAL持久化等级,代码:
put.setDurability(Durability.SYNC_WAL);复制代码
使用批量请求,能够减小RPC的次数,显著提升吞吐量。须要注意的是,批量get请求要么成功返回全部请求数据,要么抛出异常。
Result[] re= table.get(List<Get> gets);复制代码
一次scan可能会返回大量数据,可是实际客户端发起一次scan请求,并不会将全部数据一次性加载到本地,而是分红屡次RPC请求进行加载,这样设计一方面是由于大量数据请求可能会致使网络带宽严重消耗进而影响其余业务,另外一方面是有可能由于数据量太大致使客户端发生OOM。因此采用先加载一部分数据到本地,而后进行遍历,每次加载一部分数据,如此往复,直至全部数据加载完成。数据加载到本地就存放在scan缓存中,默认100。
增大scan的缓存,可让客户端减小一次scan的RPC次数,从而从总体上提高数据读取的效率。
scan.setCaching(int caching); //大scan能够设置为1000复制代码
HBase是列族数据库,同一列族的数据存储在一块,不一样列族是分开存储的,若是一个表由多个列族,只是根据RowKey而不指定列族进行检索的话,不一样列族的数据须要独立进行检索,性能必然会比指定列族的查询差的多。
此外指定请求的列的话,不须要将整个列族的全部列的数据返回,这样就减小了网路IO。
scan.addColumn();复制代码
在只须要Rowkey数据时,能够为Scan添加一个只读取Rowkey的filter(FirstKeyOnlyFilter
或KeyOnlyFilter
)。
在使用table.getScanner
以后,记得关闭,不然它会和服务器端一直保持链接,资源没法释放,从而致使服务端的某些资源不可用。
scanner.close();复制代码
当离线访问HBase时,每每会对HBase表进行扫描,此时读取的数据没有必要存放在BlockCache
中,不然会下降扫描的效率。
scan.setBlockCache(false);复制代码
建议在对HBase表进行扫描时禁用缓存。
对于频繁查询HBase的应用场景不须要禁用缓存,而且能够考虑在应用程序和HBase之间加一层缓存系统(如Redis),先查询缓存,缓存没有命中再去查询HBase。
同
配置HFile中block块的大小,不一样的block大小,能够影响HBase读写数据的效率。越大的block块,配置压缩算法,压缩的效率就越好;可是因为HBase的读取数据时以block块为单位的,因此越大的block块,对于随机读的状况,性能可能会比较差,若是要提高写入的性能,通常扩大到128kb或者256kb,能够提高写数据的效率,也不会影响太大的随机读性能。
配置HFile中block块的编码方法。当一行数据中存在多个列时,通常能够配置为"FAST_DIFF",能够有效的节省数据存储的空间,从而提高性能。
优化原理:BloomFilter主要用来过滤不存在待检索RowKey或者Row-Col的HFile文件,避免无用的IO操做。它会告诉你在这个HFile文件中是否可能存在待检索的KeyValue,若是不存在,就能够不用小号IO打开文件进行seek。经过设置BloomFilter能够提高读写的性能。
BloomFilter是一个列族级别的配置属性,若是列族设置了BloomFilter,那么HBase会在生成StoreFile时包含一份BloomFilter的结构的数据,称为MetaBlock(一旦写入就没法更新)。MetaBlock和DataBlock(真实的KeyValue数据)一块儿由LRUBlockCache维护,因此开启了BloomFilter会有必定的存储即内存cache开销。
HBase利用BloomFilter能够节省必须读磁盘过程,能够提升随机读(get)的性能,可是对于顺序读(scan)而言,设置BloomFilter是没有做用的(0.92版本之后,若是设置了BloomFilter为ROWCOL
,对于执行了qualifier的scan有必定的优化)
BloomFilter取值有两个,ROW和ROWCOL,须要根据业务来肯定具体使用哪一种。
若是业务大多数随机查询仅仅使用row做为查询条件,BloomFilter必定要设置为ROW。
若是大多数随机查询使用row+col做为查询条件,BloomFilter须要设置为ROWCOL。
若是不肯定业务查询类型,设置为ROW。‘
同
HBase是利用内存完成读写操做。提升HBase内存能够有效提升HBase性能。GC_OPTS主要须要调整HeapSize和NewSize的大小。调整HeapSize大小的时候,建议将Xms和Xmx设置成相同的值,这样能够避免JVM动态调整HeapSize大小的时候影响性能。调整NewSize大小的时候,建议把其设置为HeapSize大小的1/9。
当HBase集群规模越大,Region数量越多时,能够适当调大HMaster的GC_OPTS参数
RegionServer须要比HMaster更大的内存,在内存充足的状况下,HeapSize能够相对设置大一些。
HMaster的HeapSize为4G的时候,HBase集群能够支持100000个Region的规模。根据经验值,单个RegionServer的HeapSize不建议超过20GB。
# HMaster、RegionServer GC_OPTS配置以下:HMaster: -Xms2G -Xmx2G -XX:NewSize=256M -XX:MaxNewSize=256M RegionServer: -Xms4G -Xmx4G -XX:NewSize=512M -XX:MaxNewSize=512M复制代码
hbase.regionserver.handler.count
表示RegionServer在同一时刻可以并发处理多少请求。若是设置太高会致使激烈的线程竞争,若是设置太小,请求将会在RegionServer长时间等待,下降处理能力。应该根据资源状况,适当增长处理线程数。
建议根据CPU的使用状况,能够设置为100至300之间的值。
hbase.hregion.memstore.flush.size
默认值128M,单位字节,一旦有MemStore超过该值将被flush,若是regionserver的jvm内存比较充足(16G以上),能够调整为256M。在内存足够put负载大状况下能够调整增大。
BlockCache做为读缓存,合理设置对于提升读性能很是重要。默认状况下,BlockCache和MemStore的配置各占40%,能够根据集群业务进行修正,好比读多写少业务能够将BlockCache占比调大。另外BlockCache的策略也很重要,不一样策略对读性能来讲影响并不大,可是对GC的影响 却很显著。
HBase缓存区大小,主要影响查询性能。根据查询模式以及查询记录分布状况来决定缓存区的大小。若是采用随机查询使得缓存区的命中率较低,能够适当下降缓存大小。
hfile.block.cache.size,默认0.4,用来提升读性能hbase.regionserver.global.memstore.size,默认0.4,用来提升写性能复制代码
MemStore在flush以前,会进行StoreFile的文件数量校验(经过hbase.hstore.blockingStoreFiles
参数配置),若是大于设定值,系统将会强制执行Compaction操做进行文件合并,在合并的过程当中会阻塞MemStore的数据写入,等待其余线程将StoreFile进行合并。一般状况下发生在数据写入很快的状况下。
hbase.hstore.compactionThreshold
表示启动Compaction的最低阈值,该值不能太大,不然会积累太多文件,通常建议设置为5~8左右。
hbase.hstore.blockingStoreFiles
默认设置为7,能够适当调大一些。
hbase.hregion.max.filesize
表示HBase中Region的文件总大小的最大值。当Region中的文件大于该参数时,将会致使Region分裂。
若是该参数设置太小时,可能会致使Split操做频繁
若是该参数设置过大时,会致使Compaction操做须要处理的文件个数增大,影响Compaction执行效率
hbase.hstore.compaction.min
当一个Store中文件超过该值时,会进行Compaction,适当增大该值,能够减小文件被重复执行Compaction。可是若是过大,会致使Store中文件数过多而影响读取的性能。
hbase.hstore.compaction.max
控制一次Compaction操做时的文件数据量的最大值。
hbase.hstore.compaction.max.size
若是一个HFile文件的大小大于该值,那么在Minor Compaction操做中不会选择这个文件进行Compaction操做,除非进行Major Compaction操做。这个值能够防止较大的HFile参与Compaction操做。在禁止Major Compaction后,一个Store中可能存在几个HFile,而不会合并成为一个HFile,这样不会对数据读取形成太大的性能影响。
原则是:尽可能要减少Compaction的次数和Compaction的执行时间
在HBase使用过程当中,要想获取好的读写性能,能够从如下几个方面进行优化:
一个优秀的HBase表列族设置方案,能够参考《带你快速上手HBase | HBase列族优化》
一个优秀的HBase RowKey设计方案,能够参考《一篇文章带你快速搞懂HBase RowKey设计》
读写时客户端相关配置
HBase服务器优化