带你快速上手HBase | HBase列族优化

1,711 阅读8分钟

随着大数据的越来越普及,HBase也变得越来越流行。使用HBase并不困难,但是如何用好HBase,这确是一个难点。为了合理地使用HBase,尽可能发挥HBase的功能,我们需要根据不同的场景对HBase进行不同地优化以最大程度上提升系统的性能。本文重点介绍列族设计有关的优化。我们先来了解下HBase列族具有哪些属性配置。

列族属性配置

版本数量(VERSIONS)

每个列族可以单独设置行版本数,默认是3。这个设置很重要,因为HBase是不会去覆盖一个值的,它只会在后面追加写,用时间戳(版本号)来区分,过早的版本会在执行Major Compaction时删除。这个版本的值可以根据具体的场景来增加或减少。

不推荐将版本最大值设置成一个很高的水平,除非老数据对你也非常重要。过多的版本,会导致存储文件变大,以至于影响查询效率。

最小版本数(MIN_VERSIONS )

每个列族可以设置最小版本数,最小版本数缺省值是0,表示禁用该特性。最小版本数参数和存活时间是一起使用的,允许配置“如保存最后T秒有价值的数据,最多N个版本,但最少M个版本”(M是最小版本,M<N)。该参数仅在存活时间对列族启用,且必须小于行版本数。

存活时间(TTL)

HBase支持配置版本数据的存活时间(TTL),TTL设置了一个基于时间戳的临界值,HBase会自动检查TTL值是否达到上限,如果TTL达到上限,则该数据会在Major Compaction过程中被删除。

数据块大小(BLOCKSIZE )

hbase默认的块大小是64kb,不同于HDFS默认64MB的块大小。原因是hbase需要支持随机访问,一旦找到了行键所在的块,接下来就会定位对应的单元格。使用64kb的块扫描的速度显然优于64MB大小的块。

对于不同的业务数据,块大小的合理设置对读写性能有很大的影响。如果业务请求以Get请求为主,可以考虑将块大小设置较小;如果以Scan请求为主,可以将块大小调大;默认的64K块大小是在Scan和Get之间取得的一个平衡。

注意:

默认块大小适用于多种数据使用模式,调整块大小是比较高级的操作。配置错误将对性能产生负面影响。因此建议在调整之后进行测试,根据测试结果决定是否可以线上使用。

块缓存(BLOCKCACHE)

默认是true。缓存是内存存储,hbase使用块缓存将最近使用的块加载到内存中。块缓存会根据最近最久未使用(LRU)”的规则删除数据块。

如果你的使用场景是经常顺序访问或者很少被访问,可以关闭列族的缓存。列族缓存默认是打开的。

激进缓存的配置(IN_MEMORY)

HBase可以选择一个列族赋予更高的优先级缓存,激进缓存(表示优先级更高),IN_MEMORY 默认是false。如果设置为true,hbase会尝试将整个列族保存在内存中,只有在需要保存是才会持久化写入磁盘。但是在运行时hbase会尝试将整张表加载到内存里。

这个参数通常适合较小的列族。

压缩(COMPRESSION)

HBase在写入数据块到HDFS之前会首先对数据进行压缩,再落盘,从而减少磁盘空间使用量。而在读数据的时候首先从HDFS中加载出block块之后进行解压缩,然后再缓存到BlockCache,最后返回给用户。

使用压缩其实就是使用CPU资源换取磁盘空间资源

HBase支持三种压缩方式:LZOSnappyGZIP

默认为NONE,不适用压缩,

压缩算法压缩比率压缩速度解压速度
GZIP13.4%21 MB/s118 MB/s
LZO20.5%135 MB/s410 MB/s
Snappy22.2%172 MB/s409 MB/s

其中:

  1. GZIP的压缩率最高,但是其实CPU密集型的,对CPU的消耗比其他算法要多,压缩和解压速度也慢;

  2. LZO的压缩率居中,比GZIP要低一些,但是压缩和解压速度明显要比GZIP快很多,其中解压速度快的更多;

  3. Snappy的压缩率最低,而压缩和解压速度要稍微比LZO要快一些。

综合来看,Snappy的压缩率最低,但是编解码速率最高,对CPU的消耗也最小,目前一般建议使用Snappy。

复制范围(REPLICATION_SCOPE )

HBase提供了跨级群同步的功能,本地集群的数据更新可以及时同步到其他集群。复制范围(replication scope)的参数默认为0,表示复制功能处于关闭状态。

预分区(SPLITS )

在默认情况下,HBase表在刚刚被创建的时候,只有1个分区(Region),当一个Region的大小达到阈值(通过hbase.hregion.max.filesize参数控制),Region会进行split,分裂成2个Region。但是在进行split的时候,会消耗大量的资源,频繁的split会对HBase的性能造成巨大的影响。

HBase提供了预分区的功能,用户可以在创建表的时候对表按照一定的规则提前进行分区。这样是进行HBase数据读写的时候,会按照Region分区情况,在集群内做数据的负载均衡。

常用分区方法:

create 'table','cf', SPLITS => ['1', '2', '3', '4', '5', '6', '7', '8', '9']create 'table','cf', { NUMREGIONS => 8 , SPLITALGO => 'UniformSplit' }create 'table','cf', { NUMREGIONS => 10, SPLITALGO => 'HexStringSplit' } 

BLOOMFILTER

BloomFilter主要用来过滤不存在待检索RowKey或者Row-Col的HFile文件,避免无用的IO操作。它会告诉你在这个HFile文件中是否可能存在待检索的KV,如果不存在,就可以不用消耗IO打开文件进行seek。通过设置BloomFilter可以提升随机读写的性能。

BloomFilter是一个列族级别的配置属性,如果在表中设置了BloomFilter,那么HBase会在生成StoreFile时包含一份BloomFilter结构的数据,称其为MetaBlockDataBlock(真实KeyValue数据)一起由LRUBlockCache维护。所以开启BloomFilter会有一定的存储即内存Cache的开销。

BloomFilter取值有两个,rowrowcol,需要根据业务来确定具体使用哪种。

  • 如果业务大多数随机查询时仅仅使用row作为查询条件,BloomFilter一定要设置为row;

  • 如果大多数随机查询使用row+cf作为查询条件,BloomFilter需要设置为rowcol;

  • 如果不确定查询类型,建议设置为row。

列族设置

列族数量

不要在一张表中定义太多的列族。目前HBase并不能很好的处理2~3以上的列族,flushcompaction 操作是针对一个Region的。

当一个列族操作大量数据的时候会引发一个flush,它邻近的列族也会因关联效应被触发flush,尽管它没有操作多少数据。compaction操作是根据一个列族下的全部文件的数量触发的,而不是根据文件大小触发的。

当很多的列族在flush和compaction时,会造成很多没用的IO负载。

尽量在模式中只针对一个列族进行操作。将使用率相近的列归为一个列族,这样每次访问就只用访问一个列族,既能提升查询效率,也能保持尽可能少的访问不同的磁盘文件。

列族的基数

如果一个表存在多个列族,要注意列族之间基数(如行数)相差不要太大。例如列族A有100万行,列族B有10亿行,按照RowKey切分后,列族A可能被分散到很多很多Region(及RegionServer),这导致扫描列族A十分低效。

列族名、列名长度

列族名和列名越短越好,冗长的名字虽然可读性好,但是更短的名字在HBase中更好。

一个具体的值由存储该值的行键、对应的列(列族:列)以及该值的时间戳决定。HBase中索引是为了加速随机访问的速度,索引的创建是基于“行键+列族:列+时间戳+值”的,如果行键和列族的大小过大,甚至超过值本身的大小,那么将会增加索引的大小。并且在HBase中数据记录往往非常之多,重复的行键、列将不但使索引的大小过大,也将加重系统的负担

总结

根据HBase列族的这些属性配置,结合我们的使用场景,HBase列族可以进行如下优化:

  1. 列族不宜过多,将相关性很强的key-value都放在同一个列族下,;

  2. 尽量最小化行键和列族的大小;

  3. 提前预估数据量,再根据Rowkey规则,提前规划好Region分区,在创建表的时候进行预分区;

  4. 在业务上没有特别要求的情况下,只使用一个版本,即最大版本和最小版本一样,均为1;

  5. 根据业务需求合理设置好失效时间(存储的时间越短越好);

  6. 根据查询条件,设置合理的BloomFilter配置;

  7. 合理设计RowKey,可以参考《一篇文章带你快速搞懂HBase RowKey设计》。