HBase优化

文章目录

  • HBase优化
    • 预分区
      • 手动设定预分区
      • 生成十六进制序列预分区
      • 按照文件中设置的规则预分区
      • 使用JavaAPI创建预分区
    • RowKey设计
      • rowkey常用的设计方案
        • 生成随机数,hash,散列值
        • 字符串反转
        • 字符串拼接
      • 案例分析
    • 内存优化
    • 基础优化
      • ZooKeeper会话超时时间
      • 设置RPC监听数量
      • 手动控制Major Compaction
      • 优化HStore文件大小
      • 优化HBase客户端缓存
      • 指定scan.next扫描HBase所获取的行数
      • BlockCache占用RegionServer堆内存的比例
      • MemStore占用RegionServer堆内存的比例
    • 底层结构

HBase优化

曾为梦想仗剑走天涯,如今这个那个得优化。
还想围观群众吃个瓜?赶紧关注我博涨文化!

预分区

预分区和RowKey设计是搭配使用的。
预分区的分区键是什么样的,RowKey就得是什么样的,他俩得配合使用才行。比如,都是十六进制。

如果不使用预分区,那么等数据量大了就会进行自动分裂。
自动分裂有什么坏处呢?

  • 很长一段时间内,只有很少的Region,那么并发度就是个问题。
  • 自动分裂时,Region的分界点不能确定。
  • 可能导致:某个Region负载过重,也就是Region的热点问题。

每一个Region维护着startKey和endKey,如果加入的数据符合某个Region维护的RowKey范围,则该数据交给这个Region维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高HBase性能。

手动设定预分区

hbase(main):092:0> create 'emp1', 'info', SPLITS => ['1000','2000','3000','4000']
Created table emp1
Took 1.3647 seconds                                                                                                                   
=> Hbase::Table - emp1
  • 在hadoop131:16010打开HBase集群web界面,拉到Tables栏,然后点击emp1表,查看Region的分区情况。在Table Regions栏可以看到:

HBase优化_第1张图片

这里,设置了5个分区:(-∞, 1000), [1000, 2000), [2000, 3000), [3000, 4000), [4000, +∞)

生成十六进制序列预分区

hbase(main):094:0> create 'emp2', 'info', {NUMREGIONS => 5, SPLITALGO => 'HexStringSplit'}
Created table emp2
Took 1.3252 seconds                                                                                                                   
=> Hbase::Table - emp2

HBase优化_第2张图片

  • 这里的分区键为什么是长这个样子的呢?

因为十六进制最大为FFFFFFFF,那么均分5分,就是这个样子了呢。

按照文件中设置的规则预分区

创建splits.txt文件:

aaaa
bbbb
cccc
dddd

然后执行:

hbase(main):098:0> create 'emp3', 'info', SPLITS_FILE => 'splits.txt'
  • SPLITS_FILE会自动对文件内的key进行排序

使用JavaAPI创建预分区

    public static void preRegion(String nameSpace, String tableName, String... families) throws IOException {
        if(families.length <= 0) {
            System.out.println("至少有一个列族");
            return;
        }

        Admin admin = connection.getAdmin();
        try {
            if(admin.tableExists(TableName.valueOf(nameSpace, tableName))) {
                System.out.println(nameSpace + ":" + tableName + "已存在");
                return;
            }

            TableDescriptorBuilder builder = TableDescriptorBuilder
                    .newBuilder(TableName.valueOf(nameSpace, tableName));

            for(String family: families) {
                ColumnFamilyDescriptorBuilder cfBuilder = ColumnFamilyDescriptorBuilder
                        .newBuilder(Bytes.toBytes(family));
                builder.setColumnFamily(cfBuilder.build());
            }

            TableDescriptor descriptor = builder.build();

            byte[][] splitKeys = new byte[4][];
            for(int i = 0; i < 4; i ++) {
                splitKeys[i] = Bytes.toBytes(String.valueOf((i + 1) * 1000));
            }

            admin.createTable(descriptor, splitKeys);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            admin.close();
        }
    }

RowKey设计

一条数据的唯一标识就是rowkey,那么这条数据存储于哪个分区,取决于rowkey处于哪个预分区的区间内。设计rowkey的主要目的,就是要让数据均匀的分布于所有的Region中,在一定程度上防止数据倾斜。

rowkey常用的设计方案

生成随机数,hash,散列值

比如:

原本rowKey为1001的,SHA1后变成:dd01903921ea24941c26a48f2cec24e0bb0e8cc7
原本rowKey为3001的,SHA1后变成:49042c54de64a1e9bf0b33e00245660ef92dc7bd
原本rowKey为5001的,SHA1后变成:7b61dec07e02c188790670af43e717f0f46e8913
  • 在做此操作之前,一般我们会选择从数据集中抽取样本,来决定什么样的rowkey来Hash后作为每个分区的临界值。

字符串反转

20170524000001转成10000042507102
20170524000002转成20000042507102
  • 这样也可以在一定程度上散列逐步put进来的数据。

字符串拼接

20170524000001_a12e
20170524000001_93i7

案例分析

网站首页访问记录实时存入hbase,统计网站首页每分钟的访问次数
注意:rowkey唯一性 + 读数据的时候数据需要在一块那么写的时候就写在一块

思路:满足业务 -> user_id, timestamp:

rowKey:"yyyyMMddHHmm".hashCode()%5_yyyyMMddHHmmssSSSS_user_id

regionKey:
-∞, 1
1, 2
2, 3
3, 4
4, +∞
  • | 竖线是assic码较大的字符,可以用于过滤匹配rowkey。
  • 预分区个数:每个RegionServer分5~10个Region
  • rowkey设计尽可能短一些
  • 列族名和列名尽可能的短:一般情况一个字符

内存优化

  • HBase操作过程中需要大量的内存开销,毕竟Table是可以缓存在内存中的。
  • 但是不建议分配非常大的堆内存,因为GC过程持续太久会导致RegionServer处于长期不可用状态。
  • 一般16~36G内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。

基础优化

ZooKeeper会话超时时间

hbase-site.xml
属性:zookeeper.session.timeout
解释:默认值为90000毫秒(90s)。当某个RegionServer挂掉,90s之后Master才能觉察到。可适当减小此值,以加快Master响应,可调整至60000毫秒。

设置RPC监听数量

hbase-site.xml
属性:hbase.regionserver.handler.count
解释:默认值为30,用于指定RPC监听的数量,可以根据客户端的请求数进行调整,读写请求较多时,增加此值。

  • 一般调成regionServer的cup核数的两倍

手动控制Major Compaction

hbase-site.xml
属性:hbase.hregion.majorcompaction
解释:默认值为604800000秒(7天),Major Compaction的周期,若关闭自动Major Compaction,可将其设为0

优化HStore文件大小

hbase-site.xml
属性:hbase.hregion.max.filesize
解释:默认值10737418240(10GB),如果需要运行HBase的MR任务,可以减小此值。因为一个region对应一个map任务,如果单个region过大,会导致map任务执行时间过长。该值的意思就是,如果HFile的大小达到这个数值,则这个region会被切分为两个HFile。

优化HBase客户端缓存

hbase-site.xml
属性:hbase.client.write.buffer
解释:默认值2097152bytes(2M)用于指定HBase客户端缓存,增大该值可以减少RPC调用次数,但是会消耗更多内存。一般我们需要设定一定的缓存大小,以达到减少RPC次数的目的。

指定scan.next扫描HBase所获取的行数

hbase-site.xml
属性:hbase.client.scanner.caching
解释:用于指定scan.next方法获取的默认行数,值越大,消耗内存越大。

BlockCache占用RegionServer堆内存的比例

hbase-site.xml
属性:hfile.block.cache.size
解释:默认0.4,读请求比较多的情况下,可适当调大

MemStore占用RegionServer堆内存的比例

hbase-site.xml
属性:hbase.regionserver.global.memstore.size
解释:默认0.4,写请求较多的情况下,可适当调大

底层结构

Log-Structured Merge Tree 存储引擎
lsm tree

你可能感兴趣的:(HBase)