大数据系列之数据库Hbase知识整理(九)RowKey的设计规范,Hbase协处理器

1.RowKey的设计规范

rowkey长度原则

rowkey是一个二进制码流,可以是任意字符串,最大长度 64kb ,实际应用中一般为10-100bytes,以byte[] 形式保存,一般设计成定长

建议越短越好,不要超过16个字节,原因如下:

前操作系统都是64位系统,内存8字节对齐,控制在16个字节,8字节的整数倍利用了操作系统的最佳特性

 

rowkey散列原则

如果rowkey按照时间戳的方式递增,不要将时间放在二进制码的前面,建议将rowkey的高位采用散列字段处理,由程序随机生成,低位放时间字段,这样将提高数据均衡分布在每个RegionServer,以实现负载均衡的几率

如果不进行列字段处理,首字段直接使用时间信息,所有的数据都会集中在一个RegionServer上,这样在数据检索的时候负载会集中在个别的RegionServer上,造成热点问题,会降低查询效率。

 

rowkey唯一原则

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

 

什么是热点

HBase中的行是按照rowkey的字典顺序排序的,这种设计优化了scan操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于scan。然而糟糕的rowkey设计是热点的源头。 热点发生在大量的client直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。设计良好的数据访问模式以使集群被充分,均衡的利用

为了避免写热点,设计rowkey使得不同行在同一个region,但是在更多数据情况下,数据应该被写入集群的多个region,而不是一个。

盐析(salt)

rowkey的前面增加随机数,具体就是给rowkey分配一个随机前缀以使得它和之前的rowkey的开头不同。分配的前缀种类数量应该和你想使用数据分散到不同的region的数量一致。加盐之后的rowkey就会根据随机生成的前缀分散到各个region上,以避免热点。

 

哈希

哈希会使同一行永远用一个前缀加盐。哈希也可以使负载分散到整个集群,但是读却是可以预测的。使用确定的哈希可以让客户端重构完整的rowkey,可以使用get操作准确获取某一个行数据。

 

反转

第三种防止热点的方法时反转固定长度或者数字格式的rowkey。这样可以使得rowkey中经常改变的部分(最没有意义的部分)放在前面。这样可以有效的随机rowkey,但是牺牲了rowkey的有序性。

反转rowkey的例子以手机号为rowkey,可以将手机号反转后的字符串作为rowkey,这样的就避免了以手机号那样比较固定开头导致热点问题。

时间戳反转

一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用Long.Max_Value - timestamp追加到key的末尾,例如[key][reverse_timestamp] ,[key] 的最新值可以通过scan [key]获得[key]的第一条记录,因为HBaserowkey是有序的,第一条记录是最后录入的数据

其他一些建议

                   尽量减少行和列的大小在HBase中,value永远和它的key一起传输的。当具体的值在系统间传输时,它的rowkey,列名,时间戳也会一起传输。如果你的rowkey和列名很大,甚至可以和具体的值相比较,那么你将会遇到一些有趣的问题。HBase storefiles中的索引(有助于随机访问)最终占据了HBase分配的大量内存,因为具体的值和它的key很大。可以增加block大小使得storefiles索引再更大的时间间隔增加,或者修改表的模式以减小rowkey和列名的大小。压缩也有助于更大的索引。列族尽可能越短越好,最好是一个字符。冗长的属性名虽然可读性好,但是更短的属性名存储在HBase中会更好。

 

2.Hbase协处理器

官方说明如下:

https://blogs.apache.org/hbase/entry/coprocessor_introduction

比较好的博文参考

https://www.cnblogs.com/liuwei6/p/6837674.html

coprocessor(协处理器)
--------------------
    批处理的,等价于存储过程或者触发器
    
    [Observer]
        观察者,类似于触发器,基于事件。发生动作时,回调相应方法。
        RegionObserver        //RegionServer区域观察者
        MasterObserver        //Master节点。
        WAlObserver            //预写日志观察者

步骤如下(下面步骤是从配置中协处理器加载模式,并不是从表描述符中协处理加载模式。所以每次配置需要重新开启集群,也可以使用从表描述符中协处理器加载,这样就不用重启集群。根据实际需求自行配置
--------------------
    1.加载
        [hbase-site.xml]
        <property>
            <name>hbase.coprocessor.region.classes</name>
            <value>coprocessor.RegionObserverExample, coprocessor.AnotherCoprocessor</value>
        </property>
        <property>
            <name>hbase.coprocessor.master.classes</name>
            <value>coprocessor.MasterObserverExample</value>
        </property>
        <property>
            <name>hbase.coprocessor.wal.classes</name>
            <value>coprocessor.WALObserverExample, bar.foo.MyWALObserver</value>
        </property>

    2.Demo:

        自定义观察者 [MyRegionObserver]
        package com.hadoop.hbase.coprocessor;

        import org.apache.hadoop.hbase.Cell;
        import org.apache.hadoop.hbase.CoprocessorEnvironment;
        import org.apache.hadoop.hbase.client.Delete;
        import org.apache.hadoop.hbase.client.Durability;
        import org.apache.hadoop.hbase.client.Get;
        import org.apache.hadoop.hbase.client.Put;
        import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
        import org.apache.hadoop.hbase.coprocessor.ObserverContext;
        import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
        import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
        import org.apache.hadoop.hbase.util.Bytes;

        import java.io.FileWriter;
        import java.io.IOException;
        import java.util.List;

        /**
         * 自定义区域观察者
         */
        public class MyRegionObserver extends BaseRegionObserver{

            private void outInfo(String str){
                try {
                    FileWriter fw = new FileWriter("/home/centos/coprocessor.txt",true);
                    fw.write(str + "\r\n");
                    fw.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
            public void start(CoprocessorEnvironment e) throws IOException {
                super.start(e);
                outInfo("MyRegionObserver.start()");
            }

            public void preOpen(ObserverContext<RegionCoprocessorEnvironment> e) throws IOException {
                super.preOpen(e);
                outInfo("MyRegionObserver.preOpen()");
            }

            public void postOpen(ObserverContext<RegionCoprocessorEnvironment> e) {
                super.postOpen(e);
                outInfo("MyRegionObserver.postOpen()");
            }

            @Override
            public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results) throws IOException {
                super.preGetOp(e, get, results);
                String rowkey = Bytes.toString(get.getRow());
                outInfo("MyRegionObserver.preGetOp() : rowkey = " + rowkey);
            }

            public void postGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, List<Cell> results) throws IOException {
                super.postGetOp(e, get, results);
                String rowkey = Bytes.toString(get.getRow());
                outInfo("MyRegionObserver.postGetOp() : rowkey = " + rowkey);
            }

            public void prePut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
                super.prePut(e, put, edit, durability);
                String rowkey = Bytes.toString(put.getRow());
                outInfo("MyRegionObserver.prePut() : rowkey = " + rowkey);
            }

            @Override
            public void postPut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
                super.postPut(e, put, edit, durability);
                String rowkey = Bytes.toString(put.getRow());
                outInfo("MyRegionObserver.postPut() : rowkey = " + rowkey);
            }

            @Override
            public void preDelete(ObserverContext<RegionCoprocessorEnvironment> e, Delete delete, WALEdit edit, Durability durability) throws IOException {
                super.preDelete(e, delete, edit, durability);
                String rowkey = Bytes.toString(delete.getRow());
                outInfo("MyRegionObserver.preDelete() : rowkey = " + rowkey);
            }

            @Override
            public void postDelete(ObserverContext<RegionCoprocessorEnvironment> e, Delete delete, WALEdit edit, Durability durability) throws IOException {
                super.postDelete(e, delete, edit, durability);
                String rowkey = Bytes.toString(delete.getRow());
                outInfo("MyRegionObserver.postDelete() : rowkey = " + rowkey);
            }
        }

    2.注册协处理器并分发到Hbase的集群
        <property>
            <name>hbase.coprocessor.region.classes</name>
            <value>com.hadoop.hbase.coprocessor.MyRegionObserver</value>
        </property>

    3.导出jar包。
    
    4.复制jar到共享目录,分发到jar到hbase集群的hbase lib目录下.
        [/soft/hbase/lib]

      5.启动hbase集群(如果Hbase集群正在使用,请重新开启).

猜你喜欢

转载自blog.csdn.net/u011444062/article/details/81155663