ROWKEY设计
- RowKey尽量散列设计,保证所有的数据都不是在一个Region上,从而避免读写的时候负载会集中在个别Region上
- RowKey的长度尽量短,建议不要超过16个字节,目的是减少HFile文件中key占用的磁盘空间,提供memstore内存利用率
- 系统开发过程中将RowKey数据类型设置为String类型,保证通用性
- RowKey具有定长性,便于排序和比较
- RowKey要具有业务含义
- RowKey在全表中具有唯一性
- 原生HBase只支持从小到大的字典排序,若想展现影片热度排行榜,这就要求HBase实现从大到小排列,针对这种情况可以采用Rowkey=Integer.MAX_VALUE-Rowkey的方式将Rowkey进行转换,最大的变最小,最小的变最大,在应用层再转回来即可完成排序需求
列族设计
- 列族不宜过多,列族类似于传统关系数据库中的表,关联的表越多,查询效率越低。一个表的列族建议不超过3个
- 经常要在一起查询的数据列尽量放在一个列族,避免跨列族访问
HBase调优
- 调节数据块(data block)的大小
数据块大小的设置影响数据块索引的大小。数据块越小,索引占用更大内存空间。
数据块越小,随机查找性能更好(get命令);数据块越大,更好的序列扫描性能(scan命令)。HFile数据块大小可以在列族层次设置。65536字节等于64KB
create '表名称',{NAME => '列族名称', BLOCKSIZE => '65536'}
- 关闭读缓存BlockCache
如果一个表或表的列族只被顺序化扫描访问或很少被访问,则Get或Scan操作花费时间长一点可以接受。在这种情况下,可以选择关闭列族的缓存,数据块缓存默认打开。
关闭缓存,可以防止缓存的滥用,让出更多缓存给其他表和同一表的其他列族使用。
create ‘表名称’, {NAME => '列族名称', BLOCKCACHE => 'false'}
- 开启布隆过滤器
布隆过滤器也需要占用额外的空间,且列级布隆过滤器空间花销比行级布隆过滤器占用空间多。当系统空间充裕时,可以开启布隆过滤器。可以在列族上打开布隆过滤器:
create '表名称', {NAME => '列族名称', BLOOMFILTER => 'ROWCOL'}
布隆过滤器参数的默认值是NONE。另外,还有两个值:ROW表示行级布隆过滤器;ROWCOL表示列标识符级布隆过滤器。行级布隆过滤器在数据块中检查特定行键是否不存在,列标识符级布隆过滤器检查行和列标识符联合体是否不存在。
- 开启数据压缩
HFile可以被压缩并存放在HDFS上,这有助于节省硬盘I/O,但是读写数据时压缩和解压缩会抬高CPU利用率。只有在数据不能被压缩,或者因为某些原因服务器的CPU利用率有限制要求的情况下,有可能需要关闭压缩特性。
HBase可以使用多种压缩编码,包括LZO、SNAPPY和GZIP,当建表时可以在列族上打开压缩:
create '表名称', {NAME => ''列族名称'', COMPRESSION => 'SNAPPY'}
注意,数据只在硬盘上是压缩的,在内存中(MemStore或BlockCache)或在网络传输时是没有压缩的
- 设置Scan缓存
HBase的Scan查询中可以设置缓存,定义从服务器端传输到客户端的行数,设置方法是使用Scan类中setCaching()方法,这样能有效地减少服务器端和客户端的交互。
- 显式地指定列
如果在查询中指定某列或者某几列,能够有效地减少网络传输量,在一定程度上提升查询性能
- 关闭ResultScanner
ResultScanner类用于存储服务端扫描的最终结果,可以通过遍历该类获取查询结果。但是,如果不关闭该类,可能会出现服务端在一段时间内一直保存连接,资源无法释放,从而导致服务器端某些资源的不可用。
代码的最后一行rsScanner.close()就是执行关闭ResultScanner
- 使用批量读
HTable.get(List<Get>)方法可以根据一个指定的行键列表,批量获取多行记录。使用该方法可以在服务器端执行完批量查询后返回结果,降低网络传输的速度,节省网络I/O开销,对于数据实时性要求高且网络传输RTT高的场景,能带来明显的性能提升。
- 使用批量写
HTable.put(List<Put>)方法可以将指定的多个行键批量写入。这样做的好处是批量执行,减少网络I/O开销。
- 关闭写WAL日志
WAL是规避数据丢失风险的一种补偿机制,如果应用可以容忍一定的数据丢失的风险,可以尝试在更新数据时,关闭写WAL。该方法存在的风险是,当RegionServer宕机时,可能写入的数据会出现丢失的情况,且无法恢复
通过在代码中添加:put.setWriteToWAL(false);来关闭WAL日志
- 设置AutoFlush
HTable有一个属性是AutoFlush,该属性用于支持客户端的批量更新。该属性默认值是true,即客户端每收到一条数据,立刻发送到服务端。如果将该属性设置为false,当客户端提交Put请求时,将该请求在客户端缓存,直到数据达到某个阈值的容量时(该容量由参数hbase.client.write.buffer决定)或执行hbase.flushcommits()时,才向RegionServer提交请求。这种方式避免了每次跟服务端交互,采用批量提交的方式,所以更高效。
但是,如果还没有达到该缓存阈值而客户端崩溃,该部分数据将由于未发送到RegionServer而丢失。这对于有些零容忍的在线服务是不可接受的。
可以在代码中添加下面代码
table.setAutoFlush(false);table.setWriteBufferSize(12*1024*1024);
实现客户端缓存
- 预创建Region
在HBase中创建表时,该表开始只有一个Region,并且在这个表创建后相当长的一段时间内,针对该表的所有写操作总是集中在某一台或者少数几台机器上,造成局部磁盘和网络资源紧张,同时也是对整个集群资源的浪费。为了解决这个问题,可以预先多创建几个HRegion。
${HBASE_HOME}/bin/hbase org.apache.hadoop.hbase.util.RegionSplitter 表名称 HexStringSplit -c 10 -f 列族名称
HexStringSplit表示划分的算法,
参数-c 10表示预创建10个Region
- 调整ZooKeeper Session的有效时长
vim hbase-site.xml
zookeeper.session.timeout默认值是180秒,这意味着一旦某个RegionServer宕机,HMaster至少需要180秒才能察觉到宕机,然后开始恢复。或者客户端读写过程中,如果服务端不能提供服务,客户端直到180秒后才能觉察到。在某些场景中,这样的时长可能对生产线业务来讲不能容忍,需要调整这个值。