HBase表设计及优化点

1 在创建表的时候指定分区

​默认情况下,在创建HBase表的时候会自动创建一个region分区,当导入数据的时候,所有的HBase客户端都向这一个region写数据,直到这个region足够大了才进行切分。一种可以加快批量写入速度的方法是通过预先创建一些空的regions,这样当数据写入HBase时,会按照region分区情况,在集群内做数据的负载均衡。

  • 实现方式是使用admin对象的切分策略
byte[] startKey = ...;      // your lowest key
byte[] endKey = ...;        // your highest key
int numberOfRegions = ...;  // number of regions to create
admin.createTable(table, startKey, endKey, numberOfRegions);
  • 用户自定义切片
byte[][] splits = ...;   // create your own splits
/*
byte[][] splits = new byte[][] { Bytes.toBytes("100"),
                Bytes.toBytes("200"), Bytes.toBytes("400"),
                Bytes.toBytes("500") };
*/
admin.createTable(table, splits);

2 Rowkey设计

​HBase中rowkey用来检索表中的记录,支持以下三种方式:

​1. 通过单个rowkey访问:即按照某个rowkey键值进行get操作;

​2. 通过rowkey的range进行scan:即通过设置startRowKey和endRowKey,在这个范围内进行扫描;

​3. 全表扫描:即直接扫描整张表中所有行记录

​在HBase中,rowkey可以是任意字符串,最大长度64KB,实际应用中一般为10~100字节,存为byte[]字节数组,一般设计成定长的。

rowkey是按照字典序存储,因此,设计rowkey时,要充分利用这个排序特点,将经常一起读取的数据存储到一块,将最近可能会被访问的数据放在一块。

​Rowkey设计原则:

​1. 越短越好,提高效率

​* 数据的持久化文件HFile中是按照KeyValue存储的,如果rowkey过长,比如超过100字节,1000万行数据,单单是存储rowkey的数据就要占用10亿个字节,将近1G数据,这样会影响HFile的存储效率。

​* HBase中包含缓存机制,每次会将查询的结果暂时缓存到HBase的内存中,如果rowkey字段过长,内存的利用率就会降低,系统不能缓存更多的数据,这样会降低检索效率。

​2. 散列原则——实现负载均衡

​如果rowkey是按时间戳的方式递增,不要将时间放在二进制码的前面,建议将rowkey的高位作为散列字段,由程序循环生成,低位放时间字段,这样将提高数据均衡分布在每个regionserver实现负载均衡的几率。如果没有散列字段,首字段直接是时间信息将产生所有新数据都在一个 regionServer上堆积的热点现象,这样在做数据检索的时候负载将会集中在个别regionServer,降低查询效率。

​* 加盐:添加随机值

​* hash:采用md5散列算法取前4位做前缀

​* 反转:将手机号反转(尽量将具有随机性的字符串放在前面,这样便于Hash

​3. 唯一原则–字典序排序存储

​必须在设计上保证其唯一性,rowkey是按照字典顺序排序存储的,因此,设计rowkey的时候,要充分利用这个排序的特点,将经常读取的数据存储到一块,将最近可能会被访问的数据放到一块。

3 列族的设计

​不要在一张表里定义太多的column family。目前Hbase并不能很好的处理超过2~3column family的表。因为某个column familyflush的时候,它邻近的column family也会因关联效应被触发flush,最终导致系统产生更多的I/O

​原因:

​1. 当开始向hbase中插入数据的时候,数据会首先写入到memstore,而memstore是一个内存结构,每个列族对应一个memstore,当包含更多的列族的时候,会导致存在多个memstore,每一个memstoreflush的时候会对应一个hfile的文件,因此会产生很多的hfile文件,更加严重的是,flush操作时region级别,当region中的某个memstoreflush的时候,同一个region的其他memstore也会进行flush操作,当某一张表拥有很多列族的时候,且列族之间的数据分布不均匀的时候,会产生更多的磁盘文件;

​2. 当hbase表的某个region过大,会被拆分成两个,如果我们有多个列族,且这些列族之间的数据量相差悬殊的时候,regionsplit操作会导致原本数据量小的文件被进一步的拆分,而产生更多的小文件;

​3. 与Flush操作一样,目前HBaseCompaction操作也是Region级别的,过多的列族也会产生不必要的I/O;

​4. HDFS其实对一个目录下的文件数有限制的(dfs.namenode.fs-limits.max-directory-items)。如果我们有N个列族,MRegion,那么我们持久化到HDFS至少会产生N*M个文件,而每个列族对应底层的HFile文件往往不止一个,我们假设为K个,那么最终表在HDFS目录下的文件数将是N*M*K,这可能会操作HDFS的限制。

4 in memory

hbaseLRU缓存基础之上采用了分层设计,整个blockcache分成了三个部分,分别是singlemultiinMemory

​三者区别如下:

  • single:如果一个block第一次被访问,放在该优先队列中;
  • multi:如果一个block被多次访问,则从single队列转移到multi队列
  • inMemory:优先级最高,常驻cache,因此一般只有hbase系统的元数据,如meta表之类的才会放到inMemory队列中。

5 保存版本数

​创建表的时候,可以通过ColumnFamilyDescriptorBuilder.setMaxVersions(int maxVersions)设置表中数据的最大版本,如果只需要保存最新版本的数据,那么可以设置setMaxVersions(1),保留更多的版本信息会占用更多的存储空间。

6 设置最大保存时间

​创建表的时候,可以通过ColumnFamilyDescriptorBuilder.setTimeToLive(int timeToLive)设置表中数据的存储生命期,过期数据将自动被删除,例如如果只需要存储最近两天的数据,那么可以设置setTimeToLive(2 * 24 * 60 * 60)

7 合并操作

​在HBase中,数据在更新时首先写入WAL 日志(HLog)和内存(MemStore)中,MemStore中的数据是排序的,当MemStore累计到一定阈值(64MB)时,就会创建一个新的MemStore,并且将老的MemStore添加到flush队列,由单独的线程flush到磁盘上,成为一个StoreFile。于此同时, 系统会在zookeeper中记录一个redo point,表示这个时刻之前的变更已经持久化了。

StoreFile是只读的,一旦创建后就不可以再修改。因此Hbase的更新其实是不断追加的操作。当一个Store中的StoreFile达到一定的阈值后,就会进行一次合并,将对同一个key的修改合并到一起,形成一个大的StoreFile,当StoreFile的大小达到一定阈值后,又会对StoreFile进行分割,等分为两个StoreFile

​由于对表的更新是不断追加的,处理读请求时,需要访问Store中全部的StoreFileMemStore,将它们按照row key进行合并,由于StoreFileMemStore都是经过排序的,并且StoreFile带有内存中索引,通常合并过程还是比较快的。

实际应用中,可以考虑必要时手动进行major compact,将同一个row key的修改进行合并形成一个大的StoreFile。同时,可以将StoreFile设置大些,减少split的发生。

hbase为了防止小文件(被刷到磁盘的menstore)过多,以保证保证查询效率,hbase需要在必要的时候将这些小的store file合并成相对较大的store file,这个过程就称之为compaction。在hbase中,主要存在两种类型的compactionminor compactionmajor compaction

​1. minor compaction: 的是较小、很少文件的合并。minor compaction的运行机制要复杂一些,它由一下几个参数共同决定:

hbase.hstore.compaction.min: 默认值为3,表示至少需要三个满足条件的store file时,minor compaction才会启动

hbase.hstore.compaction.max: 默认值为10,表示一次minor compaction中最多选取10store file

hbase.hstore.compaction.min.size: 表示文件大小小于该值的store file一定会加入到minor compactionstore file

hbase.hstore.compaction.max.size: 表示文件大小大于该值的store file一定不会被添加到minor compaction

hbase.hstore.compaction.ratio: 将StoreFile按照文件年龄排序,minor compaction总是从older store file开始选择,如果该文件的size小于后面hbase.hstore.compaction.maxstore file size之和乘以ratio的值,那么该store file将加入到minor compaction中。如果满足minor compaction条件的文件数量大于hbase.hstore.compaction.min,才会启动。

​2. major compaction的功能是将所有的store file合并成一个,触发major compaction的可能条件有:

major_compact命令

majorCompact() API

region server运行

hbase.hregion.majorcompaction默认为24小时

hbase.hregion.majorcompaction.jetter默认值为0.2为防止region server在同一时间进行major compaction

hbase.hregion.majorcompaction.jetter参数的作用是:对参数hbase.hregion.majorcompaction规定的值起到浮动的作用,假如两个参数都为默认值240.2,那么major compact最终使用的数值为19.2~28.8这个范围。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值