版权声明:本文为博主原创文章,未经博主允许不得转载。//www.greatytc.com/p/27c2137fdfee
一、通过预分区的方式建表
在建表时对HBase进行region的预分区,这样就能避免热点,提高数据写入效率。比如,rowkey的前几位字符串都是从0001~0010的数字,这样就可以分成10个region,使用SPLISTS,如下:
create't1','f1',{SPLITS=>['0001','0002','0003','0004','0005','0006','0007','0008','0009']}
二、列族数量不应超过2个
一个StoreFile对应一个ColumnFamily,多个ColumnFamily那么就有多个StoreFile。由于不同的列族会共享region,所以有可能出现,一个列族A已经有1000万行,而另外一个B才100行。当一个要求region分割的时候,会导致100行的列族B会同样分布到多个region中,导致扫描列族B的性能低下。
三、 隆过滤器
减少特定访问模式下的查询时间,建议使用ROW模式。
NONE:不使用布隆过滤器
ROW:行级,行键使用布隆过滤器——其在额外空间开销和提升性能之间有很好平衡
ROWCOL:行加列级,列键也使用布隆过滤器,不建议开启。
BLOOMFILTER=>‘ROW’
四、BlockCache设置
BlockCache作为读缓存,对于对性能来说至关重要,默认情况下BlockCache和Memstore的配置相对均衡(各占40%),可以根据集群业务进行调整,比如读多写少业务可以将BlockCache比例调大,反之亦然。
缓存策略:BlockCache的不同策略虽然对读性能无太多影响,但对GC影响很大。默认为LRUBlockCache,但由于该机制完全基于JVM Heap的缓存,有发生FGC的风险,因此有了基于堆外内存的BlockCache方案BucketCache,该方案为阿里贡献的,堆外内存的好处就是JVM几乎不会停顿,但由于性能上来说LRUCache较强,BucketCache属于二级缓存的概念,实际生产情况下,两者可以结合使用。
具体来说,HBase在读取时,是以Block为单位进行cache的,而Block分为DataBlock(默认64K,存储KV)、BloomBlock(默认128K,存储BloomFilter数据)、IndexBlock(默认128K,索引数据),对于一次随机读,Block的访问顺序为BloomBlock、IndexBlock、DataBlock。因此可以将IndexBlock和BloomBlock放入LRUCache张,DataBlock放入BucketCache。LRUCache使用内存,BucketCache使用SSD或offheap。
缓存块大小:BlockCache默认大小64K,如果业务请求以随机Get为主,可以考虑将其设置较小,比如32K;对于以SCAN为主的业务,可以适当加大到128K,以获得更好的性能。
BLOCKCACHE=>’true’
BLOCKSIZE=>’65535’
五、在内存中
基于LRUBlockCache策略,正常的数据读取过程中,块数据加载到缓存区中并长期驻留在内存中。
这个参数适应于数据量较少的列族,例如:登陆账号和密码的用户表,否则建议设为false。
In Memory=’false’
六、复制
REPLICATION_SCOPE=>’0’——复制范围,提供了跨集群的同步功能,无此需求可关闭。
七、压缩
COMPRESSION=>’SNAPPY’
数据压缩是HBase提供的另一个特性,HBase在写入数据块到HDFS之前会首先对数据块进行压缩,再落盘,从而可以减少磁盘空间使用。压缩是flush这个阶段执行的,因此对flush操作有影响,对写性能本身并不会有太大影响。而在读数据的时候首先从HDFS中加载出block块之后进行解压缩,然后再缓存到BlockCache(内存中的block是解压后的),最后返回给用户。由于读取存在解压缩过程,理论上读性能会有所下降,而如果是从缓存中读取数据,因为缓存中的数据是解压后的,那么性能不好有任何影响。大多数读都是热点读,缓存读占比例较大,因而压缩读读性能的影响不会太大。
HBase支持大量压缩算法,并支持列族级别上的数据压缩。建议开启。理论Snappy压缩率可以达到5:1,压缩率比较低,且编解码速率最高,对CPU消耗也最小,建议使用。
八、数据编码(Encode/Decode)
DataBlock Encode是HBase 0.94版本引入的特性,可以将重复的行/列族/值进行压缩,
减少block的空间占用,提高内存使用率。它和Compress提高了相似的功能,但由于用户从缓存中加载出KV时需要先解码,不利于读性能,因此不建议在内存不足的情况下启用。并且由于已开启了SNAPPY压缩,就无必要再开启编码了。
DATA_BLOCK_ENCODING=>’NONE’
九、生存期TTL
生存期(TTL)设置了一个基于时间戳的临界值,HBase会检查TTL值是否达到上限,在进行Major Compaction过程中被判定为TTL超期的数据会被删除。单位是秒。使用TTL默认值的数据可以理解为永久保存,建议设置合适保留生存期以节约存储。
十、使用批量put进行写入
HBase分别提供了单条put以及批量put的API接口,使用批量put接口可以减少客户
端到RegionServer之间的RPC连接数,提高写入性能。另外需要注意的是,批量put请求要么全部成功返回,要么抛出异常。
table.setWriteBufferSize(6* 1024 * 1024);
通过调用HTable.setWriteBufferSize(writeBufferSize)方法可以设置HTable客户端的写buffer大小,比如6M,如果新设置的buffer小于当前写buffer中的数据时,buffer将会被flush到服务端。其中,writeBufferSize的单位是byte字节数,可以根据实际写入数据量的多少来设置该值。需要注意的是,在某些情况下客户端异常的情况下缓存数据有可能丢失。
使用方式:setAutoFlush(false)
十一、SCAN或GET
对客户端访问的时候对返回结果大小做限制
1、setCaching(100),Scan.next()的一次RPC请求fetch的记录条数,可以设置一次返回多条缓存在客户端缓存,比如100条;
2、对列数量做限制(scan.setBatch(10)),可以控制每次获取的最大列数,进一步从列级别控制流量,例如表有两个列簇 cf,info,每个列簇50个列,这样每行可能有100列了,设置为10,则每次只取10列;
3、scan.setMaxResultSize(2*1024*1024)),HBase-1.2 默认值为 210241024,即2M。Scan.next()的一次RPC请求fetch的数据量大小,如果网络带宽足够,每次scan数据量比较大,可以配置高一点,但有可能导致scantimeout,该值需与setCaching配合使用;
4、hbase.server.scanner.max.result.size,该参数为server端的一个配置,为HBase-1.2版本以后新增,可以对返回结果大小在服务器端进行限制,默认值为 10010241024,即 100M。该参数表示当 Scan.next() 发起 RPC 后,服务端返回给客户端的最大字节数,防止 Server OOM。
对于离线批量读取请求设置禁用缓存,scan.setBlockCache(false)
通常离线批量读取数据会进行一次性全表扫描,一方面数据量很大,另一方面请求只会执行一次。这种场景下如果使用scan默认设置,就会将数据从HDFS加载出来之后放到缓存。可想而知,大量数据进入缓存必将其他实时业务热点数据挤出,其他业务不得不从HDFS加载,进而会造成明显的读延迟毛刺。 优化建议:离线批量读取请求设置禁用缓存,scan.setBlockCache(false)
十二、业务重试机制
重试机制主要考虑三个参数,一是重试的最大次数,hbase.client.retries.number,默认31。
HBase有一个重试系数表,表示重试时间会随着重试次数逐渐递增:
public static int RETRY_BACKOFF[] = { 1, 2, 3, 5, 10, 20, 40, 100, 100, 100,100, 200, 200 }
,最大是200,那么默认情况下最大的重试间隔休眠时间expectedSleep=hbase.client.pause*200,hbase.client.pause默认100,那么最大重试间隔休眠为20s。假设重试次数为31次,那么重试时间为[100,200,300,500,1000,2000,4000,10000,10000,10000,10000,20000,20000,…,20000],即客户端将在448s内重试30次,然后放弃连接HBase。这样的话,一个线程会存在7.5分钟左右的重试时间,从而导致其他线程阻塞,因此可考虑降低着两个参数值。
hbase.client.pause=80;hbase.client.retries.number=21。
十三、Rowkey设计
RowKey类似于RDBMS中的主键,设计好坏对业务影响很大,常见的方法有加盐、反转、哈希,这个后面单独开一篇文章来讲。