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
- 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
- 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
- ROW: 根据KeyValue中的row来过滤storefile,举例如下:假设有2个storefile文件sf1和sf2
- 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之间取得的一个平衡
- 平均键值对规划,如下
- [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
- [root@HDP0 bin]# hbase hfile -m -f /apps/hbase/data/data/default/PerTest/7685e6c39d1394d94e26cf5ddafb7f9f/d/3ef195ca65044eca93cfa147414b56c2
从上面输出的信息可以看出,该HFile的平均键值对规模为55B + 10B = 65B,相对较小,在这种情况下可以适当将块大小调小(例如8KB或16KB)。这样可以使得一个block内不会有太多kv,kv太多会增大块内寻址的延迟时间,因为HBase在读数据时,一个block内部的查找是顺序查找
- 选择较小块的大小的目的是使随机读取更快,而付出的代价是块索引变大,会消耗更多的内存。相反,如果平均键值对规模很大,或者磁盘速度慢造成了性能瓶颈,那就应该选择一个较大的块大小,以便使一次磁盘IO能够读取更多的数据
- 思考:实际场景大部分是Scan读,但平均键值规划较小,如何设置BlockSize?
IN_MEMORY:
- Block Cache 包含三个级别的优先级队列:
- Single: 如果一个Block被第一次访问,则放在这一级的队列中
- Multi: 如果一个Block被多次访问,则从Single队列移动Multi队列
- 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 => '', IN_MEMORY => 'true', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS =>
- '', BLOCKCACHE => 'true', BLOCKSIZE => '', REPLICATION_SCOPE => ''}
- 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 => '', IN_MEMORY => 'true', KEEP_DELETED_CELLS => 'FALSE', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS =>
- '', BLOCKCACHE => 'true', BLOCKSIZE => '', REPLICATION_SCOPE => ''}
- 1 row(s) in 0.0330 seconds
- hbase(main):009:0>
- hbase(main):002:0> create 'Test',{NAME=>'d',IN_MEMORY=>'true'}
COMPRESSION/ENCODING:
- Compression就是在用CPU资源换取磁盘空间资源,对读写性能并不会有太大影响,HBase目前提供了三种常用的压缩方式:GZip | LZO | Snappy
- HBase在写入数据块到HDFS之前会首先对数据块进行压缩,再落盘,从而可以减少磁盘空间使用量
- 读数据的时候首先从HDFS中加载出block块之后进行解压缩,然后再缓存到BlockCache,最后返回给用户。写路径和读路径分别如下
- 结合上图,来看看数据压缩对资源使用情况以及读写性能的影响:
- 资源使用情况:压缩最直接、最重要的作用即是减少数据硬盘容量,理论上snappy压缩率可以达到5:1,但是根据测试数据不同,压缩率可能并没有理论上理想;压缩/解压缩无疑需要大量计算,需要大量CPU资源;根据读路径来看,数据读取到缓存之前block块会先被解压,缓存到内存中的block是解压后的,因此和不压缩情况相比,内存前后基本没有任何影响
- 读写性能:因为数据写入是先将kv数据值写到缓存,最后再统一flush的硬盘,而压缩是在flush这个阶段执行的,因此会影响flush的操作,对写性能本身并不会有太大影响;而数据读取如果是从HDFS中读取的话,首先需要解压缩,因此理论上读性能会有所下降;如果数据是从缓存中读取,因为缓存中的block块已经是解压后的,因此性能不会有任何影响;一般情况下大多数读都是热点读,缓存读占大部分比例,压缩并不会对读有太大影响
- 官方分别从压缩率,编解码速率三个方面对其进行对比如下图:
- 综合来看,Snappy的压缩率最低,但是编解码速率最高,对CPU的消耗也最小,目前一般建议使用Snappy
- 从上图看数据编码对资源使用情况以及读写性能的影响:
- 资源使用情况:和压缩一样,编码最直接、最重要的作用也是减少数据硬盘容量,但是数据编码压缩率一般没有数据压缩的压缩率高,理论上只有5:2;编码/解码一般也需要大量计算,需要大量CPU资源;根据读路径来看,数据读取到缓存之前block块并没有被解码,缓存到内存中的block是编码后的,因此和不编码情况相比,相同数据block快占用内存更少,即内存利用率更高
- 读写性能:和数据压缩相同,数据编码也是在数据flush到hdfs阶段执行的,因此并不会直接影响写入过程;前面讲到,数据块是以编码形式缓存到blockcache中的,因此同样大小的blockcache可以缓存更多的数据块,这有利于读性能。另一方面,用户从缓存中加载出来数据块之后并不能直接获取KV,而需要先解码,这却不利于读性能。可见,数据编码在内存充足的情况下会降低读性能,而在内存不足的情况下需要经过测试才能得出具体结论
- HBase目前提供了四种常用的编码方式:Prefix | Diff | Fast_Diff | Prefix_Tree。
- 压缩与编码使用测试结果示例,来源于:http://hbasefly.com/2016/07/02/hbase-pracise-cfsetting/
- 结果分析:
- 数据压缩率并没有理论上0.2那么高,只有0.7左右,这和数据结构有关系。其中压缩、编码、压缩+编码三种方式的压缩率基本相当
- 随机读场景:和默认配置相比,snappy压缩在性能上没有提升,CPU开销却上升了38%;prefix_tree性能上没有提升,CPU利用率也基本相当;snappy+prefix_tree性能没有提升,CPU开销上升了38%
- 区间扫描场景:和默认配置相比,snappy压缩在性能上略有10%的提升,但是CPU开销却上升了23%;prefix_tree性能上略有4%左右的下降,但是CPU开销也下降了5%,snappy+prefix_tree在性能上基本没有提升,CPU开销却上升了23%
- 设计原则:
- 在任何场景下开启prefix_tree编码都是安全的
- 在任何场景下都不要同时开启snappy压缩和prefix_tree编码
- 通常情况下snappy压缩并不能比prefix_tree编码获得更好的优化结果,如果需要使用snappy需要针对业务数据进行实际测试
VERSIONS:
- 用于定义某列族所能记录的最多的版本数量,默认值是3,即每个单元格的最大版本数量是3
- 对于更新频繁的应用,建设设置为1,可以快速淘汰无用的数据,节省存储空间同时还能提升查询效率
- 同样道理,可在建表时指定或通过alter修改表结构实现
TTL:
- TTL:Time To Live 用于定义列族中单元格存活时间,过期数据自动删除
- TTL属性特性:
- 单位是秒,默认值:FOREVEN (永不过期)
- 当一行所有列都过期后,RowKey也会被删除
- 若TTL设置为两个月,则时间戮为2个月之前的数据不能插入
- 同理,在建表时指定或通过alter修改表结构设置
HBase(八): 表结构设计优化的更多相关文章
- Hbase入门(四)——表结构设计-RowKey
Hbase的表结构设计与关系型数据库有很多不同,主要是Hbase有Rowkey和列族.timestamp这几个全新的概念,如何设计表结构就非常的重要. 创建 Hbase就是通过 表 Rowkey 列族 ...
- MySQL优化之表结构优化的5大建议(数据类型选择讲的很好)
殊不知,在N年前被奉为"圣经"的数据库设计3范式早就已经不完全适用了.这里我整理了一些比较常见的数据库表结构设计方面的优化技巧,希望对大家有用. 由于MySQL数据库是基于行(Ro ...
- MySQL优化三 表结构优化
由于MySQL数据库是基于行(Row)存储的数据库,而数据库操作 IO 的时候是以 page(block)的方式,也就是说,如果我们每条记录所占用的空间量减小,就会使每个page中可存放的数据行数增大 ...
- MySQL性能优化方法二:表结构优化
原文链接:http://isky000.com/database/mysql-perfornamce-tuning-schema 很多人都将 数据库设计范式 作为数据库表结构设计“圣经”,认为只要按照 ...
- MySQL优化之表结构优化的5大建议
很多人都将 数据库设计范式 作为数据库表结构设计“圣经”,认为只要按照这个范式需求设计,就能让设计出来的表结构足够优化,既能保证性能优异同时还能满足扩展性要求殊不知,在N年前被奉为“圣经”的数据库设计 ...
- 0709MySQL 数据库性能优化之表结构优化
转自http://isky000.com/database/mysql-perfornamce-tuning-schema MySQL 数据库性能优化之缓存参数优化 MySQL数据库性能优化之硬件瓶颈 ...
- hive中与hbase外部表join时内存溢出(hive处理mapjoin的优化器机制)
与hbase外部表(wizad_mdm_main)进行join出现问题: CREATE TABLE wizad_mdm_dev_lmj_edition_result as select * from ...
- 七 HBase表结构设计
表结构设计之 高表 与 宽表 选择 HBase 中的表可以设计为高表(tall-narrow table) 和 宽表(flat-wide table). 高表 : ...
- MySQL基于左右值编码的树形数据库表结构设计
MySQL基于左右值编码的树形数据库表结构设计 在关系型数据库中设计树形的数据结构一直是一个十分考验开发者能力的,最常用的方案有主从表方案和继承关系(parent_id)方案.主从表方案的最大缺点 ...
随机推荐
- 利用phpmyadmin设置mysql主从同步(或者备份)
一.实现同步的原理: 在主数据库与 从数据库 之间的实现整个复制过程主要由三个线程来完成,其中两个线程(Sql线程和IO线程)在 从数据库 端,另外一个线程(IO线程)在 主数据库 端. 注意: 1. ...
- 用C语言计算圆的面积~!!!!!!!
#include <stdio.h>void main(){ int a,b,c,y,g,f; printf("圆柱底面的半径,圆柱的高"); scanf(" ...
- 如何修改WAMP数据库上传文件的大小及上传时间限制
一个文件如果几十兆的话,上传时可能出错,因为执行时间不够, 比如我遇到的ECshop的数据库文件就是 40多M 第一次执行失败. 所以索性一次性把所有东西都设置好.在php.ini(apache中的P ...
- 第一个Sprint冲刺事后诸葛报告
用户反馈:软件一般般,比较传统. 用户数量:5 团队改进建议:选择题与填空题太没有新意了,需要新的创新功能. 1.每个成员第一个sprint阶段有何需要改进? 成员 需要改进 邵家文 需要更多的技术的 ...
- NSFileHandle 、 沙箱机制 、 属性列表
1 使用NSFilehandle进行数据读写 1.1 问题 NSFileManager用于实现对文件的操作,而NSFileHandle是IOS提供的对文件内容(二进制数据)进行操作的类,例如数据的读写 ...
- static 静态代码块 动态代码块 单例
1. 共享,不属于对象,属于类,类成员变量,任何一个类的对象都有该属性,一旦被修改,则其他对象中的该属性也被更改. 2. 类中方法是static的,可以通过类名直接访问,不用new一个该类的对象. 3 ...
- Hooks
function getValType(elem) { var ret = elem.tagName.toLowerCase() return ret === "input" &a ...
- Source Xref 与 JavaDocs 学习理解
最近学习Mybatis的官方文档,看到了[项目文档]一节有很多内容没有见过,做个笔记,理解一下. 没找到java相关代码的解释,其实用下面这个php版本解释,也非常不错. What is SOURCE ...
- ztong上机3
二.实验名称:数字图像处理matlab上机 三.实验学时:2学时 四.实验目的:(详细填写) 掌握几何变换 掌握插值 理解配准的概念 五.实验内容 (1)首先自己写一个对图像进行旋转和缩放的复合变换程 ...
- 3-5 RPM包校验
1.RPM包校验 <1>rpm -V 已安装的包名 <2>选项: -V 校验制定RPM包中的文件(verify) <3>说明: <1>若没有显示任何内容 ...