精华内容
下载资源
问答
  • hbase热点问题解决(预分区

    万次阅读 多人点赞 2018-06-30 22:08:12
    一、出现热点问题原因 1、hbase的中的数据是按照字典序排序的,当大量连续的rowkey集中写在个别的region,各个region之间... 3、创建表已经提前预分区,但是设计的rowkey没有规律可循,设计的rowkey应该由regionN...

    一、出现热点问题原因

           1、hbase的中的数据是按照字典序排序的,当大量连续的rowkey集中写在个别的region,各个region之间数据分布不均衡;

           2、创建表时没有提前预分区,创建的表默认只有一个region,大量的数据写入当前region;

           3、创建表已经提前预分区,但是设计的rowkey没有规律可循,设计的rowkey应该由regionNo+messageId组成。

    二、如何解决热点问题       

           解决这个问题,关键是要设计出可以让数据分布均匀的rowkey,与关系型数据库一样,rowkey是用来检索记录的主键。访问hbase table中的行,rowkey 可以是任意字符串(最大长度 是 64KB,实际应用中长度一般为 10-100bytes),在hbase内部,rowkey保存为字节数组,存储时,数据按照rowkey的字典序排序存储。

    创建表命令:

    create 'testTable',{NAME => 'cf', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW', REPLICATION_SCOPE=> '0', VERSIONS => '1', COMPRESSION => 'snappy', MIN_VERSIONS =>'0', TTL => '15552000', KEEP_DELETED_CELLS => 'false', BLOCKSIZE =>'65536', IN_MEMORY => 'false', BLOCKCACHE => 'true', METADATA =>{'ENCODE_ON_DISK' => 'true'}},{SPLITS_FILE=>'/app/soft/test/region.txt'}

    region.txt内容:

    我这里预分10个region,执行命令之后,在hbase的console中可以看到以下信息,说明预分区ok了!!!

     1、第一种设计rowkey方式:随机数+messageId,如果想让最近的数据快速get到,可以将时间戳加上,我这里的region是0001|到0009|开头的,因为hbase的数据是字典序排序的,所以如果我生成的 rowkey=0002rer4343343422,则当前这条数据就会保存到0001|~0002|这个region里,因为我的messageId都是字母+数字,“|”的ASCII值大于字母、数字。

    生成regionNo的工具类:RegionUtils

    package com.cn.dl;
    
    import java.util.Random;
    
    /**
     * Created by Tiger on 2018/4/18.
     */
    public class RegionUtils {
    
        //十个预分区
        private static final int REGION_NUM = 10;
    
        //存放regionNo:0001,0002,...0009,0010
        private static final String[] REGION_ARRAY = new String[REGION_NUM];
    
        static {
            initRegionArray();
        }
        /**
         * 生成regionNo
         * */
        private static void initRegionArray(){
            for(int i=1; i<=REGION_NUM; i++){
                String regionNo = String.valueOf(i);
                while (regionNo.length() < 4){
                    regionNo = "0" + regionNo;
                }
                REGION_ARRAY[i-1] = regionNo;
            }
        }
        /**
         * 随机获取regionNo
         * @return  regionNo
         * */
        public static String getRegionNo(){
            Random random = new Random();
            return REGION_ARRAY[random.nextInt(10)];
        }
    
        public static void main(String[] args) {
            int i= 0;
            while (i < 100){
                System.out.println(getRegionNo());
                i++;
            }
        }
    }
    

      生成rowKey:

    // TODO: 2018/12/18 只是一个生成rowKey的案例
        public void execute(Tuple tuple) {
            try {
                JSONObject json = JSONObject.parseObject(tuple.getStringByField("messageSpout"));
                String messageId = json.getString("messageId");
                // TODO: 2018/12/18 生成rowKey:regionNo+时间戳+messageId ,加上时间戳在hbase中可以提高查询效率
                String rowKey = RegionUtils.getRegionNo() + System.currentTimeMillis() + messageId;
                json.put("rowKey",rowKey);
                System.out.println(json.toJSONString());
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                collector.ack(tuple);
            }
        }

      打印结果rowkey=regionNo+时间戳+messageId,前缀是随机的

    {"name":"name2","messageId":"b998a8dfc05a4a819284213d4e727a85","age":12,"rowKey":"00041545105001972b998a8dfc05a4a819284213d4e727a85"}
    {"name":"name3","messageId":"799affbf346641e8a00bfeee78ffcdb4","age":13,"rowKey":"00031545105002973799affbf346641e8a00bfeee78ffcdb4"}
    {"name":"name4","messageId":"bbdf16b9a12b4fa09b060402f9522fed","age":14,"rowKey":"00051545105003973bbdf16b9a12b4fa09b060402f9522fed"}
    {"name":"name5","messageId":"03c119868cd742459464df53c3827147","age":15,"rowKey":"0009154510500497403c119868cd742459464df53c3827147"}
    {"name":"name6","messageId":"84c682681cdc4ac09ad3d270741074d3","age":16,"rowKey":"0002154510500597484c682681cdc4ac09ad3d270741074d3"}
    {"name":"name7","messageId":"aecbd65f3f434452ab4a924d8e42b947","age":17,"rowKey":"00091545105006976aecbd65f3f434452ab4a924d8e42b947"}
    {"name":"name8","messageId":"3bcb23e414e5450898b6b0eefff4d80a","age":18,"rowKey":"000315451050079783bcb23e414e5450898b6b0eefff4d80a"}
    {"name":"name9","messageId":"40be62bfcea24ea799ae6f191241c5e8","age":19,"rowKey":"0002154510500897840be62bfcea24ea799ae6f191241c5e8"}
    {"name":"name10","messageId":"94c220cd10d141c89cf08e61d0a48e7f","age":20,"rowKey":"0007154510500997894c220cd10d141c89cf08e61d0a48e7f"}
    {"name":"name11","messageId":"0796f735b1ba43beb7b15d63c4fd4ec8","age":21,"rowKey":"000515451050109780796f735b1ba43beb7b15d63c4fd4ec8"}
    {"name":"name12","messageId":"05ac8417e52443f48bf2c56879b3e2c6","age":22,"rowKey":"0009154510501197805ac8417e52443f48bf2c56879b3e2c6"}
    {"name":"name13","messageId":"b87484b633b747ba8320cdf69334459b","age":23,"rowKey":"00101545105012978b87484b633b747ba8320cdf69334459b"}
    {"name":"name14","messageId":"84c6daf1cdfd4c0f977a8742ee528977","age":24,"rowKey":"0005154510501397984c6daf1cdfd4c0f977a8742ee528977"}
    {"name":"name15","messageId":"8e01e5c53d024de18507ed2a98e38519","age":25,"rowKey":"000415451050149808e01e5c53d024de18507ed2a98e38519"}
    {"name":"name16","messageId":"48939394581946e881b91e797659d6ca","age":26,"rowKey":"0005154510501598248939394581946e881b91e797659d6ca"}
    {"name":"name17","messageId":"b5c2024c721642a5a2b4cc8682c7cd40","age":27,"rowKey":"00021545105016981b5c2024c721642a5a2b4cc8682c7cd40"}
    {"name":"name18","messageId":"4efb10fc351947f6a78761e7b3bf1783","age":28,"rowKey":"000215451050179824efb10fc351947f6a78761e7b3bf1783"}
    {"name":"name19","messageId":"a4a27bafd5f749d0b08d9eb832120737","age":29,"rowKey":"00041545105018983a4a27bafd5f749d0b08d9eb832120737"}
    {"name":"name20","messageId":"1d90758cbcc2495197b2ef0a05e58610","age":30,"rowKey":"000815451050199831d90758cbcc2495197b2ef0a05e58610"}
    {"name":"name21","messageId":"47ae3fb0e3914496b75445df92d0a133","age":31,"rowKey":"0002154510502098347ae3fb0e3914496b75445df92d0a133"}
    {"name":"name22","messageId":"419dccbeb2b74484997bd5373f8347af","age":32,"rowKey":"00071545105021982419dccbeb2b74484997bd5373f8347af"}
    {"name":"name23","messageId":"51e38da4c9c74542bcd38be704ff3fec","age":33,"rowKey":"0005154510502298351e38da4c9c74542bcd38be704ff3fec"}
    {"name":"name24","messageId":"e99e783220d14f63be1f967f82bd69fb","age":34,"rowKey":"00081545105023983e99e783220d14f63be1f967f82bd69fb"}
    {"name":"name25","messageId":"0e15f181464146598dfe44976676c706","age":35,"rowKey":"000915451050249840e15f181464146598dfe44976676c706"}
    {"name":"name26","messageId":"d0d8c0e939b44a688915714b85d61b1f","age":36,"rowKey":"00101545105025985d0d8c0e939b44a688915714b85d61b1f"}
    {"name":"name27","messageId":"4a7d3404871a4137b9db4e24209f1346","age":37,"rowKey":"000515451050269844a7d3404871a4137b9db4e24209f1346"}
    {"name":"name28","messageId":"51a5f39f032241fcaa6e5956a9ddb474","age":38,"rowKey":"0002154510502798651a5f39f032241fcaa6e5956a9ddb474"}
    {"name":"name29","messageId":"c54ec6f46fa047fdae6187afad55e8f0","age":39,"rowKey":"00011545105028986c54ec6f46fa047fdae6187afad55e8f0"}
    {"name":"name30","messageId":"076322780dae4748997006d53aa246c1","age":40,"rowKey":"00021545105029987076322780dae4748997006d53aa246c1"}
    {"name":"name31","messageId":"563c258840a84f12ad1a5341381b163c","age":41,"rowKey":"00101545105030986563c258840a84f12ad1a5341381b163c"}
    {"name":"name32","messageId":"377f3fbe63634fc4aa93b1737dd462da","age":42,"rowKey":"00051545105031988377f3fbe63634fc4aa93b1737dd462da"}
    {"name":"name33","messageId":"5c1af0f26cc94d0b85d0fbca617e57ad","age":43,"rowKey":"000715451050329895c1af0f26cc94d0b85d0fbca617e57ad"}
    {"name":"name34","messageId":"2d3050a0b3dd42df97ef87d45dbc0d26","age":44,"rowKey":"000715451050339902d3050a0b3dd42df97ef87d45dbc0d26"}
    {"name":"name35","messageId":"526401e1f5ab45aeb95421e0ac200e4b","age":45,"rowKey":"00041545105034990526401e1f5ab45aeb95421e0ac200e4b"}

    总结:

           这种设计的rowkey可以解决热点问题,但是要建立关联表,比如将rowkey保存到数据库或者nosql数据库中,因为前面的regionNo是随机的,不知道 对应数据在hbase的rowkey是多少;同一批数据,因为这个regionNo是随机的,所以要到多个region中get数据,不能使用startkey和endkey去get数据。

              2、第二种设计rowkey的方式:通过messageId映射regionNo,这样既可以让数据均匀分布到各个region中,同时可以根据startkey和endkey可以get到同一批数据,messageId映射regionNo,使用一致性hash算法解决,一致性哈希算法在1997年由麻省理工学院的Karger等人在解决分布式Cache中提出的,设计目标是为了解决因特网中的热点(Hot spot)问题,

    参考:

    (https://baike.baidu.com/item/%E4%B8%80%E8%87%B4%E6%80%A7%E5%93%88%E5%B8%8C/2460889?fr=aladdin)(https://www.cnblogs.com/lpfuture/p/5796398.html)

    public class ConsistentHash<T> implements Serializable{
        private static final long serialVersionUID = 1L;
        private  final HashFunction hashFunction;
        //每个regions的虚拟节点个数
        private final int numberOfReplicas;
        //存储虚拟节点的hash值到真实节点的映射
        private final SortedMap<Long, String> circle = new TreeMap<Long, String>();
        public ConsistentHash(HashFunction hashFunction, int numberOfReplicas, Collection<String> nodes) {
            this.hashFunction = hashFunction;
            this.numberOfReplicas = numberOfReplicas;
            for (String node : nodes){
                add(node);
            }
        }
        /**
         * 添加节点
         * @param node
         * @see java.util.TreeMap
         * */
        public void add(String node) {
            for (int i = 0; i < numberOfReplicas; i++)
                 /*
                  * 不同的虚拟节点(i不同)有不同的hash值,但都对应同一个实际机器node
                  * 虚拟node一般是均衡分布在环上的,数据存储在顺时针方向的虚拟node上
                  */
                circle.put(hashFunction.getHashValue(node.toString() + i), node);
        }
        /**
         * 移除节点
         * @param node
         * @see java.util.TreeMap
         * */
        public void remove(String node) {
            for (int i = 0; i < numberOfReplicas; i++)
                circle.remove(hashFunction.getHashValue(node.toString() + i));
        }
        /**
         * 获取对应key的hashcode值,然后根据hashcode获取当前数据储存的真实节点
         * */
        public String get(Object key) {
            if (circle.isEmpty())
                return null;
            //获取对应key的hashcode值
            long hash = hashFunction.getHashValue((String) key);
            //数据映射在两台虚拟机器所在环之间,就需要按顺时针方向寻找机器
            if (!circle.containsKey(hash)) {
                SortedMap<Long, String> tailMap = circle.tailMap(hash);
                hash = tailMap.isEmpty() ? circle.firstKey() : tailMap.firstKey();
            }
            return circle.get(hash);
        }
        /**
         * 获取hash环节点大小
         * @return
         * */
        public long getSize() {
            return circle.size();
        }
        /**
         * 获取double类型数据的小数位后四位小数
         * @param num
         * @return
         * */
        public String getDecimalPoint(double num){
            DecimalFormat df = new DecimalFormat("0.0000");
            return df.format(num);
        }
    }
    public class HashFunction implements Serializable{
        private static final long serialVersionUID = 1L;
        /**
         * 获取对应字符串的hashCode值
         * @param key
         * @return
         * */
        public  long getHashValue(String key) {
            final int p = 1677761999;
            int hash = (int) 216613626111L;
            for (int i = 0; i < key.length(); i++)
                hash = (hash ^ key.charAt(i)) * p;
            hash += hash << 13;
            hash ^= hash >> 8;
            hash += hash << 3;
            hash ^= hash >> 18;
            hash += hash << 5;
            // 如果算出来的值为负数则取其绝对值
            if (hash < 0)
                hash = Math.abs(hash);
            return hash;
        }
    }

         我目前满意第二种方式,然后在es中建立关联表,get数据时,先在es中get到rowkey,然后在hbase中获取数据,这个根据自己的业务设计。

    写的内容有问题,欢迎来吐槽,我会及时修改,谢谢!

    展开全文
  • Hbase的预分区与Hbase的rowKey的设计原则 文章目录Hbase的预分区与Hbase的rowKey的设计原则1、HBase的预分区1.1、为何要预分区?1.2、如何预分区?1.3、如何设定预分区?1.3.1、手动指定预分区1.3.2、使用16进制...

    Hbase的预分区与Hbase的rowKey的设计原则

    在这里插入图片描述

    1、HBase的预分区

    1.1、为何要预分区?

    • 增加数据读写效率
    • 负载均衡,防止数据倾斜
    • 方便集群容灾调度region
    • 优化Map数量

    1.2、如何预分区?

    每一个region维护着startRow与endRowKey,如果加入的数据符合某个region维护的rowKey范围,则该数据交给这个region维护。

    1.3、如何设定预分区?

    1.3.1、手动指定预分区
    hbase(main):001:0> create 'staff','info','partition1',SPLITS => ['1000','2000','3000','4000']
    

    完成后如图:
    在这里插入图片描述

    1.3.2、使用16进制算法生成预分区
    hbase(main):003:0> create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
    

    完成后如图:
    在这里插入图片描述

    1.3.3、分区规则创建于文件中

    创建splits.txt文件内容如下:

    cd /export/servers/
    vim splits.txt
    

    编辑内容:

    aaaa
    bbbb
    cccc
    dddd
    

    然后执行:

    hbase(main):004:0> create 'staff3','partition2',SPLITS_FILE => '/export/servers/splits.txt'
    

    成功后如图:
    在这里插入图片描述

    1.3.4、使用JavaAPI创建预分区

    Java代码如下:

    /**
         * 通过javaAPI进行HBase的表的创建以及预分区操作
         */
        @Test
        public void hbaseSplit() throws IOException {
            //获取连接
            Configuration configuration = HBaseConfiguration.create();
            configuration.set("hbase.zookeeper.quorum", "node01:2181,node02:2181,node03:2181");
            Connection connection = ConnectionFactory.createConnection(configuration);
            Admin admin = connection.getAdmin();
            //自定义算法,产生一系列Hash散列值存储在二维数组中
            byte[][] splitKeys = {{1,2,3,4,5},{'a','b','c','d','e'}};
    
    
            //通过HTableDescriptor来实现我们表的参数设置,包括表名,列族等等
            HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf("stuff4"));
            //添加列族
            hTableDescriptor.addFamily(new HColumnDescriptor("f1"));
            //添加列族
            hTableDescriptor.addFamily(new HColumnDescriptor("f2"));
            admin.createTable(hTableDescriptor,splitKeys);
            admin.close();
    
        }
    

    2、HBase的rowKey设计技巧

    HBase是三维有序存储的,通过rowkey(行键),column key(column family和qualifier)和TimeStamp(时间戳)这个三个维度可以对HBase中的数据进行快速定位。
    HBase中rowkey可以唯一标识一行记录,在HBase查询的时候,有以下几种方式:
    1.通过get方式,指定rowkey获取唯一一条记录
    2.通过scan方式,设置startRow和stopRow参数进行范围匹配
    3.全表扫描,即直接扫描整张表中所有行记录

    2.1 rowkey长度原则

    owkey是一个二进制码流,可以是任意字符串,最大长度64kb,实际应用中一般为10-100bytes,以byte[]形式保存,一般设计成定长。建议越短越好,不要超过16个字节,原因如下:

    • 数据的持久化文件HFile中是按照KeyValue存储的,如果rowkey过长,比如超过100字节,1000w行数据,光rowkey就要占用100*1000w=10亿个字节,将近1G数据,这样会极大影响HFile的存储效率;
    • MemStore将缓存部分数据到内存,如果rowkey字段过长,内存的有效利用率就会降低,系统不能缓存更多的数据,这样会降低检索效率。

    2.2 rowkey散列原则

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

    2.3 rowkey唯一原则

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

    2.4 什么是热点

    HBase中的行是按照rowkey的字典顺序排序的,这种设计优化了scan操作,可以将相关的行以及会被一起读取的行存取在临近位置,便于scan。然而糟糕的rowkey设计是热点的源头。
    热点发生在大量的client直接访问集群的一个或极少数个节点(访问可能是读,写或者其他操作)。大量访问会使热点region所在的单个机器超出自身承受能力,引起性能下降甚至region不可用,这也会影响同一个RegionServer上的其他region,由于主机无法服务其他region的请求。
    设计良好的数据访问模式以使集群被充分,均衡的利用。为了避免写热点,设计rowkey使得不同行在同一个region,但是在更多数据情况下,数据应该被写入集群的多个region,而不是一个。下面是一些常见的避免热点的方法以及它们的优缺点:

    2.4.1、加盐

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

    2.4.2、哈希

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

    2.4.3、反转

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

    例:时间戳反转
    一个常见的数据处理问题是快速获取数据的最近版本,使用反转的时间戳作为rowkey的一部分对这个问题十分有用,可以用 Long.Max_Value - timestamp 追加到key的末尾,例如 [key][reverse_timestamp] , [key] 的最新值可以通过scan [key]获得[key]的第一条记录,因为HBase中rowkey是有序的,第一条记录是最后录入的数据。
    其他一些建议:
    尽量减少行键和列族的大小在HBase中,value永远和它的key一起传输的。当具体的值在系统间传输时,它的rowkey,列名,时间戳也会一起传输。如果你的rowkey和列名很大,这个时候它们将会占用大量的存储空间。
    列族尽可能越短越好,最好是一个字符。
    冗长的属性名虽然可读性好,但是更短的属性名存储在HBase中会更好。

    ----------------------------------------------------------------------------------------------------
    

    以上内容到这里就结束了哦。各位读者的 三连就是小编坚持下去的动力哦!以上内容如有错误,欢迎大家及时帮助小编纠正哦!!!最好的关系就是互相成就,我们下期见。
    我是小哪吒。一名编程行业的业余选手…哈哈哈

    学而不思则罔,思而不学则殆。
    展开全文
  • 目录简单了解概述设置预分区一、手动指定预分区二、使用16进制算法生成预分区三、将分区规则写在文本文件中四、使用JavaAPI进行预分区 简单了解 概述 由上图可以看出,每一个表都有属于自己的一个Region,但Region...


    简单了解

    概述

    在这里插入图片描述
    由上图可以看出,每一个表都有属于自己的一个Region,但Region内的数据达到10GB时,会进行分割,但仍会在同一个RegionServer上,而预分区的作用主要是增加数据读写效率负载均衡防止数据倾斜方便集群容灾调度Region优化Map数量


    设置预分区

    在设置预分区前要先明白一个概念,每一个Region都维护着从StartKey到EndKey的数据,如果加入的数据符合某个Region的rowKey范围,就把数据交给这个Region维护
    比如说,现在有三个分区,它们的StartKey和EndKey分别是1-1000,1001-2000,2001-3000,现在如果有一条rowKey为1888的数据,那么他就会被分配到第二个Region中
    预分区的设置方法一共有四种:

    一、手动指定预分区

    进入hbase shell输入一下命令

    create 'staff','info','partition1',SPLITS => ['1000','2000','3000','4000']
    

    二、使用16进制算法生成预分区

    进入hbase shell输入一下命令

    create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
    

    三、将分区规则写在文本文件中

    首先在/export/servers目录下创建splits.txt文本文件,并输入一下内容

    aaaa
    bbbb
    cccc
    dddd
    

    然后在hbase shell中执行以下命令

    create 'staff3','partition2',SPLITS_FILE => '/export/servers/splits.txt'
    

    四、使用JavaAPI进行预分区

    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.hbase.HBaseConfiguration;
    import org.apache.hadoop.hbase.HColumnDescriptor;
    import org.apache.hadoop.hbase.HTableDescriptor;
    import org.apache.hadoop.hbase.TableName;
    import org.apache.hadoop.hbase.client.Admin;
    import org.apache.hadoop.hbase.client.Connection;
    import org.apache.hadoop.hbase.client.ConnectionFactory;
    import org.testng.annotations.Test;
    
    import java.io.IOException;
    
    public class HbasePartition {
        /**
         * 通过javaAPI进行HBase的表的创建以及预分区操作
         */
        @Test
        public void hbaseSplit() throws IOException {
            //获取连接
            Configuration configuration = HBaseConfiguration.create();
            configuration.set("hbase.zookeeper.quorum", "node01:2181,node02:2181,node03:2181");
            Connection connection = ConnectionFactory.createConnection(configuration);
            Admin admin = connection.getAdmin();
            //自定义算法,产生一系列Hash散列值存储在二维数组中
            byte[][] splitKeys = {{1,2,3,4,5},{'a','b','c','d','e'}};
    
    
            //通过HTableDescriptor来实现我们表的参数设置,包括表名,列族等等
            HTableDescriptor hTableDescriptor = new HTableDescriptor(TableName.valueOf("stuff4"));
            //添加列族
            hTableDescriptor.addFamily(new HColumnDescriptor("f1"));
            //添加列族
            hTableDescriptor.addFamily(new HColumnDescriptor("f2"));
            admin.createTable(hTableDescriptor,splitKeys);
            admin.close();
    
        }
    }
    

    在这里插入图片描述


    注意
    在实际工作当中,创建表时一般都需要提前做预分区处理,一般来说每台服务器上面设置两个到五个的预分区,这么做可以更好地减少Split的过程,在设置预分区时,rowKey的设计尤为重要
    关于rowKey的设计可以查看文章:【HBase】快速了解上手rowKey的设计技巧

    展开全文
  • Hbase的预分区和BloomFilter特性

    预分区特性

           Hbase中的表会被划分为n个Region,然后存放在多个RegionServer中,每个Region有StartKey和EndKey,表示这个Region维护的RowKey范围,而第一个Region没有StartKey,最后一个Region没有EndKey。需要读写数据时,RowKey会落在某个范围内,就会定位到目标的Region以及所在的RegionServer。
            默认情况下,创建表的时候只有一个Region,当表的数据增长,数据大小大于一定的阈值,HBase就会找到一个MidKey将Region一分为二,这个过程被称为Region-Split,而Split是有时间和资源开销的。

    随机散列与预分区

    1.通过随机取样随机生成一定数量的RowKey,将取样数据按升序排序放到一个集合里
    2.根据预分区的Region个数,对集合进行平均切割
    3.HBaseAdmin.createTable(HTableDescriptor tableDescriptor,byte[][] splitkeys)可以指定预分区的splitKey,即是指定region间的rowkey临界值.

    Java实现

    两个接口

    package com.test.HBasePrepartition;
    
    public interface RowKeyGenerator {
    	byte[] nextId();
    }
    
    package com.test.HBasePrepartition;
    
    //createTable(HTableDescriptor desc, byte[][] splitKeys),所以要建二维数组
    public interface SplitKeysCalculator {
    	public byte[][] calcSplitKeys();
    }
    

    RowKey的生成 

    package com.test.HBasePrepartition;
    
    import java.util.Random;
    
    import org.apache.hadoop.hbase.util.Bytes;
    import org.apache.hadoop.hbase.util.MD5Hash;
    
    public class HashRowKeyGenerator implements RowKeyGenerator {
        private long currentId = 1;
        private long currentTime = System.currentTimeMillis();
        private Random random = new Random();
        public byte[] nextId() {
            try {
                currentTime += random.nextInt(1000);
                //从偏移量第4位开始取长度为4作为byte数组返回
                byte[] lowT = Bytes.copy(Bytes.toBytes(currentTime), 4, 4);
                byte[] lowU = Bytes.copy(Bytes.toBytes(currentId), 4, 4);
                
                //拼装成一个byte数组
                return Bytes.add(MD5Hash.getMD5AsHex(Bytes.add(lowU, lowT)).substring(0, 8).getBytes(),Bytes.toBytes(currentId));
            } finally {
            	//ID递增
                currentId++;
            }
        }
    }

     生成RowKeys,通过对RowKeys均分得出SplitKeys

    package com.test.HBasePrepartition;
    
    import java.util.Iterator;
    import java.util.TreeSet;
    
    import org.apache.hadoop.hbase.util.Bytes;
    
    public class HashChoreWoker implements SplitKeysCalculator{
    
        //数据量
        private int baseRecord;
    
        //rowkey生成器
        private RowKeyGenerator rkGen;
    
        //取样时,由取样数目及region数相除所得的数量,即每个region所要存储的数据量
        private int splitKeysBase;
    
        //splitkeys个数
        private int splitKeysNumber;
    
        //由抽样计算出来的splitkeys结果
        private byte[][] splitKeys;
    
        //构造方法
        public HashChoreWoker(int baseRecord, int prepareRegions) {
            this.baseRecord = baseRecord;
    
            //实例化rowkey生成器
            rkGen = new HashRowKeyGenerator();
    
            //所需要的SplitKey为分区数-1
            splitKeysNumber = prepareRegions - 1;
            splitKeysBase = baseRecord / prepareRegions;
        }
    
        //计算splitkey
        public byte[][] calcSplitKeys() {
    
            splitKeys = new byte[splitKeysNumber][];
    
            //使用treeset保存抽样数据,通过使用比较器进行排序
            TreeSet<byte[]> rows = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
    
            //生成抽样数据,因为使用了try..finally,所以生成的id在递增
            for (int i = 0; i < baseRecord; i++) {
                rows.add(rkGen.nextId());
            }
            
            int pointer = 0;
            Iterator<byte[]> rowKeyIter = rows.iterator();
            int index = 0;
            //遍历生成的rowkey,当pointer的值与每个region所要存储的数据量相等时,插入一个rowkey作为split
            while (rowKeyIter.hasNext()) {
                byte[] tempRow = rowKeyIter.next();
                rowKeyIter.remove();
                if ((pointer != 0) && (pointer % splitKeysBase == 0)) {
                    if (index < splitKeysNumber) {
                        splitKeys[index] = tempRow;
                        index ++;
                    }
                }
                pointer ++;
            }
            rows.clear();
            rows = null;
            return splitKeys;
        }
    }

    根据生成的SplitKeys建表,获得预分区的效果 

    package com.test.HBasePrepartition;
    
    import java.io.IOException;
    
    import org.apache.hadoop.conf.Configuration;
    import org.apache.hadoop.hbase.HBaseConfiguration;
    import org.apache.hadoop.hbase.HColumnDescriptor;
    import org.apache.hadoop.hbase.HTableDescriptor;
    import org.apache.hadoop.hbase.TableName;
    import org.apache.hadoop.hbase.client.Admin;
    import org.apache.hadoop.hbase.client.Connection;
    import org.apache.hadoop.hbase.client.ConnectionFactory;
    
    import org.apache.hadoop.hbase.util.Bytes;
    
    public class TestHashAndCreateTable {
    	public static void main(String[] args) throws IOException {
    		HashChoreWoker worker = new HashChoreWoker(1000000,10);
            byte [][] splitKeys = worker.calcSplitKeys();
            
            Configuration conf=HBaseConfiguration.create();
            Connection conn=ConnectionFactory.createConnection(conf);
            Admin admin = conn.getAdmin();
            TableName tableName = TableName.valueOf("hash_split_table");
            
            if (admin.tableExists(tableName)) {
                try {
                    admin.disableTable(tableName);
                } catch (Exception e) {
                }
                admin.deleteTable(tableName);
            }
    
            HTableDescriptor tableDesc = new HTableDescriptor(tableName);
            HColumnDescriptor columnDesc = new HColumnDescriptor(Bytes.toBytes("info"));
            columnDesc.setMaxVersions(1);
            tableDesc.addFamily(columnDesc);
    
            admin.createTable(tableDesc ,splitKeys);
    
            admin.close();
    	}
    }
    

    在HBase的Web界面可以看见已经分好了10个Region

    put数据的时候也是按指定的方法来生成rowkey,因为函数固定,生成的rowkey也是固定的。

    BloomFilter

           Bloom Filter是由Bloom在1970年提出的一种多哈希函数映射的快速查找算法。通常应用在一些需要快速判断某个元素是否属于集合,但是并不严格要求100%正确的场合。

    算法分析

           经过研读多篇文章,对BloomFilter算法有了一定的了解,如果理解有误,请指正,但这里不作数学类分析。
          BloomFilter的原理是,集合的大小为n,当一个元素被加入集合S时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。下面是一个例子。
          初始状态时候,Bloom Filter是一个包含m位的位数组,每一位都置为0:

    bloomfilter

          当元素A出现,使用两个Hash函数,计算出元素A对应的Hash值为1和5,然后到位数组中判断第1位和第5位的值,发现均为0,因此元素A不在位数组里,将元素A的位信息添加进位数组,元素A添加进集合。

    bloomfilter

           之后的元素,要添加进集合也是使用一样的方法。如果要判断是否在集合内,则对元素进行Hash得出Hash值作为,在位数组中,计算出的Hash值作为编号对应的值均为1,则认为该元素在集合内(但是有可能是误判);如果对应的值有任何一个是0,则被检元素一定不在集合内。

    bloomfilter

            随着元素的插入,位数组中修改的值变多,出现误判的几率也随之变大,所谓的误判就是发生了哈希碰撞,即对不同的值进行Hash得出的值是相同的,但这个元素不在集合内。
          字符串加入了就被不能删除了,因为删除会影响到其他字符串。实在需要删除字符串的可以使用Counting bloomfilter(CBF),这是一种基本Bloom Filter的变体,CBF将基本Bloom Filter每一个Bit改为一个计数器,这样就可以实现删除字符串的功能了。 
           根据这篇参考文献,对于给定的m、n,当 k = ln(2)* m/n 时出错的概率是最小的。同时该文献还给出特定的k,m,n的出错概率,即一个m/n与k值的对应表。

    BloomFilter在HBase的作用

    HBase中,使用BloomFilter有两个控制粒度:
         1.ROW依据KeyValue中的RowKey来过滤StoreFile(对Get有优化)
               举例:假设有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)

            如果设置了bloomfilter为ROW,那么get(r1)时就会过滤sf1,get(r3)就会过滤sf2

         2.ROWCOL依据KeyValue中的RowKey+Qualifier来过滤HFile(对Scan有优化)
                 举例:假设有2个storefile文件sf1和sf2, 
                            sf1包含kv1(r1 cf:q1 v)、kv2(r2 cf:q1 v) 
                            sf2包含kv3(r1 cf:q2 v)、kv4(r2 cf:q2 v)

                 如果设置了bloomfilter为ROW,无论get(r1,q1)还是get(r1,q2),都会读取sf1+sf2; 
                 而如果设置了CF属性中的bloomfilter为ROWCOL,那么get(r1,q1)就会过滤sf2,get(r1,q2)就会过滤sf1。

            如果在表中设置了BloomFilter,那么HBase会在生成StoreFile时,包含一份BloomFilter 结构的数据,称其为MetaBlock;MetaBlock与DataBlock(真实的KeyValue数据)一起由LRUBlockCache维护,所以开启BloomFilter会有一定的存储及内存cache开销。
             Region下的StoreFile数目越多,BloomFilter效果越好;StoreFile数目越少,HBase读的效率越高。

    BloomFilter在HBase中如何开启

    在建表的时候加上BloomFilter的特性即可

    create 'test',{NAME=>'INFO,BLOOMFILTER=>'ROWCOL'}
    create 'test1',{NAME=>'INFO,BLOOMFILTER=>'ROW'}


    参考:  
    http://pages.cs.wisc.edu/~cao/papers/summary-cache/node8.html 
    http://zjushch.iteye.com/blog/1530143
    http://lxw1234.com/archives/2015/12/580.htm

    展开全文
  • 文章目录HBase的预分区1、为何要预分区?2、如何预分区?3、如何设定预分区?1、手动指定预分区2、使用16进制算法生成预分区3、分区规则创建于文件中4、使用JavaAPI创建预分区HBase的rowKey设计技巧1 rowkey长度原则...
  • 文章目录HBase数据的导入和预分区HBase数据导入1.准备数据2.使用ImportTsv将csv文件直接导入HBase中步骤1:在Hbase中创建一张表ns1:in_table步骤2:将data.csv导入到ns1:in01中步骤3:查看ns1:in01中的数据3.使用...
  • 2、使用16进制算法生成预分区 create 'staff2','info','partition2',{NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}  3 、分区规则创建于文件中 cd /export/servers/ vim splits.txt ...
  • 1. 概述 ...HBase是一个数据模型,类似于Google Big Table设计,可以提供快速随机访问海量结构化数据。它利用了Hadoop的文件系统(HDFS)提供的容错能力。 它是Hadoop的生态系统,提供对数据的随机实...
  • ,stopRow是[userId反转][Long.Max_Value - timestamp] 如果需要查询某段时间的操作记录,startRow是[user反转][Long.Max_Value - 起始时间],stopRow是[userId反转][Long.Max_Value - 结束时间] HBase建表预分区:...
  • MySQL千万级数据分区存储及查询优化

    千次阅读 2018-03-23 13:39:55
     http://www.cnblogs.com/javaIOException/p/7524945.htmlMySQL千万级数据分区存储及查询优化作为传统的关系型数据库,MySQL因其体积小、速度快、总体拥有成本低受到中小企业的热捧,但是对于大数据量(百万级以上)...
  • 快速分区是什么?“快速分区”是傲梅分区助手中用于快速地重新为磁盘分区的功能,支持运行在电脑,服务器和PE系统中。适用于为新硬盘分区,或为已存在分区的硬盘重新分区。用户可指定各分区大小、类型、卷标等内容。...
  • 分区键的设计:(startrow、endrow,...可以快速确定在哪个区,便于数据的判断存储 1500100200| ... 1500100900| row key的设计: 000_ //都加上一个_,由于_的ASCII值最小 001_ 152_ 298_
  • 分区

    千次阅读 2010-10-26 17:11:00
    数据库结构和索引的是否合理在很大程度上影响了数据库的性能,但是随着数据库信息负载的增大,对数据库的性能也发生了很大的影响。可能我们的数据库在一开始...建立分区表(Table Partition)可以在某些场合下提高数据...
  • Linux分区

    2016-01-22 13:14:56
    磁盘分区是将磁盘划分成数个分区,不同类的目录和文件可以存储进不同的分区。 1. 分区和文件系统 (1)分区类型: 主分区:总共最多只能分四个。扩展分区:只能有一个,也算作主分区的一种,也就是说主分区加扩展...
  • PostgreSQL 11 新特性之分区裁剪增强

    千次阅读 2019-01-10 14:44:02
    在之前的版本中,只在查询的计划阶段执行分区排除操作(通过 constraint_exclusion 变量控制),意味着许多连接查询编译查询无法使用分区排除。另外,这种方法占用的时间会随着分区的数量线性增长。 PostgreSQL ...
  • 最近在 ESP32 编译中遇到了以下问题: esp_image: Image length 1057652 doesn't fit in partition length 1048576 boot: Factory app partition is not bootable ...make menuconfig 配置中可选择以下定义
  • 那么您想不想再创建一个新的分区,专门用于装同一类的东西呢?例如把工作中的一些重要的文件或者其它资料存放起来,又不想同日常应用程序或娱乐文件放在一起,这时您就需要创建一个适当大小的新分区了。新版分区助手...
  • 正常查询数据表操作,需要遍历扫描整个表数据。 索引是一种结构,里面有相对应的算法。可以保证速度更快的查询出来。就是把建立索引的字段,单独存储起来,对应存储字段和对应的真实数据文件存储的物理地址。 比如...
  • clickhouse连表查询5.1 Join 查询5.2 clickHouse单机JOIN实现5.3 clickHouse分布式JOIN实现5.4 数据分布实现Colocate JOIN 1. 什么是clickhouse? ClickHouse是一个面向联机分析处理(OLAP)的开源的面向列式存储的...
  • 数据库分区

    千次阅读 2016-08-09 10:34:16
    虽然分区技术可以实现很效果,但其主要目的是为了在特定的SQL操作中减少数据读写的总量以缩减响应时间。 分区主要有两种形式://这里一定要注意行和列的概念(row是行,column是列) 1. 水平分区...
  • MySQL分区

    2015-11-27 15:35:01
    分区允许根据指定的规则,跨文件系统分配单个表的个部分。表的不同部分在不同的位置被存储为单独的表。MySQL从5.1.3开始支持Partition。 分区和手动分表对比 手动分表  分区 张数据表 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 24,136
精华内容 9,654
关键字:

多预分区快速查询