zoukankan      html  css  js  c++  java
  • HBASE学习笔记(五)

    一、HBase的RowKey设计原则


       1.我们知道HBase是三维有序存储的,通过RowKey(行键),ColumnKey(Column family和qualifier)和TimeStamp(时间戳),这三个维度,对HBase中的数据进行快速的定位,Hbase中的RowKey可以唯一的标识一行记录,在HBase查询的时候,有以下的几种方式:

      1)通过get的方式,指定rowkey获取唯一一条记录。

      2)通过scan的方式,设置startRow和stopRow参数的方式进行范围的匹配

      3)全表扫描,直接扫描整张中的数据。

      2.热点问题

      hbase中的行是按照rowkey的字典顺序进行排序的,这种设计优化了scan操作,可以将相关的行以及会被一起读取的行存在临近位置,便于scan。而糟糕的设计是热点的源头,热点发生在大量的client直接访问节点上一个或者极少数个节点上(访问可能是读、写或者是其他的操作),即是大量的数据都流向了集群的一个或者几个节点的上面,大量的访问会使得单个的机器超出自身的承受能力,引起性能的下降甚至性能的不可用,这也会影响同一个regionServer上的其他region ,由于主机无法服务其他主机的请求,没有达到数据的均衡分散。设计良好的数据访问模式,以使集群被充分,均衡的调用。

      3.避免热点的方法有以下几种:

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

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

        3)反转。第三种放置热点的方法是反转固定长度或者数字格式的rowKey,这样可以使得rowKey中经常改变的部分放到前面,这样可以有效的随机rowKey,但是牺牲了rowKey的有序性。反转rowKey的例子以手机号为RowKey,可以将手机号反转后的字符串作为rowKey,这样可以避免以手机号那样的比较固定的开头导致热点问题。    

        4)时间戳反转。一个常见的数据处理问题是快速的获取数据最近的版本,使用反转的时间戳为rowkey的一部分对这个问题十分有用,可以用 Long.Max_Value - timestamp 追加到 key 的末尾,例如 [key][reverse_timestamp] , [key] 的最新值可以通过 scan[key]获得[key]的第一条记录,因为 HBase 中 rowkey 是有序的,第一条记录是最后录入的数据。列族尽可能越短越好,最好是一个字符, 冗长的属性名虽然可读性好,但是更短的属性名存储在 HBase 中会更好 。

      4.RowKey的设计原则

        1.rowkey的长度设计原则:rowkey是一个二进制码流,可以是任意字符串,最大长度是64kb,在实际应用中一般为10到100bytes,以字节数组byte[]的形式保留,一般设计成定长,建议越短越好,一般不超过16个字节。

        2.rowkey的散列原则,如果rowkey按照时间戳的方式递增,不要将时间放在二进制吗的前面,建议将rowKey的高位采用散列字段来进行处理,由程序随机生成,低位放时间字段,这样讲提高数据均衡分布在每个RegionServer中,以实现负载均衡的可能性。如果不进行散列字段处理,首字段直接使用时间信息,所有的数据都会集中分布在一个regionServer上,这样在进行检索的时候,负载会集中在个别的RegionServer上,造成热点问题,降低查询效率。

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

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

      4.电信公司实例。

      电信公司rowKey设计实例:

        0.区域划分:划分100个区域,从00到99

          CallerId +  201703 :hashcode % 100  = 00 -99 

        1.rowKey设计:rno  +  callerid  +  calltime[201702011212]  +  calleeid  +  duration ,这个地方注意:rno 等于callerid + calltime 的一部分

         2.通话记录:

          1)创建表:$hbase>create 'ns1:calllogs','f1'

          2)创建单元测试。向表中添加数据。

    package com.it18zhang.hbaseDemo;
    
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.hbase.HBaseConfiguration;
    import org.apache.hadoop.hbase.TableName;
    import org.apache.hadoop.hbase.client.Connection;
    import org.apache.hadoop.hbase.client.ConnectionFactory;
    import org.apache.hadoop.hbase.client.Put;
    import org.apache.hadoop.hbase.client.Table;
    import org.apache.hadoop.hbase.util.Bytes;
    import org.junit.Test;
    
    import java.io.IOException;
    import java.text.DecimalFormat;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * 通话日志
     * rowKey的设计
     * 区号:主叫或被叫,时间,方向0-主叫,1-被叫
     *xx,callerid ,time,direction ,calleid,duration
     *
     */
    public class TestCalllogs {
        @Test
        public  void put() throws Exception {
            Configuration conf = HBaseConfiguration.create();
            Connection conn = ConnectionFactory.createConnection(conf);
            TableName tname = TableName.valueOf("ns1:calllogs");
            Table table = conn.getTable(tname);
            String callerId ="13845456767";
            String calleeId ="13989897878";
            SimpleDateFormat sdf = new SimpleDateFormat();
            sdf.applyPattern("yyyymmddhhmmss");
            String callTime = sdf.format(new Date());
            int duration =100 ;
            DecimalFormat df= new DecimalFormat("00000");
            String dura = df.format(duration);
            //区域00-99
            int hash =(callerId+ callTime.substring(0,6)).hashCode() %100;
            hash = (hash & Integer.MAX_VALUE)%100;
            //hash区域号
            DecimalFormat dff = new DecimalFormat();
            dff.applyPattern("00");
            String regNo = dff.format(hash);
            //拼接rowkey
            String rowKey = regNo+""+","+callerId+","+callTime+","+"0"+calleeId+","+dura;
            byte[] rowid = Bytes.toBytes("row3");
            Put put = new Put(rowid);
            put.addColumn(Bytes.toBytes("f1"),Bytes.toBytes("callerPos"),Bytes.toBytes("河北"));
            put.addColumn(Bytes.toBytes("f1"),Bytes.toBytes("callerPos"),Bytes.toBytes("河南"));
            table.put(put);
        }
    }

        3)创建协处理器。

      

    public class CalleeLogRegionObserver extends BaseRegionObserver{
    
                public void postPut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, Durability durability) throws IOException {
                    super.postPut(e, put, edit, durability);
                    //
                    TableName callLogs = TableName.valueOf("calllogs");
                    //得到当前的TableName对象
                    TableName tableName = e.getEnvironment().getRegion().getRegionInfo().getTable();
                    if(!callLogs.equals(tableName)){
                        return  ;
                    }
    
                    //得到主叫的rowkey
                    //xx , callerid , time ,  direction, calleid  ,duration
                    //被叫:calleid,time,
    
                    String rowkey = Bytes.toString(put.getRow());
                    String[] arr = rowkey.split(",");
    
                    String hash = Util.getRegNo(arr[4],arr[2]);
                    //hash
    
                    String newRowKey = hash + "," + arr[4] + "," + arr[2] + ",1," + arr[1] + "," +  arr[5] ;
                    Put newPut = new Put(Bytes.toBytes(newRowKey));
    
                    Table t = e.getEnvironment().getTable(tableName);
    
                    t.put(newPut);
                }
            }
      4.配置hbase-site.xml并分发

        <property>
          <name>hbase.coprocessor.region.classes</name>
          <value>it18zhang.hbaseDemo.coprocessor1.CalleeLogRegionObserver</value>

        </property>

          

  • 相关阅读:
    js 截取指定的字符串
    WebSocket实例 Vue中使用websoket
    Vue 水半球样式、圆形水进度条、在线编辑
    Elasticsearch学习笔记3 -- 文档操作
    Elasticsearch学习笔记2 -- 索引库的操作
    Elasticsearch学习笔记1 -- 安装elasticsearch
    Docker学习笔记5 -- Docker-compose
    Docker学习笔记4 -- Dockerfile
    Docker学习笔记3 -- 数据卷
    Docker学习笔记2 -- 常用命令
  • 原文地址:https://www.cnblogs.com/bigdata-stone/p/9374024.html
Copyright © 2011-2022 走看看