HBase(八): 表结构设计优化

HBase(六): HBase体系结构剖析(上) 介绍过,Hbase创建表时,只需指定表名和至少一个列族,基于HBase表结构的设计优化主要是基于列族级别的属性配置,如下图:

目录:

  • BLOOMFILTER
  • BLOCKSIZE
  • IN_MEMORY
  • COMPRESSION/ENCODING
  • VERSIONS
  • TTL

BLOOMFILTER


  • Bloom Filter是由Bloom在1970年提出的一种多哈希函数映射的快速查找算法。通常应用在一些需要快速判断某个元素是否属于集合,但是并不严格要求100%正确的场合
  • bloom filter的数据存在StoreFile的meta中,一旦写入无法更新,因为StoreFile是不可变的。Bloomfilter是一个列族(cf)级别的配置属性,如果在表中设置了Bloomfilter,那么HBase会在生成StoreFile时包含一份bloomfilter结构的数据,称其为MetaBlock;MetaBlock与DataBlock(真实的KeyValue数据)一起由LRUBlockCache维护。所以,开启bloomfilter会有一定的存储及内存cache开销
  • 对于已经存在的表,可以使用alter表的方式修改表结构,但这种修改对于之前的数据不会生效,只针对修改后插入的数据
  • 包含三种类型:NONE、ROW、ROWCOL
    1. ROW: 根据KeyValue中的row来过滤storefile,举例如下:假设有2个storefile文件sf1和sf2

      • sf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v)
      • sf2包含kv3(r3 cf:q1 v)、kv4(r4 cf:q1 v)
      • 如果设置了CF属性中的bloomfilter为ROW,那么get(r1)时就会过滤sf2,get(r3)就会过滤sf1
    2. ROWCOL:根据KeyValue中的row+qualifier来过滤storefile
      • 如上例:若设置了CF属性中的bloomfilter为ROW,无论get(r1,q1)还是get(r1,q2),都会读取sf1+sf2;
      • 而如果设置了CF属性中的bloomfilter为ROWCOL,那么get(r1,q1)就会过滤sf2,get(r1,q2)就会过滤sf1
  • region下的storefile数目越多,bloomfilter的效果越好
  • region下的storefile数目越少,HBase读性能越好

BLOCKSIZE:


  • 从上图可发现,默认的BlockSize 为 65536B (64KB),在 HBase(七): HBase体系结构剖析(下) 介绍HBase读原理,如果在blaock cache 、memostore中都没查到符合条件的数据,则循环遍历 storeFile 文件,而hbase读取磁盘文件是按其基本I/O单元(即 hbase block)读数据的,因此HFile块大小是影响性能的重要参数
  • 参见Get\Scan场景下测试不同BlockSize大小(16K,64K,128K)对性能的影响,如下图:对比结果参考:http://hbasefly.com/2016/07/02/hbase-pracise-cfsetting/
  •   
  • 可见,如果业务请求以Get请求为主,可以考虑将块大小设置较小;如果以Scan请求为主,可以将块大小调大;默认的64K块大小是在Scan和Get之间取得的一个平衡
  • 平均键值对规划,如下

    [[email protected] bin]# hbase hfile -m -f /apps/hbase/data/data/default/PerTest/7685e6c39d1394d94e26cf5ddafb7f9f/d/3ef195ca65044eca93cfa147414b56c2
    SLF4J: Class path contains multiple SLF4J bindings.
    SLF4J: Found binding in [jar:file:/usr/hdp/2.4.2.0-258/hadoop/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: Found binding in [jar:file:/usr/hdp/2.4.2.0-258/zookeeper/lib/slf4j-log4j12-1.6.1.jar!/org/slf4j/impl/StaticLoggerBinder.class]
    SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
    SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
    2016-09-11 12:54:40,514 INFO  [main] hfile.CacheConfig: CacheConfig:disabled
    Block index size as per heapsize: 6520
    reader=/apps/hbase/data/data/default/PerTest/7685e6c39d1394d94e26cf5ddafb7f9f/d/3ef195ca65044eca93cfa147414b56c2,
        compression=none,
        cacheConf=CacheConfig:disabled,
        firstKey=00123ed7-5af8-49b1-bd13-9e086a5bd5f2/d:Action/1471406616120/Put,
        lastKey=fffbc8f7-55f2-4c49-804f-444f6ccbc903/d:UserID/1471406614464/Put,
        avgKeyLen=55,
        avgValueLen=10,
        entries=54180,
        length=4070738
  • 从上面输出的信息可以看出,该HFile的平均键值对规模为55B + 10B = 65B,相对较小,在这种情况下可以适当将块大小调小(例如8KB或16KB)。这样可以使得一个block内不会有太多kv,kv太多会增大块内寻址的延迟时间,因为HBase在读数据时,一个block内部的查找是顺序查找
  • 选择较小块的大小的目的是使随机读取更快,而付出的代价是块索引变大,会消耗更多的内存。相反,如果平均键值对规模很大,或者磁盘速度慢造成了性能瓶颈,那就应该选择一个较大的块大小,以便使一次磁盘IO能够读取更多的数据
  • 思考:实际场景大部分是Scan读,但平均键值规划较小,如何设置BlockSize?

 IN_MEMORY:


  • Block Cache 包含三个级别的优先级队列:
    1. Single: 如果一个Block被第一次访问,则放在这一级的队列中
    2. Multi:  如果一个Block被多次访问,则从Single队列移动Multi队列
    3. In Memory: 它是静态指定的(在column family上设置),不会像其他两种cache会因访问频率而发生改变,这就决定了它的独立性,另外两种block访问次数再多也不会被放到in-memory的区段里去,in-memory的block不管是第几次访问,总是被放置到in-memory的区段中
    • LRU(Least Recently Used)淘汰数据时,Single会被优先淘汰,其次是Multi, 最后是In Memory, 这三个队列分别占用 BlockCache的 25%、50%、25%
    • 每load一个block到cache时,都会检查当前cache的size是否已经超过了“警戒线”,这个“警戒线”是一个规定的当前block cache总体积占额定体积的安全比例,默认该值是0.85,即当加载了一个block到cache后总大小超过了既定的85%就开始触发异步的evict操作了
    • evict的逻辑是这样的:遍历cache中的所有block,根据它们所属的级别(single,multi,in-memory)分拨到三个优先级队列中,队头元素是最旧(最近访问日间值最小)的那个block。对这个三队列依次驱逐头元素,释放空间
    • 注意: 标记 IN_MEMORY=>‘true‘ 的column family的总体积最好不要超过in-memory cache的大小(in-memory cache = heap size * hfile.block.cache.size * 0.85 * 0.25),特别是当总体积远远大于了in-memory cache时,会在in-memory cache上发生严重的颠簸
    • 换个角度再看,普遍提到的使用in-memory cache的场景是把元数据表的column family声明为IN_MEMORY=>‘true。实际上这里的潜台词是:元数据表都很小。其时我们也可以大胆地把一些需要经常访问的,总体积不会超过in-memory cache的column family都设为IN_MEMORY=>‘true‘从而更加充分地利用cache空间。普通的block永远是不会被放入in-memory cache的,只存放少量metadata是对in-memory cache资源的浪费
    • 操作命令如下(建表时或alter已创建的表):

      hbase(main):002:0> create ‘Test‘,{NAME=>‘d‘,IN_MEMORY=>‘true‘}
      0 row(s) in 4.4970 seconds
      => Hbase::Table - Test
      hbase(main):003:0> describe ‘Test‘
      Table Test is ENABLED
      Test
      COLUMN FAMILIES DESCRIPTION
      {NAME => ‘d‘, BLOOMFILTER => ‘ROW‘, VERSIONS => ‘1‘, IN_MEMORY => ‘true‘, KEEP_DELETED_CELLS => ‘FALSE‘, DATA_BLOCK_ENCODING => ‘NONE‘, TTL => ‘FOREVER‘, COMPRESSION => ‘NONE‘, MIN_VERSIONS =>
      ‘0‘, BLOCKCACHE => ‘true‘, BLOCKSIZE => ‘65536‘, REPLICATION_SCOPE => ‘0‘}
      1 row(s) in 0.2530 seconds
      
      hbase(main):004:0> create ‘Test1‘,‘d‘
      0 row(s) in 2.2400 seconds
      => Hbase::Table - Test1
      hbase(main):005:0> disable ‘Test1‘
      0 row(s) in 2.2730 seconds
      
      hbase(main):006:0> alter ‘Test1‘,{NAME=>‘d‘,IN_MEMORY=>‘true‘}
      Updating all regions with the new schema...
      1/1 regions updated.
      Done.
      0 row(s) in 2.4610 seconds
      
      hbase(main):007:0> enable ‘Test1‘
      0 row(s) in 1.3370 seconds
      
      hbase(main):008:0> describe ‘Test1‘
      Table Test1 is ENABLED
      Test1
      COLUMN FAMILIES DESCRIPTION
      {NAME => ‘d‘, BLOOMFILTER => ‘ROW‘, VERSIONS => ‘1‘, IN_MEMORY => ‘true‘, KEEP_DELETED_CELLS => ‘FALSE‘, DATA_BLOCK_ENCODING => ‘NONE‘, TTL => ‘FOREVER‘, COMPRESSION => ‘NONE‘, MIN_VERSIONS =>
      ‘0‘, BLOCKCACHE => ‘true‘, BLOCKSIZE => ‘65536‘, REPLICATION_SCOPE => ‘0‘}
      1 row(s) in 0.0330 seconds
      
      hbase(main):009:0> 

 COMPRESSION/ENCODING:


  • Compression就是在用CPU资源换取磁盘空间资源,对读写性能并不会有太大影响,HBase目前提供了三种常用的压缩方式:GZip | LZO | Snappy
  • HBase在写入数据块到HDFS之前会首先对数据块进行压缩,再落盘,从而可以减少磁盘空间使用量
  • 读数据的时候首先从HDFS中加载出block块之后进行解压缩,然后再缓存到BlockCache,最后返回给用户。写路径和读路径分别如下
  • 结合上图,来看看数据压缩对资源使用情况以及读写性能的影响:
    1. 资源使用情况:压缩最直接、最重要的作用即是减少数据硬盘容量,理论上snappy压缩率可以达到5:1,但是根据测试数据不同,压缩率可能并没有理论上理想;压缩/解压缩无疑需要大量计算,需要大量CPU资源;根据读路径来看,数据读取到缓存之前block块会先被解压,缓存到内存中的block是解压后的,因此和不压缩情况相比,内存前后基本没有任何影响
    2. 读写性能:因为数据写入是先将kv数据值写到缓存,最后再统一flush的硬盘,而压缩是在flush这个阶段执行的,因此会影响flush的操作,对写性能本身并不会有太大影响;而数据读取如果是从HDFS中读取的话,首先需要解压缩,因此理论上读性能会有所下降;如果数据是从缓存中读取,因为缓存中的block块已经是解压后的,因此性能不会有任何影响;一般情况下大多数读都是热点读,缓存读占大部分比例,压缩并不会对读有太大影响
  • 官方分别从压缩率,编解码速率三个方面对其进行对比如下图:
  • 综合来看,Snappy的压缩率最低,但是编解码速率最高,对CPU的消耗也最小,目前一般建议使用Snappy
  • 从上图看数据编码对资源使用情况以及读写性能的影响:
    1. 资源使用情况:和压缩一样,编码最直接、最重要的作用也是减少数据硬盘容量,但是数据编码压缩率一般没有数据压缩的压缩率高,理论上只有5:2;编码/解码一般也需要大量计算,需要大量CPU资源;根据读路径来看,数据读取到缓存之前block块并没有被解码,缓存到内存中的block是编码后的,因此和不编码情况相比,相同数据block快占用内存更少,即内存利用率更高
    2. 读写性能:和数据压缩相同,数据编码也是在数据flush到hdfs阶段执行的,因此并不会直接影响写入过程;前面讲到,数据块是以编码形式缓存到blockcache中的,因此同样大小的blockcache可以缓存更多的数据块,这有利于读性能。另一方面,用户从缓存中加载出来数据块之后并不能直接获取KV,而需要先解码,这却不利于读性能。可见,数据编码在内存充足的情况下会降低读性能,而在内存不足的情况下需要经过测试才能得出具体结论
  • HBase目前提供了四种常用的编码方式:Prefix | Diff | Fast_Diff | Prefix_Tree。
  • 压缩与编码使用测试结果示例,来源于:http://hbasefly.com/2016/07/02/hbase-pracise-cfsetting/
  •    
  • 结果分析:
    1. 数据压缩率并没有理论上0.2那么高,只有0.7左右,这和数据结构有关系。其中压缩、编码、压缩+编码三种方式的压缩率基本相当
    2. 随机读场景:和默认配置相比,snappy压缩在性能上没有提升,CPU开销却上升了38%;prefix_tree性能上没有提升,CPU利用率也基本相当;snappy+prefix_tree性能没有提升,CPU开销上升了38%
    3. 区间扫描场景:和默认配置相比,snappy压缩在性能上略有10%的提升,但是CPU开销却上升了23%;prefix_tree性能上略有4%左右的下降,但是CPU开销也下降了5%,snappy+prefix_tree在性能上基本没有提升,CPU开销却上升了23%
  • 设计原则:
    1. 在任何场景下开启prefix_tree编码都是安全的
    2. 在任何场景下都不要同时开启snappy压缩和prefix_tree编码
    3. 通常情况下snappy压缩并不能比prefix_tree编码获得更好的优化结果,如果需要使用snappy需要针对业务数据进行实际测试

 VERSIONS:


  •  用于定义某列族所能记录的最多的版本数量,默认值是3,即每个单元格的最大版本数量是3
  • 对于更新频繁的应用,建设设置为1,可以快速淘汰无用的数据,节省存储空间同时还能提升查询效率
  • 同样道理,可在建表时指定或通过alter修改表结构实现

TTL:


  • TTL:Time To Live 用于定义列族中单元格存活时间,过期数据自动删除
  • TTL属性特性:
    1. 单位是秒,默认值:FOREVEN (永不过期)
    2. 当一行所有列都过期后,RowKey也会被删除
    3. 若TTL设置为两个月,则时间戮为2个月之前的数据不能插入
  • 同理,在建表时指定或通过alter修改表结构设置
时间: 2024-09-29 23:57:06

HBase(八): 表结构设计优化的相关文章

mySQL表结构优化

前言 很多人都将<数据库设计范式>作为数据库表结构设计"圣经",认为只要按照这个范式需求设计,就能让设计出来的表结构足够优化,既能保证性能优异同时还能满足扩展性要求.殊不知,在N年前被奉为"圣经"的数据库设计3范式早就已经不完全适用了.这里我整理了一些比较常见的数据库表结构设计方面的优化技巧,希望对大家有用. 由于MySQL数据库是基于行(Row)存储的数据库,而数据库操作 IO 的时候是以 page(block)的方式,也就是说,如果我们每条记录所占用

MySQL详解(22)------------表结构优化

很多人都将 数据库设计范式 作为数据库表结构设计"圣经",认为只要按照这个范式需求设计,就能让设计出来的表结构足够优化,既能保证性能优异同时还能满足扩展性要求.殊不知,在N年前被奉为"圣经"的数据库设计3范式早就已经不完全适用了.此课时整理了一些比较常见的数据库表结构设计方面的优化技巧,希望对大家有用. 由于MySQL数据库是基于行(Row)存储的数据库,而数据库操作 IO 的时候是以 page(block)的方式,也就是说,如果我们每条记录所占用的空间量减小,就会

hive中与hbase外部表join时内存溢出(hive处理mapjoin的优化器机制)

与hbase外部表(wizad_mdm_main)进行join出现问题: CREATE TABLE wizad_mdm_dev_lmj_edition_result as select * from  wizad_mdm_dev_lmj_20141120 as w JOIN wizad_mdm_main as a ON (a.rowkey = w.guid); 程序启动后,死循环,无反应.最后在进行到0.83时,内存溢出失败. 原因: 默认情况下,Hive会自动将小表加到DistributeCa

hbase表设计优化原则 ***** 生产环境中使用小结

2019/2/28 星期四 hbase表设计优化原则 https://www.cnblogs.com/qingyunzong/p/8696962.html表设计1.列簇设计 追求的原则是:在合理范围内能尽量少的减少列簇就尽量减少列簇. 最优设计是:将所有相关性很强的 key-value 都放在同一个列簇下,这样既能做到查询效率 最高,也能保持尽可能少的访问不同的磁盘文件. 以用户信息为例,可以将必须的基本信息存放在一个列族,而一些附加的额外信息可以放在 另一列族.2.RowKey 设计 HBas

HBase 数据库检索性能优化策略

HBase 数据表介绍 HBase 数据库是一个基于分布式的.面向列的.主要用于非结构化数据存储用途的开源数据库.其设计思路来源于 Google 的非开源数据库"BigTable". HDFS 为 HBase 提供底层存储支持,MapReduce 为其提供计算能力.ZooKeeper 为其提供协调服务和 failover(失效转移的备份操作)机制.Pig 和 Hive 为 HBase 提供了高层语言支持,使其能够进行数据统计(可实现多表 join 等).Sqoop 则为其提供 RDBM

浅谈数据库用户表结构设计,第三方登录

说起用户表,大概是每个应用/网站立项动工(码农们)考虑的第一件事情.用户表结构的设计,算是整个后台架构的基石.如果基石不稳,待到后面需求跟进了发现不能应付,回过头来反复修改用户表,要大大小小作改动的地方也不少.与其如此,不妨设计用户表之初就考虑可拓展性,争取不需要太多额外代价的情况下一步到位. 先前设计: id username password 用户名加上密码,解决简单需求,留个id作为其他表的外键.当然,那时候密码还可能是明文存储,好点的知道md5. 后来呢,随着业务需求的拓展,要加个用户状

mysql 用户表结构设计,第三方登录

说起用户表,大概是每个应用/网站立项动工(码农们)考虑的第一件事情.用户表结构的设计,算是整个后台架构的基石.如果基石不稳,待到后面需求跟进了发现不能应付,回过头来反复修改用户表,要大大小小作改动的地方也不少.与其如此,不妨设计用户表之初就考虑可拓展性,争取不需要太多额外代价的情况下一步到位. 先前设计 idusernamepassword用户名加上密码,解决简单需求,留个id作为其他表的外键.当然,那时候密码还可能是明文存储,好点的知道md5. 后来呢,随着业务需求的拓展,要加个用户状态 st

深入学习hbase:表,列族,列标识,版本和cell

HBase是面向列的分布式的数据库,和传统的关系型数据库有很大的不同:物理模型和逻辑模型.这里我们要首先讲一下HBase数据库相关的区别于关系型数据库的几个基本概念:          表:HBase将数据组织到自己的HTable表中,这个表是根据列族(colomn family)在物理上保存数据的,每个列族都有自己的文件夹和storefiles,不像关系型数据库那样将一个表保存成一个文件,表明也是文件系统路径的一部分.          行:HBase中的行是逻辑上的行,物理上模型上行是按列族

【转载】HBase 数据库检索性能优化策略

转自:http://www.ibm.com/developerworks/cn/java/j-lo-HBase/index.html 高性能 HBase 数据库 本文首先介绍了 HBase 数据库基本原理及专用术语,然后介绍了 HBase 数据库发布的操作 API 及部分示例,重点介绍了 Scan 方法的操作方式,接着介绍了检索 HBase 数据库时的优化方案,最后通过一个示例总结了实际项目中遇到的检索速度慢的解决方案. HBase 数据表介绍 HBase 数据库是一个基于分布式的.面向列的.主