热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

HBase(八):表结构设计优化,HBase(六):HBase体系结构剖析(上),HBase(七):HBase体系结构剖析(下)

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

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

  • 《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/
  • 《HBase(八): 表结构设计优化,HBase(六): HBase体系结构剖析(上),HBase(七): HBase体系结构剖析(下)》   《HBase(八): 表结构设计优化,HBase(六): HBase体系结构剖析(上),HBase(七): HBase体系结构剖析(下)》
  • 可见,如果业务请求以Get请求为主,可以考虑将块大小设置较小;如果以Scan请求为主,可以将块大小调大;默认的64K块大小是在Scan和Get之间取得的一个平衡
  • 平均键值对规划,如下

    [root@HDP0 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):00
      8: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):00
      9:0>

 COMPRESSION/ENCODING

  • Compression就是在用CPU资源换取磁盘空间资源,对读写性能并不会有太大影响,HBase目前提供了三种常用的压缩方式:GZip | LZO | Snappy
  • HBase在写入数据块到HDFS之前会首先对数据块进行压缩,再落盘,从而可以减少磁盘空间使用量
  • 读数据的时候首先从HDFS中加载出block块之后进行解压缩,然后再缓存到BlockCache,最后返回给用户。写路径和读路径分别如下
  • 《HBase(八): 表结构设计优化,HBase(六): HBase体系结构剖析(上),HBase(七): HBase体系结构剖析(下)》
  • 结合上图,来看看数据压缩对资源使用情况以及读写性能的影响:
    1. 资源使用情况:压缩最直接、最重要的作用即是减少数据硬盘容量,理论上snappy压缩率可以达到5:1,但是根据测试数据不同,压缩率可能并没有理论上理想;压缩/解压缩无疑需要大量计算,需要大量CPU资源;根据读路径来看,数据读取到缓存之前block块会先被解压,缓存到内存中的block是解压后的,因此和不压缩情况相比,内存前后基本没有任何影响
    2. 读写性能:因为数据写入是先将kv数据值写到缓存,最后再统一flush的硬盘,而压缩是在flush这个阶段执行的,因此会影响flush的操作,对写性能本身并不会有太大影响;而数据读取如果是从HDFS中读取的话,首先需要解压缩,因此理论上读性能会有所下降;如果数据是从缓存中读取,因为缓存中的block块已经是解压后的,因此性能不会有任何影响;一般情况下大多数读都是热点读,缓存读占大部分比例,压缩并不会对读有太大影响
  • 官方分别从压缩率,编解码速率三个方面对其进行对比如下图:
  • 《HBase(八): 表结构设计优化,HBase(六): HBase体系结构剖析(上),HBase(七): HBase体系结构剖析(下)》
  • 综合来看,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/
  • 《HBase(八): 表结构设计优化,HBase(六): HBase体系结构剖析(上),HBase(七): HBase体系结构剖析(下)》  《HBase(八): 表结构设计优化,HBase(六): HBase体系结构剖析(上),HBase(七): HBase体系结构剖析(下)》  《HBase(八): 表结构设计优化,HBase(六): HBase体系结构剖析(上),HBase(七): HBase体系结构剖析(下)》
  • 结果分析:
    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修改表结构设置

推荐阅读
  • 一、Hadoop来历Hadoop的思想来源于Google在做搜索引擎的时候出现一个很大的问题就是这么多网页我如何才能以最快的速度来搜索到,由于这个问题Google发明 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 2018深入java目标计划及学习内容
    本文介绍了作者在2018年的深入java目标计划,包括学习计划和工作中要用到的内容。作者计划学习的内容包括kafka、zookeeper、hbase、hdoop、spark、elasticsearch、solr、spring cloud、mysql、mybatis等。其中,作者对jvm的学习有一定了解,并计划通读《jvm》一书。此外,作者还提到了《HotSpot实战》和《高性能MySQL》等书籍。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 本文介绍了在CentOS 6.4系统中更新源地址的方法,包括备份现有源文件、下载163源、修改文件名、更新列表和系统,并提供了相应的命令。 ... [详细]
author-avatar
洋索菲命_563
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有