• Hbase的表设计


    一、应用背景

    微博:用户表users、微博表weibos、用户关系表relations,和具体哪个公司的微博没关系。

    微博中的用户想关注其他用户的微博,首先要维护一个特定用户的关注列表,例如张三关注了李四和王五。

    为了要的得到张三应该看到的所有微博,你需要查找列表{李四、王五},然后读出列表中每个用户的所有微博,这个信息需要保存在hbase中。

    二、表模式(Schema)设计应该考虑的问题

    • 这个表应该保存多少个列族
    • 列族使用什么数据
    • 每个列族应该有多少列
    • 列名应该是什么
    • 每个单元(Cell)存储多少时间版本
    • 行健结构是什么?应该包括什么信息?

    三、问题建模

    问题分析:关系表需要存储一个特定用户关注什么用户的数据。

    表的访问模式:

    • 读出全部用户列表。张三关注人的列表
    • 查询某指定用户是否在列表里。如张三是否关注了李四

    需要几个列族呢?

       张三的关注对象里的所有用户,都有可能被确认是否存在,从访问模式看无法区分彼此,不能假定一个用户比其他用户的被访问的可能性大,推断被关注用户需要同一个列族。

      原因:同一个列族的数据存储在同一个物理存储(store)里面,这个物理存储被分为多个Hfile,理想情况下合并为一个Hfile。一个列族的所有列在物理上存在一起,使用这种模式可以把不同访问模式的列放在不同的列族,以便隔离他们。即Hbase被称为是面向列族存储的。

        初始表设计为:

               说明:follws:1--->TheRealMT.

    三、定义访问模式(读模式,写模式)

    这张表用来干嘛?

    • 张三关注了谁?
    • 张三关注李四了吗?
    • 谁关注了张三?
    • 李四关注张三了吗?

    表创建脚本:

    1.创建关系表
    create 'relation',{NAME => 'follows', VERSIONS => 2}
    2.添加数据
    put 'relation','zhansan','follows:1','lisi'
    put 'relation','zhansan','follows:2','wangwu'

    回答第一个问题:张三关注了谁?

      @Test
           public void demo2() throws Exception{
               Configuration config = HBaseConfiguration.create();
               config.set("hbase.zookeeper.quorum", "node1,node2,node3");
               
               HTable table = new HTable(config, "relation");
               String rowKey = "zhansan";
               Get get = new Get(Bytes.toBytes(rowKey));
               List<String> fllowed = new ArrayList<String>();
         
               Result rs = table.get(get);
               List<KeyValue> list = rs.list();
               Iterator<KeyValue> it = list.iterator();
               while(it.hasNext()){
                   KeyValue kv = it.next();
                   System.out.println(Bytes.toString(kv.getValue()));
                   fllowed.add(Bytes.toString(kv.getValue()));
              }
               table.close();
           }

    回答第二个问题:张三关注李四了谁?

         public static boolean  demo3() throws Exception{
               Configuration config = HBaseConfiguration.create();
               config.set("hbase.zookeeper.quorum", "node1,node2,node3");
               
               HTable table = new HTable(config, "relation");
               String rowKey = "zhansan";
            Get get = new Get(Bytes.toBytes(rowKey));
            //要查询的对象
            String followUser = "lisi";
         
               Result rs = table.get(get);
            List<KeyValue> list = rs.list();
            Iterator<KeyValue> it = list.iterator();
            while(it.hasNext()){
                KeyValue kv = it.next();
                System.out.println(followUser.equals(Bytes.toString(kv.getValue())));
                //followUser
                if(followUser.equals(Bytes.toString(kv.getValue())));
                    return true;
            }
            return false;
               
           }

    现在我们这样的回答能简单回答第一个和第二个问题,另外另个不确定,但是这两个问题的回答都属于表的读模式。

    让我么来看看表的写模式:以下应用场景会引发hbase表的写模式

    • 一个用户关注了某人
    • 一个用户取消关注了某人

    当用户新增一个新的关注的时候,需要在用户的关注列表里新增一个对象,如之前的表设计,TheFakeMT新增一个关注用户的时候,需要知道这个

    用户是用户列表里的第5个,如果不查询hbase客户端并不知道这个信息,还有不指定列限定符,也没办法要求Hbase在已有的行上增加一个单元。

    不清楚要插入的列的位置,会出现数据覆盖。

    解决办法:

             在同一行维护一个计数器。如下图所示;

      

    优点:count列能有效的让任何用户知道所关注的用户数量,避免遍历整个关注列表。

    基于现在的表设计,插入关注用户的步骤如下:

    这种表设计的确定:

    • 增加了客户端代码的复杂性,写入需要四步
    • hbase不支持事务,线程不安全

    怎么解决????--->去掉count计数器

    新的表设计如下:

    表设计说明:

    • 列限定符设计为被关注人的用户名(用户id)
    • cell的内容可以随便存储内容,可以存储1
    • hbase把一切数据存储为byte[](字节数组),可以存储任意数量的列
    • 这就是传说中的宽表设计,宽表的设计会便于检索数据
    • 由于用户id的是唯一的,所以不会出现数据覆盖

    客户端代码:

    public static void  demo4() throws Exception{
               Configuration config = HBaseConfiguration.create();
               config.set("hbase.zookeeper.quorum", "node1,node2,node3");
               
               HTable table = new HTable(config, "relation");
               String rowKey = "zhansan";
               Put put = new Put(Bytes.toBytes(rowKey));
              //要存储的对象
               String followUser = "chaosju";
               put.add(Bytes.toBytes("follows"),
                    Bytes.toBytes(followUser), 
                    Bytes.toBytes(1));
               table.put(put);
               table.close();
           }
    hbase(main):018:0> scan 'relation'
    ROW                                COLUMN+CELL                                                                                       
     zhansan                           column=follows:1, timestamp=1442106287998, value=lisi                                             
     zhansan                           column=follows:2, timestamp=1442106352630, value=wangwu                                           
     zhansan                           column=follows:chaosju, timestamp=1442109774792, value=x00x00x00x01                           
    1 row(s) in 0.1640 seconds

    学到的东西:

    • hbase没有跨行事务的概念,避开客户端使用复杂的业务逻辑

    四、均衡分布数据和负载

    背景分析:基于上述的表设计

    • 一个人可能关注很多人,这个表会特别长,这本身不是问题,但会影响读模式。如TheFakeMT关注了TheRealMT吗?如何使用这个表回答这个问题呢,行健指定TheFakeMT,列限定符指定TheRealMT,一个Get请求即可。

    follows表的另一种设计——高表(high table)设计

    优点:

    • 短的列族和列限定名,减少网络io和磁盘空间占用

    具体的存储数据样式:

    回答张三是否关注了李四???

          先进性一次索引查找,先找到第一个以张三为前缀的第一个数据块,然后以张三开头的行健对所有数据行进行最后一次扫描。

    优化设计,及高表的优点:

    • MD5(user1ID)MD5(user2ID)作为rowkey,使得数据均匀的分布在region上,避免热点问题
    • rowkey长度统一,便于预测读写性能
    • 去掉分隔符,更容易扫描计算起始和停止键

    hbase热点问题???数据倾斜

    热点:数据集中分布在一小部分region上。

    例如插入时间序列数据,行健开头是时间戳,因为任何写入的数据的时间戳都是大于之前写入的时间戳,数据总是追加在表的尾部,最后的region会成为热点。

    基于MD5_rowkey的设计

    如果需要查找,用户id,客户把用户id保存为列限定符。  

     表设计总结:

    采菊东篱下,悠闲现南山~
  • 相关阅读:
    为什么需要Docker?
    一分钟学会《模板方法模式》
    2018再见|2019你好
    三分钟学会《门面模式》
    策略模式原来这么简单!
    外行人都能看得懂的机器学习,错过了血亏!
    我是如何将博客转成PDF的
    面试前必须知道的MySQL命令【explain】
    count(*)、count(1)和count(列名)的区别
    Linux shell去除字符串中所有空格
  • 原文地址:https://www.cnblogs.com/ChaosJu/p/4807607.html
Copyright © 2020-2023  润新知