29.hbase复习及表的设计实例

hbase:

1.hbase是数据库

特点:高可靠性,高性能,面向列,可伸缩,实时读写
数据量:十亿级别的行
百万级别的列
速度快的原因:1充分利用内存 2会用了LSM树 3 缓存机制 4文件是顺序读

2数据模型

rowkey
rowkey相当于mysql中的主键,唯一标识一行记录
rowkey是字典序
rowkey的长度最长是64kb,但是一般推荐10-100字节
colunm family
一组列的集合
列族必须作为表的schema定义给出
列族是权限,存储的最小单元
qulifier

可以动态的,随机的插入
表定义之后没有限制列,随着值的插入也把列插入
列必须归属于某一个列族
timestamp
时间戳,64位整数,精度是毫秒
器版本号的作用,一个cell中可以存在多版本的数据
时间戳可以自己定义,但是一般不推荐
cell
存储数据的最小单元(逻辑概念)
存储的是KV格式的数据 K:rowkey+column family +qulifier+timestamp
V:value
hbase的cell存储数据的时候没有类型的区分,存放的都是字节数组

3.架构

hbase是主从架构
角色:
client:操作hbase的接口(命令行和java api)并维护客户端缓存
zookeeper:保证任何时刻集群中有且仅有一台active的master
存储所有region的寻址入口--所有region元数据存储在哪一台regionserver
监控regionserver的上线和下线信息,并实时通知master
存储相关的表的schema数据
master:分配region
保证整个集群中的所有regionserver负载均衡
当发现某一台regionserver宕机之后,重新分配上面的region
当region变大进行裂变的时候,master去分配region到哪一台regionserver
regionserver:负责接收客户端的读写请求,处理对于region的IO
当某一个region变大之后,负责等分为两个region
region:相当于表的概念。一张表至少对应一个region。
当表的数据过大,region会发生裂变(等分)
store:相当于列族
store里有角色memstore和storefile
memstore位于内场,每一个store有一个memstore
storefile磁盘存储空间,将数据持久化的存储位置
每一个region有一个或者多个storefile
storefile可以进行合并操作
store存储结构:使用了LSM的数据模型
WAL:write ahead log
防止数据丢失
先写内存,再向hdfs上溢写,但是是异步的方式
读写流程
读流程
1.客户端向zk中发送请求
2.从zk中拿到metadata的存储节点
3去存储metadata的节点获取对应region的所在位置
4访问对应的region获取数据
5先去memstore中查询数据,如果有结果,直接返回
6如果没有查询到结果,去blockcache查找数据,如果找到,直接返回
7如果没有找到,去storefile中查找数据,并将查询到的结果缓存回blockcache中,方便下一次查询
8将结果返回给客户端
注意:blockcache是缓存,有大小限制,会有淘汰机制,默认将最早的数据淘汰
写流程
1.client向zk发送请求
2.从zk中拿到metadata的存储节点
3去存储metadata的节点获取对应region的所在位置
4访问对应的region写数据
5先首先会向wal中写数据,写成功后才会存储到memstore
6.当memstore中的数据量达到阈值之后,进行溢写,溢写成storefile
7.store file是一个个的小文件,会进行合并(minor(一般自动触发),major(一般手动触发))
8.storefile是对hfile的封装。hfile是实际存储在hdfs上的数据文件

JAVA API

hbase admin:
管理表 :createtable disabletable deletetable
HTable:管理数据:put get scan delete

对于通话记录的表的设计

通话记录表所要记录的数据有:手机号,通话时长,对方手机号,日期,类型(主叫,被叫)
按时间戳倒序,所以在hbase中应该如何设计?
设计如下phone_(long.maxValue-timestamp)
cf:length=,cf:data=,cf:dnum=,cf:type=
设计如下

public void insert() throws Exception {
       List puts = new ArrayList();
       for (int i = 0; i < 10; i++) {
           String phoneNumber = getPhone("158");//158开头的十一位整数
           for (int j = 0; j < 1000; j++) {
               // 属性
               String dnum = getPhone("177");
               String length = String.valueOf(r.nextInt(99));
               String type = String.valueOf(r.nextInt(2));
               String date = getDate("2018");
               // rowkey设计
               String rowkey = phoneNumber + "_" + (Long.MAX_VALUE - sdf.parse(date).getTime());
               Put put = new Put(rowkey.getBytes());
               put.add("cf".getBytes(), "dnum".getBytes(), dnum.getBytes());
               put.add("cf".getBytes(), "length".getBytes(), length.getBytes());
               put.add("cf".getBytes(), "type".getBytes(), type.getBytes());
               put.add("cf".getBytes(), "date".getBytes(), date.getBytes());
               puts.add(put);
           }
       }
       table.put(puts);
   }

对于以下
查询某一个用户3月份的所有通话记录

 /**
     * 查询某一个用户3月份的所有通话记录 条件: 1、某一个用户 2、时间
     * 
     * @throws Exception
     */
    @Test
    public void scan() throws Exception {
        String phoneNumber = "15895223166";
        String startRow = phoneNumber + "_" + (Long.MAX_VALUE - sdf.parse("20180401000000").getTime());//取三月份通话记录,所以04 四月是start(倒序)
        String stopRow = phoneNumber + "_" + (Long.MAX_VALUE - sdf.parse("20180301000000").getTime());

        Scan scan = new Scan();
        scan.setStartRow(startRow.getBytes());
        scan.setStopRow(stopRow.getBytes());
        ResultScanner scanner = table.getScanner(scan);
        for (Result result : scanner) {
            System.out.print(Bytes
 .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "dnum".getBytes()))));
            System.out.print("--" + Bytes
       .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "type".getBytes()))));
            System.out.print("--" + Bytes
.toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "date".getBytes()))));
            System.out.println("--" + Bytes
.toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "length".getBytes()))));
        }
    }

查询某一个用户。所有的主叫电话 .设置过滤器

    /**
     * 查询某一个用户。所有的主叫电话 条件: 1、电话号码 2、type=0
     * 
     * @throws Exception
     */
    @Test
    public void scan2() throws Exception {
        FilterList filters = new FilterList(FilterList.Operator.MUST_PASS_ALL);
        SingleColumnValueFilter filter1 = new SingleColumnValueFilter("cf".getBytes(), "type".getBytes(),
                CompareOp.EQUAL, "0".getBytes());
        PrefixFilter filter2 = new PrefixFilter("15895223166".getBytes());
        filters.addFilter(filter1);
        filters.addFilter(filter2);

        Scan scan = new Scan();
        scan.setFilter(filters);
        ResultScanner scanner = table.getScanner(scan);
        for (Result result : scanner) {
            System.out.print(Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "dnum".getBytes()))));
            System.out.print("--" + Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "type".getBytes()))));
            System.out.print("--" + Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "date".getBytes()))));
            System.out.println("--" + Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "length".getBytes()))));
        }
        scanner.close();
    }

代码:



public class PhoneCase {

    // 表的管理类
    HBaseAdmin admin = null;
    // 数据的管理类
    HTable table = null;
    // 表名
    String tm = "phone";

    /**
     * 完成初始化功能
     * 
     * @throws Exception
     */
    @Before
    public void init() throws Exception {
        Configuration conf = new Configuration();
        conf.set("hbase.zookeeper.quorum", "node1,node2,node3");
        admin = new HBaseAdmin(conf);
        table = new HTable(conf, tm.getBytes());
    }

    /**
     * 创建表
     * 
     * @throws Exception
     */
    @Test
    public void createTable() throws Exception {
        // 表的描述类
        HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tm));
        // 列族的描述类
        HColumnDescriptor family = new HColumnDescriptor("cf".getBytes());
        desc.addFamily(family);
        if (admin.tableExists(tm)) {
            admin.disableTable(tm);
            admin.deleteTable(tm);
        }
        admin.createTable(desc);
    }

    /**
     * 10个用户,每个用户每年产生1000条通话记录
     * 
     * dnum:对方手机号 type:类型:0主叫,1被叫 length:长度 date:时间
     * 
     * @throws Exception
     * 
     */
//插入数据
    @Test
    public void insert() throws Exception {
        List puts = new ArrayList();
        for (int i = 0; i < 10; i++) {
            String phoneNumber = getPhone("158");//158开头的十一位整数
            for (int j = 0; j < 1000; j++) {
                // 属性
                String dnum = getPhone("177");
                String length = String.valueOf(r.nextInt(99));
                String type = String.valueOf(r.nextInt(2));
                String date = getDate("2018");
                // rowkey设计
                String rowkey = phoneNumber + "_" + (Long.MAX_VALUE - sdf.parse(date).getTime());
                Put put = new Put(rowkey.getBytes());
                put.add("cf".getBytes(), "dnum".getBytes(), dnum.getBytes());
                put.add("cf".getBytes(), "length".getBytes(), length.getBytes());
                put.add("cf".getBytes(), "type".getBytes(), type.getBytes());
                put.add("cf".getBytes(), "date".getBytes(), date.getBytes());
                puts.add(put);
            }
        }
        table.put(puts);
    }

    /**
     * 查询某一个用户3月份的所有通话记录 条件: 1、某一个用户 2、时间
     * 
     * @throws Exception
     */
    @Test
    public void scan() throws Exception {
        String phoneNumber = "15895223166";
        String startRow = phoneNumber + "_" + (Long.MAX_VALUE - sdf.parse("20180401000000").getTime());
        String stopRow = phoneNumber + "_" + (Long.MAX_VALUE - sdf.parse("20180301000000").getTime());

        Scan scan = new Scan();
        scan.setStartRow(startRow.getBytes());
        scan.setStopRow(stopRow.getBytes());
        ResultScanner scanner = table.getScanner(scan);
        for (Result result : scanner) {
            System.out.print(Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "dnum".getBytes()))));
            System.out.print("--" + Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "type".getBytes()))));
            System.out.print("--" + Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "date".getBytes()))));
            System.out.println("--" + Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "length".getBytes()))));
        }
    }

    /**
     * 查询某一个用户。所有的主叫电话 条件: 1、电话号码 2、type=0
     * 
     * @throws Exception
     */
    @Test
    public void scan2() throws Exception {
        FilterList filters = new FilterList(FilterList.Operator.MUST_PASS_ALL);
        SingleColumnValueFilter filter1 = new SingleColumnValueFilter("cf".getBytes(), "type".getBytes(),
                CompareOp.EQUAL, "0".getBytes());
        PrefixFilter filter2 = new PrefixFilter("15895223166".getBytes());
        filters.addFilter(filter1);
        filters.addFilter(filter2);

        Scan scan = new Scan();
        scan.setFilter(filters);
        ResultScanner scanner = table.getScanner(scan);
        for (Result result : scanner) {
            System.out.print(Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "dnum".getBytes()))));
            System.out.print("--" + Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "type".getBytes()))));
            System.out.print("--" + Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "date".getBytes()))));
            System.out.println("--" + Bytes
                    .toString(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "length".getBytes()))));
        }
        scanner.close();
    }

    /**
     * 日期
     * @param string
     * @return
     */

    private String getDate(String string) {
        return string + String.format("%02d%02d%02d%02d%02d", r.nextInt(12) + 1, r.nextInt(31), r.nextInt(24),
                r.nextInt(60), r.nextInt(60));
    }

    Random r = new Random();
    SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss");
//电话
    private String getPhone(String phonePrefix) {
        return phonePrefix + String.format("%08d", r.nextInt(99999999));
    }

    /**
     * 10个用户,每个用户1000条,每一条记录当作一个对象进行存储
     * @throws Exception 
     */
    @Test
    public void insert2() throws Exception{
        List puts = new ArrayList();
        for(int i = 0;i<10;i++){
            String phoneNumber = getPhone("158");
            for(int j = 0;j<1000;j++){
                String dnum = getPhone("177");
                String length =String.valueOf(r.nextInt(99));
                String type = String.valueOf(r.nextInt(2));
                String date = getDate("2018");
                //保存属性到对象中
                Phone.PhoneDetail.Builder phoneDetail = Phone.PhoneDetail.newBuilder();
                phoneDetail.setDate(date);
                phoneDetail.setLength(length);
                phoneDetail.setType(type);
                phoneDetail.setDnum(dnum);
                
                //rowkey
                String rowkey = phoneNumber+"_"+(Long.MAX_VALUE-sdf.parse(date).getTime());
            
                Put put = new Put(rowkey.getBytes());
                put.add("cf".getBytes(), "phoneDetail".getBytes(), phoneDetail.build().toByteArray());
                puts.add(put);
            }
        }
        table.put(puts);
    }
    
    @Test
    public void get() throws Exception{
        Get get = new Get("15866626435_9223370522183722807".getBytes());
        Result result = table.get(get);
        PhoneDetail phoneDetail = Phone.PhoneDetail.parseFrom(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "phoneDetail".getBytes())));
        System.out.println(phoneDetail);
    }
    
    /**
     * 10个用户,每天产生1000条记录,每一天的所有数据放到一个rowkey中
     * @throws Exception 
     */
    @Test
    public void insert3() throws Exception{
        List puts = new ArrayList();

        for(int i = 0;i<10;i++){
            String phoneNumber = getPhone("133");
            String rowkey = phoneNumber+"_"+(Long.MAX_VALUE-sdf.parse("20181225000000").getTime());
            Phone.DayOfPhone.Builder dayOfPhone = Phone.DayOfPhone.newBuilder();
            for(int j=0;j<1000;j++){
                String dnum = getPhone("177");
                String length =String.valueOf(r.nextInt(99));
                String type = String.valueOf(r.nextInt(2));
                String date = getDate2("20181225");
                
                Phone.PhoneDetail.Builder phoneDetail = Phone.PhoneDetail.newBuilder();
                phoneDetail.setDate(date);
                phoneDetail.setLength(length);
                phoneDetail.setType(type);
                phoneDetail.setDnum(dnum);
                dayOfPhone.addDayPhone(phoneDetail);
            }
            Put put = new Put(rowkey.getBytes());
            put.add("cf".getBytes(), "day".getBytes(), dayOfPhone.build().toByteArray());
            puts.add(put);
        }
        table.put(puts);
        
    }
    
    @Test
    public void get2() throws Exception{
        Get get = new Get("13398049199_9223370491187575807".getBytes());
        Result result = table.get(get);
        DayOfPhone parseFrom = Phone.DayOfPhone.parseFrom(CellUtil.cloneValue(result.getColumnLatestCell("cf".getBytes(), "day".getBytes())));
        int count = 0;
        for (PhoneDetail pd : parseFrom.getDayPhoneList()) {
            System.out.println(pd);
            count++;
        }
        System.out.println(count);
    }
    
    private String getDate2(String string) {
        return string+String.format("%02d%02d%02d", r.nextInt(24),r.nextInt(60),r.nextInt(60));
    }

    @After
    public void destory() throws Exception {
        
        if (admin != null) {
            admin.close();
        }
    }
}

第二个表的设计

角色表的设计

人员有多个角色 角色优先级
角色有多个人员
人员 删除添加角色
角色 可以添加删除人员
人员 角色 删除添加
001小红 班长100,学委200

002小白 学委,劳委300

003小黑 劳委,体委400

一张表:
人员角色表

rowkey: cf1:(基本信息) cf2:(角色列表) cf3:(人员列表)
pid cf1:pname= ,cf1:page= cf2:rid=?,cf2:rid2=?
rid cf1:rname=?,cf1:rdesc=?(r描述) cf3:pid=?,cf3:pid2=?

一张表可能不是很方便,但是达到了需求
两张表
psn

rowkey:pid cf1:(人员信息) cf2:(角色列表)
001 cf1:name=小红 cf2:100=班长,cf2:200=学委(如果要满足优先级,可以设为cf2:100=10,cf2:200=9)
002 cf1:name=小白 cf2:200=。。。,cf2:300=。。。
003 cf1:name=小黑 cf2:300=。。。,cf2:400=。。。
004 cf1:name=小绿

role

rowkey: rid cf1:(角色信息) cf2:(人员列表)
100 cf1:name=班长 cf2:001=小红
200 cf1:name=学委 cf2:001=小红,cf2:002=小白
300 cf1:name=劳委 cf2:001=小黑,cf2:002=小白
400 cf1:name=体委 cf2:001=小黑,

hbase存在observer,功能与mysql中触发器类似

你可能感兴趣的:(29.hbase复习及表的设计实例)