分布式NoSQL列存储数据库Hbase_列族的设计(五)

简介: 分布式NoSQL列存储数据库Hbase_列族的设计(五)

分布式NoSQL列存储数据库Hbase_列族的设计(五)

知识点01:课程回顾

  1. Hbase存储原理
  • 存储架构
  • Hbase:对外提供分布式内存
  • Master:集群管理
  • RegionServer:数据管理
  • HDFS:提供分布式磁盘
  • DataNode
  • Zookeeper:实现辅助选举、实现元数据存储
  • 存储结构
  • Table:分布式表,一张表划分了多个Region
  • RegionServer:分布式集群节点,管理所有表的region
  • Region:每张表的每个分区,对表的数据进行划分
  • region的划分规则:按照范围划分,从-oo 到+oo进行有序划分
  • 数据的划分规则:按照Rowkey或者Rowkey前缀,Rowkey属于哪个范围,写入对应的region
  • Store:对分区内部的数据再次划分,按照列族进行划分Store
  • MemStore:内存区域,用于存储刚写入的数据,使用的RegionServer的JVM内存
  • 如果到达一定的阈值,就会写入HDFS,变成文件
  • StoreFile:逻辑上属于Store,物理上存储在HDFS【HFILE】
  • HDFS存储关系
  • Hbase目录:/hbase
  • Hbase数据:/hbase/data
  • NS目录
  • Table的目录
  • Region的目录
  • 列族的目录
  1. 热点问题
  • 现象:大量的读写请求全部集中在某个Region或者某个RegionServer上
  • 原因:数据分配的不均衡
  • 情况一:表只有一个分区
  • 情况二:表有多个分区,但是Rowkey是连续的,或者与分区的划分不匹配
  • Rowkey是连续,写入同一个Region
  • 分区都是按照数字划分的,但是Rowkey是字母开头
  • 解决
  • 实现预分区
  • 方式一:创建表时候:SPLITS => [10,30……]
  • 方式二:指定分区的个数,根据数字和字母的Hash组合
  • 应用:Rowkey的前缀是字母和数字组合
  • 方式三:Java API
  • 合理的设计Rowkey,根据Rowkey的前缀或者完整的Rowkey来划分分区
  • Rowkey的设计规则
  • 业务原则:尽量用最常用的查询条件作为Rowkey的前缀
  • 目的:尽量走索引查询
  • 唯一原则:每条Rowkey是不能重复
  • 目的:唯一标识一条数据
  • 组合原则:将最常用的几个查询条件组合构建Rowkey
  • 目的:尽量大部分查询都走索引
  • 散列原则:Rowkey整体或者前缀不能是连续的,需要构建随机的散列
  • 目的:避免热点问题
  • 长度原则:在满足业务需求情况下,越短越好
  • 目的:提高性能
  • Rowkey本身每列都会存储:存储占用的空间越大
  • Rowkey会构建索引:内存的占用越大,比较就越慢

知识点02:课程目标

  1. 聊天系统案例
  • 目标
  • 掌握基于业务需求场景的Hbase表的设计
  • 熟练Hbase JavaAPI
  • 实现
  • 写:保存【重点】
  • Rowkey构建的代码
  • 数据写入的代码
  • 读:查询【重点】
  • 过滤器代码
  1. 二级索引【重点】
  • 问题:如果查询数据的条件,不是rowkey的前缀,怎么解决查询效率的问题?
  • Hbase只支持RowKey作为唯一索引,不支持创建索引
  • 与MySQL不一样
id    name    age   sex   addr    phone
主键
select * from table where id = 1
select * from table where name=‘zhangsan’
|
create index name on table(name);
  • 本质:基于Rowkey索引之上,自己构建一层索引

知识点03:Hbase设计:列族的设计

  • 设计目的
  • Hbase特点:按列存储,允许存储一行有非常多的列
  • 工作中主要处理的场景:对列进行处理
select name,count(*) from table group by name;
  • 列族的设计:将列进行分组
  • 列族的规则:将拥有相似IO属性的列划分为同一列族
  • 要读一起读,要写一起写
  • 底层实现
  • Store:每个Region中根据列族将不同列族的列存储在不同的Store
  • 设计规则
  • 个数原则:Hbase列族的个数有一定的要求,大部分情况下建议给2个
  • 如果列的个数比较多:30列以上
  • 2 ~ 3
  • 如果列的个数比较少,数据量不大
  • 1个
  • 长度原则 :能满足业务需求的情况下,越短越好
  • 名称没有其他功能,只要有标示性即可
  • Hbase底层数据结构:KV结构,按列存储
  • K:rowkey+列族+列名+Timestamp
  • V:value

知识点04:聊天系统案例:需求分析

  • 目标
  • 掌握Hbase表的设计:Rowkey设计、列族设计
  • 熟练应用Hbase Java API
  • 需求
  • 基于Hbase来设计聊天系统的数据存储
  • 需求1:当用户聊天时,将用户的聊天的信息写入Hbase
  • A发送消息给B
发送人ID 接受者ID 时间    内容……
  • 写入Hbase
  • 需求2:允许用户查询聊天记录:指定时间查询两个用户的聊天信息
  • 查询某个时间当前用户与其他用户的聊天记录
  • 时间戳
  • 发送人ID
  • 接收人ID
  • 数据
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m51LYFsL-1616666919956)(20210322_分布式NoSQL列存储数据库Hbase(五).assets/image-20210322092911806.png)]
消息时间 发件人昵称 发件人账号 发件人性别 发件人IP 发件人系统 发件人手机型号 发件人网络制式 发件人GPS 收件人昵称 收件人IP 收件人账号 收件人系统 收件人手机型号 收件人网络制式 收件人GPS 收件人性别 消息类型 双方距离 消息
msg_time sender_nickyname sender_account sender_sex sender_ip sender_os sender_phone_type sender_network sender_gps receiver_nickyname receiver_ip receiver_account receiver_os receiver_phone_type receiver_network receiver_gps receiver_sex msg_type distance message
2020/05/08 15:11:33 古博易 14747877194 48.147.134.255 Android 8.0 小米 Redmi K30 4G 94.704577,36.247553 莱优 97.61.25.52 17832829395 IOS 10.0 Apple iPhone 10 4G 84.034145,41.423804 TEXT 77.82KM 天涯海角惆怅渡,牛郎织女隔天河。佛祖座前长顿首,只求共度一百年。

知识点05:聊天系统案例:Hbase表设计

  • NameSpace的设计
create_namespace 'MOMO_CHAT'
  • 表的设计
  • Rowkey设计:每一条聊天记录都是一个Rowkey的数据,Rowkey怎么设计?
  • 业务原则
  • 唯一原则
  • 散列原则
  • 组合原则
  • 长度原则
  • 需求:根据时间戳、发件人ID、收件人ID查询数据

         
  • Rowkey:MD5【发件人_收件人_时间戳】取8位_发件人_收件人_时间戳

         
  • 发件人_收件人_时间戳

         
  • 列族设计
  • 个数原则
  • 长度原则
  • 需求:存储聊天记录所有列,列的个数不多,给定1个列族
C1
  • 列的设计
  • 直接使用数据中的每一列即可
  • 预分区
  • 规则:划分多个分区,根据Rowkey的设计划分
  • Hbase中提供了数字与字母组合的指定分区个数的方式:HexStringSplit
  • 建表语句
create 'MOMO_CHAT:MSG', {NAME => "C1", COMPRESSION => "GZ"}, { NUMREGIONS => 6, SPLITALGO => 'HexStringSplit'}
  • COMPRESSION:压缩
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fa0jBwS0-1616666919959)(20210322_分布式NoSQL列存储数据库Hbase(五).assets/image-20210322094757899.png)]

知识点06:聊天系统案例:环境准备

  • 参考附录一导入Maven依赖
  • 构建以下包,并依次添加代码
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AdmYCQsN-1616666919960)(20210322_分布式NoSQL列存储数据库Hbase(五).assets/image-20210321214639429.png)]
  • entity:实体类,JavaBean
  • Msg:用于将每一条消息封装为一个对象
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kRiU1ypz-1616666919961)(20210322_分布式NoSQL列存储数据库Hbase(五).assets/image-20210322101623133.png)]
  • service:应用层
  • ChatMessageService:用于实现用户数据查询的接口
public interface ChatMessageService {
    //根据条件进行查询方法
    List<Msg> getMessage(String date, String sender, String receiver) throws Exception;
    //关闭资源方法
    void close();
}
  • 给定时间、发送人、接收人、返回所有聊天记录
  • tool:工具类
  • ExcelReader:用于读取Excel表中的数据,从每一列中随机选择一个值,模拟生成用户之间的聊天数据
  • readXlsx:用于接收Excel文件地址和要读取的表名,将每一列和这一列的所有数据构建一个KV
//参数一:Excel文件的地址
//参数二:指定读取哪个表格
  • //返回值:Map集合
K:String:列名称
V:List:这一列对应的所有数据
public static Map<String, List> readXlsx(String path, String sheetName)
```
  • randomColumn:随机的从某一列总取某一个值
//参数一:Excel中的所有数据
    //参数二:指定获取的列名
    //返回值:从指定列名中随机返回一个值
public static String randomColumn(Map<String, List<String>> resultMap, String columnName)
    ```
  - getOneMsg:用于从整张Excel表中让每一列都随机产生一个值,构建一条消息数据
    ```java
    //参数:Excel中的所有数据
    //返回值:模拟的数据对应的Msg对象
    public static Msg getOneMsg(Map<String, List<String>> resultMap)
    ```
  - 运行测试,得到一条数据
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3aHK6cHg-1616666919962)(20210322_分布式NoSQL列存储数据库Hbase(五).assets/image-20210322103055968.png)]

知识点07:聊天系统案例:模拟生成数据

  • 目标
  • 模拟产生用户聊天数据,将每条聊天数据写入Hbase表中
  • 路径
  • step1:读取Excel文件,读取指定表格
  • step2:从表格的每一列中随机生成一条数据,构建一条模拟数据
  • step3:将模拟数据封装在一个Msg对象中
  • 实现
  • 读取Excel文件
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XPxKWFGg-1616666919964)(20210322_分布式NoSQL列存储数据库Hbase(五).assets/image-20210322105117623.png)]
  • 随机取某一列的一个值
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9a9g0u0B-1616666919964)(20210322_分布式NoSQL列存储数据库Hbase(五).assets/image-20210322105145249.png)]
  • 随机生成一条数据
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hm39BwML-1616666919965)(20210322_分布式NoSQL列存储数据库Hbase(五).assets/image-20210322105206464.png)]
  • 总结
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GvWhnY47-1616666919965)(20210322_分布式NoSQL列存储数据库Hbase(五).assets/image-20210322103055968.png)]

知识点08:聊天系统案例:构建Rowkey

  • 目标
  • 基于表的设计构建Rowkey
  • HbaseJava API
  • step1:构建连接
  • step2:构建Table对象
  • step3:写入数据
table.put(Put)
  • 路径
  • step1:根据每条模拟数据,获取时间、发送人ID、接收人ID
  • step2:基于时间戳、发送人ID、接收人ID构建MD5编码
  • step3:使用MD5前8位+发送人ID+接收人ID+时间戳,构建Rowkey返回
  • 实现
//构建rowkey,根据每条数据的内容来生成rowkey:ROWKEY = MD5Hash8位_发件人账号_收件人账号_时间戳
    private static String getRowKey(Msg msg) throws Exception {
        //构建一个动态字符串,用于拼接Rowkey
        StringBuilder stringBuilder = new StringBuilder(msg.getSender_account());
        stringBuilder.append("_");
        stringBuilder.append(msg.getReceiver_account());
        stringBuilder.append("_");
        //转换时间戳
        stringBuilder.append(input.parse(msg.getMsg_time()).getTime());
        //构建MD5前缀
        byte[] orginkey = Bytes.toBytes(stringBuilder.toString());
        String md5AsHex = MD5Hash.getMD5AsHex(orginkey).substring(0, 8);
        //返回最终rowkey
        return md5AsHex + "_"+stringBuilder.toString();
    }
  • 总结
  • Rowkey必须根据需求提前设计
  • 在构建Put等DML操作时,提前基于数据构建Rowkey即可

知识点09:聊天系统案例:测试写入代码

  • 目标
  • 将每条模拟数据写入Hbase
  • 路径
  • step1:基于Rowkey构建Put对象
  • step2:获取模拟数据中每一列,添加到Put对象中
  • step3:对表执行Put操作,将每条模拟数据,构建一个Put,写入hbase表中中,观察结果
  • 实现
//写入Hbase表
    private static void writeToHbase(Table table) throws Exception {
        for(int i = 0;i<100 ;i++){
            //step1:先获取要写入的数据
            Map<String, List<String>> maps = ExcelReader.readXlsx("datas/momo/测试数据集.xlsx", "陌陌数据");
            Msg msg = ExcelReader.getOneMsg(maps);
            //测试
//            System.out.println(msg);
            //step2:构建rowkey
            String rowkey = getRowKey(msg);
//            System.out.println(rowkey);
            //step3:写入Hbase,构建Put对象
            Put put = new Put(Bytes.toBytes(rowkey));
            //添加每一列到put对象中
//            put.addColumn(列族、列、值)
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_msg_time), Bytes.toBytes(msg.getMsg_time()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_sender_nickyname), Bytes.toBytes(msg.getSender_nickyname()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_sender_account), Bytes.toBytes(msg.getSender_account()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_sender_sex), Bytes.toBytes(msg.getSender_sex()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_sender_ip), Bytes.toBytes(msg.getSender_ip()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_sender_os), Bytes.toBytes(msg.getSender_os()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_sender_phone_type), Bytes.toBytes(msg.getSender_phone_type()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_sender_network), Bytes.toBytes(msg.getSender_network()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_sender_gps), Bytes.toBytes(msg.getSender_gps()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_receiver_nickyname), Bytes.toBytes(msg.getReceiver_nickyname()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_receiver_ip), Bytes.toBytes(msg.getReceiver_ip()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_receiver_account), Bytes.toBytes(msg.getReceiver_account()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_receiver_os), Bytes.toBytes(msg.getReceiver_os()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_receiver_phone_type), Bytes.toBytes(msg.getReceiver_phone_type()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_receiver_network), Bytes.toBytes(msg.getReceiver_network()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_receiver_gps), Bytes.toBytes(msg.getReceiver_gps()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_receiver_sex), Bytes.toBytes(msg.getReceiver_sex()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_msg_type), Bytes.toBytes(msg.getMsg_type()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_distance), Bytes.toBytes(msg.getDistance()));
            put.addColumn(Bytes.toBytes(cf_name), Bytes.toBytes(col_message), Bytes.toBytes(msg.getMessage()));
            //让表执行Put操作
            table.put(put);
        }
    }
  • 总结
  • 写入hbase只要设计好rowkey,构建Put,添加列,执行即可
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jaacGobF-1616666919966)(20210322_分布式NoSQL列存储数据库Hbase(五).assets/image-20210322110352464.png)]

知识点10:聊天系统案例:查询需求分析

  • 目标
  • 根据时间【日期】、发送人ID、接收人ID查询用户在指定时间的聊天记录
  • Hbase JavaAPI
  • step1:构建连接
  • step2:构建Table
  • step3:读分为两种
  • Get:给定rowkey,只返回一条数据
  • Scan:基于各种条件的复杂查询,返回多条数据
  • 路径
  • step1:指定时间[2021-03-22]、发送人ID、接收人ID查询条件,构建Scan
  • step2:根据条件构建过滤器
ff98a62c_17351912952_17742251415_1616381252000
  • Rowkey前缀范围过滤:startrow,stoprow
  • Rowkey前缀过滤:PrefixFIlter
  • 列值过滤:SingleColumnValueFilter
  • 发送人ID = 给定的发送人ID
  • 收件人ID = 给定的收件人ID
  • 开始时间:2021-03-22 00:00:00【闭区间】
  • 结束时间:2021-03-23 00:00:00【开区间】
  • 四个条件:并且:and
  • 列的过滤:MultipleColumnPrefixFilter
  • step3:在Scan中加载过滤器
  • 实现
//构建返回值
        List<Msg> msgs = new ArrayList<>();
        //step1:先构建连接
        Configuration conf = HBaseConfiguration.create();
        conf.set("hbase.zookeeper.quorum","node1:2181,node2:2181,node3:2181");
        conn = ConnectionFactory.createConnection(conf);
        //step2:构建表的对象
        Table table = conn.getTable(tbName);
        //step3:构建Scan + Filter
        Scan scan = new Scan();
        String startTime = date+" 00:00:00";
        String endTime = date+" 23:59:59";
        SingleColumnValueFilter valueFilter1 = new SingleColumnValueFilter(
                Bytes.toBytes(cf_name),
                Bytes.toBytes("msg_time"),
                CompareOperator.GREATER_OR_EQUAL,
                Bytes.toBytes(startTime));
        SingleColumnValueFilter valueFilter2 = new SingleColumnValueFilter(
                Bytes.toBytes(cf_name),
                Bytes.toBytes("msg_time"),
                CompareOperator.LESS_OR_EQUAL,
                Bytes.toBytes(endTime));
        SingleColumnValueFilter valueFilter3 = new SingleColumnValueFilter(
                Bytes.toBytes(cf_name),
                Bytes.toBytes("sender_account"),
                CompareOperator.EQUAL,
                Bytes.toBytes(sender));
        SingleColumnValueFilter valueFilter4 = new SingleColumnValueFilter(
                Bytes.toBytes(cf_name),
                Bytes.toBytes("receiver_account"),
                CompareOperator.EQUAL,
                Bytes.toBytes(receiver));
        FilterList list = new FilterList();
        list.addFilter(valueFilter1);
        list.addFilter(valueFilter2);
        list.addFilter(valueFilter3);
        list.addFilter(valueFilter4);
        scan.setFilter(list);
  • 总结
  • step1:分析需求,使用哪种过滤器
  • step2:构建Scan和过滤器即可

知识点11:聊天系统案例:测试查询代码

  • 目标
  • 将查询结果,输出打印
  • 路径
  • step1:将返回的每条Rowkey的数据中的每一列取出
  • step2:将每一列赋值为Msg对象的对应属性
  • step3:输出每一条Msg数据
  • 实现
ResultScanner scanner = table.getScanner(scan);
        //step5:将每个rowkey的数据取出转换为Msg对象,放入集合中
        for (Result result : scanner) {
            //每个rowkey构建一个Msg对象
            Msg msg = new Msg();
            //读取Rowkey中每一列的数据赋值给msg的属性
//            result.getValue(列族,列)
            msg.setSender_nickyname(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_sender_nickyname))));
            msg.setSender_account(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_sender_account))));
            msg.setSender_sex(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_sender_sex))));
            msg.setSender_ip(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_sender_ip))));
            msg.setSender_os(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_sender_os))));
            msg.setSender_phone_type(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_sender_phone_type))));
            msg.setSender_network(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_sender_network))));
            msg.setSender_gps(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_sender_gps))));
            msg.setReceiver_nickyname(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_receiver_nickyname))));
            msg.setReceiver_account(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_receiver_account))));
            msg.setReceiver_sex(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_receiver_sex))));
            msg.setReceiver_ip(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_receiver_ip))));
            msg.setReceiver_os(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_receiver_os))));
            msg.setReceiver_phone_type(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_receiver_phone_type))));
            msg.setReceiver_network(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_receiver_network))));
            msg.setReceiver_gps(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_receiver_gps))));
            msg.setDistance(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_distance))));
            msg.setMsg_type(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_msg_type))));
            msg.setMessage(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_message))));
            msg.setMsg_time(Bytes.toString(result.getValue(Bytes.toBytes(cf_name),Bytes.toBytes(col_msg_time))));
            //将msg放入集合
            msgs.add(msg);
        }
        return msgs;
  • 总结[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OteMzxhZ-1616666919966)(20210322_分布式NoSQL列存储数据库Hbase(五).assets/image-20210322112127417.png)]
  • step1:执行Scan,得到所有符合条件的数据
  • step2:迭代每条数据进行处理即可

知识点12:聊天系统案例:查询问题

  • 问题
  • 根据当前Rowkey的设计以及当前的需求,无法实现基于索引查询,性能较差,如何解决?
  • 原因
  • Rowkey的设计:MD58位+发件人+收件人+消息时间戳
  • 索引查询:只有根据Rowkey或者Rowkey的前缀查询,才走索引查询
  • 必须:发件人+收件人+消息时间戳
  • 查询条件:发件人、收件人、时间【天】
  • 问题:即使Rowkey中包含了我们经常查询的列,但凡查询条件不在Rowkey中,不能做索引查询,怎么解决?
  • 假设
rowkey:id_name_age    id      name  age   addr    phone
  • 走索引
  • 查询id等于001的所有信息
  • 查询id等于001名字为张三的所有信息
  • 查询id等于001名字为张三,并且年龄为18的所有信息
  • 不走索引:全表扫描
  • 查询张三的所有信息
  • 查询18岁的所有信息
  • 查询上海的所有数据信息
  • 查询号码为110的所有信息
  • 解决
  • 二级索引

知识点13:二级索引

  • 目标
  • 基于一级索引之上,构建一层索引,通过走两次索引来代替全表扫描
  • Hbase中的唯一索引是Rowkey
  • 目标:得到所有符合条件的数据
  • 思考:能不能先得到所有符合条件的rowkey,再通过rowkey得到所有符合条件的数据
  • 路径
  • step1:先构建原始数据表
  • step2:根据查询需求,构建索引表
  • step3:先查询索引表,再查询原始数据表
  • 实现
  • 原始数据表
rowkey:name_id      id      name      age     sex     addr
zhangsan_001      001     zhangsan    18      male    shanghai
lisi_002        002     lisi      18      female    beijing
zhangsan_003      003     zhangsan    20      male    
……
  • 哪些查询走索引?
  • name
  • name + id
  • 哪些查询不走索引?
  • id
  • age
  • sex
  • addr
  • 需求:根据id查询对应id的所有数据
  • 不走索引
  • 解决方案:构建二级索引
  • 原始数据表:记录所有原始数据
rowkey:name_id      id      name      age     sex     addr
zhangsan_001      001     zhangsan    18      male    shanghai
lisi_002        002     lisi      18      female    beijing
zhangsan_003      003     zhangsan    20      male    
  • 数据索引表:记录查询条件与原表Rowkey的映射关系
rowkey:id_name      col:原表的rowkey
001_zhangsan      zhangsan_001
002_lisi        lisi_002
003_zhangsan      zhangsan_003
……
  • 查询过程:查询id = 003
  • step1:先根据条件查询索引表,获取所有符合条件的原表的rowkey
  • 根据索引表的前缀匹配
003_zhangsan      zhangsan_003
  • step2:根据原表的rowkey获取数据
zhangsan_003      003     zhangsan    20      male    
  • 总结
  • 在原表之上构建索引表
  • 先查询索引表,再查询原表
  • Hive的问题
  • Hive中能否执行create index
  • 可以
  • 要求:0.7 ~ 3.x
  • 原因:Hive中索引表不会自动同步原始数据表,必须手动通过MapReduce同步索引表
  • Hbase问题:索引表如何与原表保持一致的问题
  • 方案一:当客户端往Hbase原表写入时,在客户端中也往索引表写一份
  • 不会用的
  • 优点:最简单
  • 缺点:Hbase无法保证不同表不同的rowkey的事务性,不能实现同时成功或者同时失败,性能很差
  • 方案二:协处理器,类似于Hive中的UDF
  • 自定义开发代码,让Hbase实现监听原表,如果原表的数据发生变化,索引自动发生变化
  • 优点:Hbase原生发生,可以满足原子性包括性能
  • 缺点:开发比较麻烦
  • 方案三:第三方工具
  • Phoenix:专门为Hbase所设计的工具,底层通过大量协处理器来实现,提供SQL接口
create index 
  • 自动创建索引表,维护索引同步
  • ES:ElasticSearch / Solr

附录一:Maven 依赖

<repositories>
        <repository>
            <id>aliyun</id>
            <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
        </repository>
    </repositories>
    <properties>
        <hbase.version>2.1.2</hbase.version>
    </properties>
    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.hbase/hbase-client -->
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-client</artifactId>
            <version>${hbase.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.hbase</groupId>
            <artifactId>hbase-server</artifactId>
            <version>${hbase.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!-- Xml操作相关 -->
        <dependency>
            <groupId>com.github.cloudecho</groupId>
            <artifactId>xmlbean</artifactId>
            <version>1.5.5</version>
        </dependency>
        <!-- 操作Office库 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.0.1</version>
        </dependency>
        <!-- 操作Office库 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.0.1</version>
        </dependency>
        <!-- 操作Office库 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.0.1</version>
        </dependency>
        <!-- 操作JSON -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.62</version>
        </dependency>
        <!-- phoenix core -->
        <dependency>
            <groupId>org.apache.phoenix</groupId>
            <artifactId>phoenix-core</artifactId>
            <version>5.0.0-HBase-2.0</version>
        </dependency>
        <!-- phoenix 客户端 -->
        <dependency>
            <groupId>org.apache.phoenix</groupId>
            <artifactId>phoenix-queryserver-client</artifactId>
            <version>5.0.0-HBase-2.0</version>
        </dependency>
    </dependencies>
<!-- 操作Office库 -->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml-schemas</artifactId>
        <version>4.0.1</version>
    </dependency>
    <!-- 操作JSON -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>1.2.62</version>
    </dependency>
    <!-- phoenix core -->
    <dependency>
        <groupId>org.apache.phoenix</groupId>
        <artifactId>phoenix-core</artifactId>
        <version>5.0.0-HBase-2.0</version>
    </dependency>
    <!-- phoenix 客户端 -->
    <dependency>
        <groupId>org.apache.phoenix</groupId>
        <artifactId>phoenix-queryserver-client</artifactId>
        <version>5.0.0-HBase-2.0</version>
    </dependency>
</dependencies>

         


相关实践学习
lindorm多模间数据无缝流转
展现了Lindorm多模融合能力——用kafka API写入,无缝流转在各引擎内进行数据存储和计算的实验。
云数据库HBase版使用教程
&nbsp; 相关的阿里云产品:云数据库 HBase 版 面向大数据领域的一站式NoSQL服务,100%兼容开源HBase并深度扩展,支持海量数据下的实时存储、高并发吞吐、轻SQL分析、全文检索、时序时空查询等能力,是风控、推荐、广告、物联网、车联网、Feeds流、数据大屏等场景首选数据库,是为淘宝、支付宝、菜鸟等众多阿里核心业务提供关键支撑的数据库。 了解产品详情:&nbsp;https://cn.aliyun.com/product/hbase &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
2月前
|
JSON 分布式计算 前端开发
前端的全栈之路Meteor篇(七):轻量的NoSql分布式数据协议同步协议DDP深度剖析
本文深入探讨了DDP(Distributed Data Protocol)协议,这是一种在Meteor框架中广泛使用的发布/订阅协议,支持实时数据同步。文章详细介绍了DDP的主要特点、消息类型、协议流程及其在Meteor中的应用,包括实时数据同步、用户界面响应、分布式计算、多客户端协作和离线支持等。通过学习DDP,开发者可以构建响应迅速、适应性强的现代Web应用。
|
2月前
|
存储 监控 分布式数据库
百亿级存储架构: ElasticSearch+HBase 海量存储架构与实现
本文介绍了百亿级数据存储架构的设计与实现,重点探讨了ElasticSearch和HBase的结合使用。通过ElasticSearch实现快速检索,HBase实现海量数据存储,解决了大规模数据的高效存储与查询问题。文章详细讲解了数据统一接入、元数据管理、数据一致性及平台监控等关键模块的设计思路和技术细节,帮助读者理解和掌握构建高性能数据存储系统的方法。
百亿级存储架构: ElasticSearch+HBase 海量存储架构与实现
|
2月前
|
SQL 分布式计算 Hadoop
Hadoop-37 HBase集群 JavaAPI 操作3台云服务器 POM 实现增删改查调用操作 列族信息 扫描全表
Hadoop-37 HBase集群 JavaAPI 操作3台云服务器 POM 实现增删改查调用操作 列族信息 扫描全表
38 3
|
2月前
|
分布式计算 Hadoop Shell
Hadoop-36 HBase 3节点云服务器集群 HBase Shell 增删改查 全程多图详细 列族 row key value filter
Hadoop-36 HBase 3节点云服务器集群 HBase Shell 增删改查 全程多图详细 列族 row key value filter
60 3
|
6月前
|
分布式计算 Java Hadoop
杨校老师课堂之分布式数据库HBase的部署和基本操作
杨校老师课堂之分布式数据库HBase的部署和基本操作
66 0
|
4月前
|
存储 SQL 分布式数据库
|
4月前
|
存储 监控 Shell
HBase2.1分布式部署
HBase2.1分布式部署
|
5月前
|
存储 NoSQL 大数据
大数据存储:HBase与Cassandra的对比
【7月更文挑战第16天】HBase和Cassandra作为两种流行的分布式NoSQL数据库,在数据模型、一致性模型、数据分布、查询语言和性能等方面各有千秋。HBase适用于需要强一致性和与Hadoop生态系统集成的场景,如大规模数据处理和分析。而Cassandra则更适合需要高可用性和灵活查询能力的场景,如分布式计算、云计算和大数据应用等。在实际应用中,选择哪种数据库取决于具体的需求和场景。希望本文的对比分析能够帮助读者更好地理解这两种数据库,并做出明智的选择。
|
5月前
|
存储 Java 分布式数据库
使用Spring Boot和HBase实现大数据存储
使用Spring Boot和HBase实现大数据存储
|
5月前
|
存储 Java 分布式数据库
HBase构建图片视频数据的统一存储检索
HBase构建图片视频数据的统一存储检索
下一篇
DataWorks