分布式NoSQL列存储数据库HBASE(二)

分布式NoSQL列存储数据库HBASE(二)

知识点01:

  1. Hbase的功能和应用场景是什么?

    • 功能:分布式提供大数据量的随机和实时数据存储【读写】
    • 应用:大数据量、高性能、高并发、按列存储、持久化大数据数据库【结构化或者半结构化】存储
  2. 为什么Hbase可以读写很快而且支持大数据量?

    • 读写很快:内存
    • 大数据量:磁盘
    • Hbase:分布式内存 + 分布式磁盘
      • 将刚写入的数据直接写入内存,等到内存达到一定阈值以后,将内存数据写入HDFS实现持久化存储
      • 写:内存
      • 读:内存 或者 磁盘
        • Hbase为了避免读磁盘很慢?
          • Rowkey索引
          • 二进制文件
          • 构建有序数据存储
          • 列族的设计
  3. Hbase与HDFS和Redis有什么区别?

    • Hbase:实时数据库、大数据量永久性存储
    • HDFS:离线文件系统、大数据量永久性存储
    • Redis:实时数据库、大数据量临时性存储或者小数量永久性存储
  4. 解释以下概念

    • Namespace:就是数据库的概念
    • Table:表的概念,Hbase中的表时分布式的表
    • Rowkey:行健,类似于主键,hbase表都必须自带这一列这一列的值由用户自己设计
      • 唯一标识一行
      • 作为唯一索引
      • 作为分区规则的判断条件:根据rowkey决定数据会写入哪个分区
    • ColumnFamily:列族,列的分组,任何一列都必须属于某个列族,cf:col
    • Qualifier:列标签,列的名称
    • VERSIONS:多版本,Hbase中的某一行的某一列可以存储多个版本的值,通过timestamp
    • Region:分区,实现表的分布式存储的概念
  5. Hbase的架构及角色功能是什么?

    • Hbase:由RegionServer的堆内存构建了分布式内存
      • 分布式主从架构
      • HMaster:管理节点:管理从节点,管理元数据
      • HRegionServer:存储节点:管理Region中所有数据的存储,接受客户端读写请求
    • HDFS:构建分布式磁盘存储
    • Zookeeper:辅助选举,存储管理元数据
  6. Hbase中的常用命令有哪些?

    • 场景一:运维管理,运行hbase脚本
      • hbase shell xxxxx.txt
    • 场景二:开发测试,命令行,DDL
      • namespace:create_namespace,list_namespace,drop_namespace
      • table:create[表名+列族],drop、disable、list、desc、exists
      • put tbname,rowkey,cf:col,value,ts
      • delete tbname,rowkey,cf:col
      • get tbname,rowkey,[cf:col]
      • scan tbname [Filter]
    • 场景三:生产开发,JavaAPI,DML
  7. 反馈问题

    • Hive中的表能不能算是分布式的?
    • 怎样删除一行的某一个列族了?
    • 如果用scan语法的时候有多个过滤器,过滤器的优先级是什么?可否人工进行干预优先级?
      • 优先级:只要将Rowkey的过滤器先加载
      • 逻辑关系:并列,或者

知识点02:

  1. Hbase Java API
    • 应用:Spark/MR 读写Hbase
    • DDL:创建表
    • DML:读写数据 + 过滤器
  2. Hbase存储设计
    • Table、Region、RegionServer三者之间的关系?
    • 分区的规则是什么?如何决定一个Rowkey的数据写入哪个分区?
    • 为什么要设计列族?底层Region内部的存储是什么样的?
    • Hbase存储在HDFS中的数据是什么样的结构?
  3. 热点问题
    • 类似于数据倾斜为
    • 现象、原因、解决方案
    • Rowkey的设计

知识点03:Java API:构建连接

  • 目标:实现Hbase Java API的开发构建连接

  • 实施

    //todo:1-构建连接
        Connection conn = null;
        @Before
        public void getConnect() throws IOException {
            //构建配置对象
            Configuration conf = HBaseConfiguration.create();
            //配置Hbase服务端地址:ZK
            conf.set("hbase.zookeeper.quorum","node1:2181,node2:2181,node3:2181");
            //构建连接实例
            conn = ConnectionFactory.createConnection(conf);
        }
    
        //todo:2-基于连接的方法实现操作
    
        //todo:3-释放连接
        @After
        public void closeConnect() throws IOException {
            conn.close();
        }
    
  • 小结

    • 用到了哪些类和方法?
      • Configuration:配置对象
      • Connection:连接对象
      • ConnectionFactory:连接工厂类
        • createConnection(conf)

知识点04:Java API:DDL

  • 目标:使用Hbase Java API实现DDL的管理

  • 实施

    • 构建管理员:Java API中所有的DDL操作都是由管理员对象构建的

      //构建管理员对象
          public HBaseAdmin getHbaseAdmin() throws IOException {
              HBaseAdmin admin = (HBaseAdmin) conn.getAdmin();
              return admin;
          }
      
    • 实现DDL管理

      • 列举、创建、删除NS
      • 列举、创建、删除Table
      package bigdata.itcast.cn.hbase.client;
      
      import org.apache.hadoop.conf.Configuration;
      import org.apache.hadoop.hbase.HBaseConfiguration;
      

    import org.apache.hadoop.hbase.NamespaceDescriptor;
    import org.apache.hadoop.hbase.TableName;
    import org.apache.hadoop.hbase.client.*;
    import org.apache.hadoop.hbase.util.Bytes;
    import org.junit.After;
    import org.junit.Before;
    import org.junit.Test;

    import java.io.IOException;
    import java.util.List;

    /**

    • @ClassName HbaseJavaClientDDLTest

    • @Description TODO 基于Hbase Java API 实现DDL操作

    •              - 列举、创建、删除NS
      
    •              - 列举、创建、删除Table
      
    •              Java API中所有的DDL操作都是由管理员对象构建的
      
    • @Date 2021/6/25 9:54

    • @Create By Frank
      */
      public class HbaseJavaClientDDLTest {
      //todo:1-构建连接
      Connection conn = null;
      @Before
      public void getConnect() throws IOException {
      //构建配置对象
      Configuration conf = HBaseConfiguration.create();
      //配置Hbase服务端地址:ZK
      conf.set(“hbase.zookeeper.quorum”,“node1:2181,node2:2181,node3:2181”);
      //构建连接实例
      conn = ConnectionFactory.createConnection(conf);
      }

      //todo:2-基于连接的方法实现操作
      //构建管理员对象
      public HBaseAdmin getHbaseAdmin() throws IOException {
      HBaseAdmin admin = (HBaseAdmin) conn.getAdmin();
      return admin;
      }

      @Test
      public void listNS() throws IOException {
      HBaseAdmin admin = getHbaseAdmin();
      //实现列举NS
      NamespaceDescriptor[] namespaceDescriptors = admin.listNamespaceDescriptors();
      //打印名称
      for (NamespaceDescriptor namespaceDescriptor : namespaceDescriptors) {
      System.out.println(namespaceDescriptor.getName());
      }
      //关闭管理员
      admin.close();
      }

      @Test
      public void delNs() throws IOException {
      HBaseAdmin admin = getHbaseAdmin();
      admin.deleteNamespace(“heima”);
      admin.close();
      }

      @Test
      public void createNS() throws IOException {
      HBaseAdmin admin = getHbaseAdmin();
      //创建
      NamespaceDescriptor descriptor = NamespaceDescriptor
      .create(“heima”)//指定NS名称
      .build();
      admin.createNamespace(descriptor);
      admin.close();
      }

      @Test
      public void listTables() throws IOException {
      HBaseAdmin admin = getHbaseAdmin();
      //列举所有表
      List tableDescriptors = admin.listTableDescriptors();
      //打印表名
      for (TableDescriptor tableDescriptor : tableDescriptors) {
      System.out.println(tableDescriptor.getTableName().getNameAsString());
      }
      admin.close();
      }

      @Test
      public void createTb() throws IOException {
      HBaseAdmin admin = getHbaseAdmin();
      //构建表名对象
      TableName tbname = TableName.valueOf(“itcast:t1”);
      //判断表是否存在
      if(admin.tableExists(tbname)){
      //禁用表
      admin.disableTable(tbname);
      //删除表
      admin.deleteTable(tbname);
      }
      //构建列族对象
      ColumnFamilyDescriptor basic = ColumnFamilyDescriptorBuilder
      .newBuilder(Bytes.toBytes(“basic”))
      .build();
      ColumnFamilyDescriptor other = ColumnFamilyDescriptorBuilder
      .newBuilder(Bytes.toBytes(“other”))
      .setMaxVersions(3)
      .build();
      //创建表
      TableDescriptor desc = TableDescriptorBuilder
      .newBuilder(tbname)
      .setColumnFamily(basic)
      .setColumnFamily(other)
      .build();
      admin.createTable(desc);
      admin.close();
      }

      //todo:3-释放连接
      @After
      public void closeConnect() throws IOException {
      conn.close();
      }
      }

    
    
    
  • 小结

    • 用到了哪些类和方法?
      • HbaseAdmin:管理员对象,实现DDL操作
        • listNameSpaceDescriptors
        • listTableDescriptor
        • createNameSpace
        • createTable
        • deleteNameSpace
        • deleteTable
        • dropTable
        • existsTable
      • NameSpaceDescriptor:Namespace的对象
      • TableDescriptor:表的对象
        • setColumnFamily:添加列族的方法
      • TableName:表名对象
      • ColumnFamilyDescriptor:列族的对象
        • setMaxVersions:设置最大版本

知识点05:Java API:DML:Table

  • 目标:使用Hbase Java API实现Table的实例开发

  • 实施

    • DML操作都必须构建Hbase表的对象来进行操作

      //构建Hbase表的对象
          public Table getHbaseTable() throws IOException {
              TableName tbname = TableName.valueOf("itcast:t1");
              Table table = conn.getTable(tbname);
              return table;
          }
      
  • 小结

    • 使用Hbase Java API实现Table的实例开发

知识点06:Java API:DML:Put

  • 目标:使用Hbase Java API实现Put插入或者更新数据

  • 实施

        @Test
        public void testPut() throws IOException {
            Table table = getHbaseTable();
            //构建Put对象,一个Put对象表示写入一个Rowkey的数据
            Put put = new Put(Bytes.toBytes("20210101_001"));
            //添加列的信息
            put.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("name"),Bytes.toBytes("laoda"));
            put.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("age"),Bytes.toBytes("18"));
            put.addColumn(Bytes.toBytes("other"),Bytes.toBytes("phone"),Bytes.toBytes("110"));
            //执行Put
            table.put(put);
            table.close();
        }
    
    
  • 小结

    • 有哪些类和方法?

      • Table:表的对象
      • put(Put | List)
      • Put:Put对象实例
        • addColumn(列族,列,TS,值)

知识点07:Java API:DML:Get

  • 目标:使用Hbase Java API实现Get读取数据

  • 实施

    • 插入数据

      put 'itcast:t1','20210201_000','basic:name','laoda'
      put 'itcast:t1','20210201_000','basic:age',18
      
      put 'itcast:t1','20210101_001','basic:name','laoer'
      put 'itcast:t1','20210101_001','basic:age',20
      put 'itcast:t1','20210101_001','basic:sex','male'
      
      put 'itcast:t1','20210228_002','basic:name','laosan'
      put 'itcast:t1','20210228_002','basic:age',22
      put 'itcast:t1','20210228_002','other:phone','110'
      
      put 'itcast:t1','20210301_003','basic:name','laosi'
      put 'itcast:t1','20210301_003','basic:age',20
      put 'itcast:t1','20210301_003','other:phone','120'
      put 'itcast:t1','20210301_003','other:addr','shanghai'
      
    • 实现Get

          @Test
          public void testGet() throws IOException {
              Table table = getHbaseTable();
              //构建Get:get tbname,rowkey,[cf:col]
              Get get = new Get(Bytes.toBytes("20210301_003"));
              //配置Get
              get.addFamily(Bytes.toBytes("basic"));//指定列族读取
      //        get.addColumn()//指定列读取
              //执行:一个Result代表一个Rowkey的数据
              Result result = table.get(get);
              //打印这个rowkey每一列的结果:一个Cell对象就是一列的对象:20210301_003 column=other:phone, timestamp=1624590747738, value=120
              for(Cell cell : result.rawCells()){
                  System.out.println(
                          Bytes.toString(CellUtil.cloneRow(cell)) + "\t" +
                          Bytes.toString(CellUtil.cloneFamily(cell)) + "\t" +
                          Bytes.toString(CellUtil.cloneQualifier(cell)) + "\t" +
                          Bytes.toString(CellUtil.cloneValue(cell)) + "\t" +
                          cell.getTimestamp()
                  );
              }
              table.close();
          }
      
  • 小结

    • 有哪些类和方法?

      • Table:表的对象
      • .get(Get | List)
      • Get:Get对象
        • .addFamily
        • .addColumn
      • Result:一个Result代表一个Rowkey的数据
        • .rawCells:返回所有列的数组
      • Cell:一个Cell代表一列的数据
      • CellUtils:从Cell中取值的工具类
        • cloneValue
        • cloneRow
        • ……

知识点08:Java API:DML:Delete

  • 目标:使用Hbase Java API实现Delete删除数据

  • 实施

        @Test
        public void testDel() throws IOException {
            Table table = getHbaseTable();
            //构建Delete
            Delete del = new Delete(Bytes.toBytes("20210301_003"));
            //删除列族
    //        del.addFamily()
            //删除列
            del.addColumn(Bytes.toBytes("other"),Bytes.toBytes("phone"));
    //        del.addColumns(Bytes.toBytes("other"),Bytes.toBytes("phone"));//删除所有版本
            //执行删除
            table.delete(del);
            table.close();
        }
    
  • 小结

    • 有哪些类和方法?
    • Delete:删除对象
      • .addFamily:基于列族删除
      • .addColumn:基于列删除
      • Table:表的对象
        • .delete

知识点09:Java API:DML:Scan

  • 目标:使用Hbase Java API实现Scan读取数据

  • 实施

        @Test
        public void testScan () throws IOException {
            Table table = getHbaseTable();
            //构建Scan对象
            Scan scan = new Scan();
            //执行scan:ResultScanner用于存储多个Rowkey的数据,是Result的集合
            ResultScanner scanner = table.getScanner(scan);
            //取出每个Rowkey的数据
            for (Result result : scanner) {
                //先打印当前这个rowkey的内容
                System.out.println(Bytes.toString(result.getRow()));
                for(Cell cell : result.rawCells()){
                    System.out.println(
                            Bytes.toString(CellUtil.cloneRow(cell)) + "\t" +
                                    Bytes.toString(CellUtil.cloneFamily(cell)) + "\t" +
                                    Bytes.toString(CellUtil.cloneQualifier(cell)) + "\t" +
                                    Bytes.toString(CellUtil.cloneValue(cell)) + "\t" +
                                    cell.getTimestamp()
                    );
                }
                System.out.println("------------------------------------------------------------");
            }
            table.close();
        }
    
  • 小结

    • 有哪些类和方法?
    • Scan:查询整表数据的对象
      • Table:表的对象
        • .getScanner(Scan)
      • ResultScanner:多个Rowkey的数据
        • Result:单个Rowkey的数据
          • Cell:每个列的数据

知识点10:Java API:DML:Filter

  • 目标:使用Hbase Java API实现Scan + Filter过滤

  • 实施

    • 需求:JavaAPI实现从Hbase表中根据条件读取部分

      • 需求1:查询2021年1月和2月的数据
      • 需求2:查询2021年的所有数据
      • 需求3:查询所有age = 20的数据
      • 需求4:查询所有数据的name和age这两列
      • 需求5:查询所有年age = 20的人的name和age
    • 实现

          @Test
          public void testScan () throws IOException {
              Table table = getHbaseTable();
              //构建Scan对象
              Scan scan = new Scan();
              /**
               * todo:配置Scan实现过滤操作
               * - 需求1:查询2021年1月和2月的数据
               *          select * from tb where substr(time,0,7) >= 202101 and time <= 202102
               * - 需求2:查询2021年的所有数据
               *          select * from tb where substr(time,0,4) = 2021
               * - 需求3:查询所有age = 20的数据
               *          select * from tb where age = 20
               * - 需求4:查询所有数据的name和age这两列
               *          select name,age from tb;
               * - 需求5:查询所有年age = 20的人的name和age
               *          select name,age from tb where age = 20
              */
              //startrow and stoprow 实现Rowkey范围过滤,如果不是Rowkey的范围过滤:RowFilter、FamilyFilter、QualifierFilter、ValueFilter
      //        scan.withStartRow(Bytes.toBytes("202101"));
      //        scan.withStopRow(Bytes.toBytes("202103"));
              //Rowkey前缀过滤器
      //        Filter prefixFilter = new PrefixFilter(Bytes.toBytes("202102"));
              //单列列值过滤器
              Filter singleColumnValueFilter = new SingleColumnValueFilter(
                      Bytes.toBytes("basic"),
                      Bytes.toBytes("age"),
                      CompareOperator.EQUAL,
                      Bytes.toBytes("20")
              );
              //多列前缀过滤器
              byte[][] prefixes = {
                      Bytes.toBytes("name"),
                      Bytes.toBytes("age")
              };
              Filter multiColumnPrefixFilter = new MultipleColumnPrefixFilter(prefixes);
      
              //构建过滤器集合
              FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ONE);
              filterList.addFilter(singleColumnValueFilter);
              filterList.addFilter(multiColumnPrefixFilter);
              //Scan加载过滤器
              scan.setFilter(filterList);
      
              //执行scan:ResultScanner用于存储多个Rowkey的数据,是Result的集合
              ResultScanner scanner = table.getScanner(scan);
              //取出每个Rowkey的数据
              for (Result result : scanner) {
                  //先打印当前这个rowkey的内容
                  System.out.println(Bytes.toString(result.getRow()));
                  for(Cell cell : result.rawCells()){
                      System.out.println(
                              Bytes.toString(CellUtil.cloneRow(cell)) + "\t" +
                                      Bytes.toString(CellUtil.cloneFamily(cell)) + "\t" +
                                      Bytes.toString(CellUtil.cloneQualifier(cell)) + "\t" +
                                      Bytes.toString(CellUtil.cloneValue(cell)) + "\t" +
                                      cell.getTimestamp()
                      );
                  }
                  System.out.println("------------------------------------------------------------");
              }
              table.close();
          }
      
  • 小结

    • 有哪些常用的Filter?

      • Rowkey范围过滤:startrow,stoprow
    • Rowkey前缀过滤:PrefixFilter

      • 列值过滤:SingleColumnValueFilter

      • 列的过滤:MultipleColumnPrefixFilter

      • 组合过滤:FilterList

      • 方法:scan.setFilter

知识点11:存储设计:存储架构

  • 目标:掌握Hbase的存储架构

  • 实施

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ju16nRpy-1624621141663)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210524170928749.png)]

    • 问题:Hbase整体如何实现数据的存储?

    • 分析

      • Client:负责连接服务端

        • 提交用户操作给服务端执行

        • 将服务端执行的结果返回给用户

      • Zookeeper:存储Hbase管理元数据

        • Hbase中的有哪些Master、RegionServer
      • Hbase:分布式内存

        • RegionServer的Java堆内存
      • HDFS:分布式磁盘

        • 当RegionServer的内存达到一定阈值,会将内存中的数据写入HDFS
  • 小结

    • 掌握Hbase的存储架构

知识点12:存储设计:Table、Region、RS的关系

  • 目标:掌握Hbase中Table与Region、RS三者之间的关系

  • 实施

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UReIocxr-1624621141665)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210524171650278.png)]

    • 问题:客户端操作的是表,数据最终存在RegionServer中,表和RegionServer的关系是什么?

      • client:put ns:tbname rowkey cf col value
    • 分析

      • Table:是一个逻辑对象,物理上不存在,供用户实现逻辑操作,存储在元数据的一个概念

        • 类似于HDFS中文件
        • 数据写入表以后的物理存储:分区
        • 一张表会有多个分区Region,每个分区存储在不同的机器上
        • 默认每张表只有1个Region分区
    • Region:Hbase中数据存储的最小单元

      • 类似于HDFS中Block,用于实现Hbase中分布式

      • 就是分区的概念,每张表都可以划分为多个Region,实现分布式存储

        • 默认一张表只有一个分区
      • 每个Region由一台RegionServer所管理,Region存储在RegionServer

      • 一台RegionServer可以管理多个Region

      • RegionServer:是一个物理对象,Hbase中的一个进程,管理一台机器的存储

        • 类似于HDFS中DataNode
        • 一个Regionserver可以管理多个Region
      • 一个Region只能被一个RegionServer所管理

    • 观察监控

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FrlU1tL9-1624621141666)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210525114439044.png)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mKtmpRs3-1624621141666)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210525114753868.png)]

      itcast:t1,,1624588882208.2aa671a470e2f179a86c2fe662d1a910.
      表名,这个Region开始范围,时间戳.Region的唯一编号
      
  • 小结

    • Hbase中Table与Region、RS三者之间的关系是什么?
      • Table:分布式表,逻辑概念
        • 一张表可以有多个Region,默认只有1个
      • RegionServer:存储节点,物理概念,进程
        • 每台RegionServer管理多个Region

知识点13:存储设计:Region的划分规则

  • 目标掌握Hbase中表的Region的划分规则

  • 实施

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2ZXHaJdA-1624621141667)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210524171757479.png)]

    • 问题:一张表划分为多个Region,划分的规则是什么?写一条数据到表中,这条数据会写入哪个Region,分配规则是什么?

    • 分析

      • 回顾:HDFS划分规则

        • 划分分区的规则:按照大小划分,文件按照每128M划分一个Block
      • Hbase分区划分规则范围划分【根据Rowkey范围】

        • 任何一个Region都会对应一个范围

          • 如果只有一个Region,范围:-oo ~ +oo
        • 范围划分:从整个-oo ~ +oo区间上进行范围划分

          • 每个分区都会有一个范围:根据Rowkey属于哪个范围就写入哪个分区

            [startKey,stopKey)
            
            • 前闭后开区间
        • 默认:一张表创建时,只有一个Region

          • 范围:-oo ~ +oo
        • 自定义:创建表时,指定有多少个分区,每个分区的范围

          • 举个栗子:创建一张表,有2个分区Region
          • region0:-oo ~ 50
          • region1:50 ~ +oo
      • 数据分配的规则:根据Rowkey属于哪个范围就写入哪个分区

        • 举个栗子:创建一张表,有4个分区Region,20,40,60

          • region0:-oo ~ 20
          • region1:20 ~ 40
          • region2:40 ~ 60
          • region3:60 ~ +oo
        • 写入数据的rowkey:比较是按照ASC码比较的,不是数值比较

          • A1234:region3
          • c6789:region3
          • 00000001:region0
          • 2:region1
          • 99999999:region3
          • 9:region3
        • 问题

          Hbase中对数值构建的序列:按照ASC码表实现的
          1
          11
          2
          23
          4
          如果我想基于数值构建有序:Rowkey补0
          01
          02
          04
          11
          23
          
    • 观察监控

      • 默认只有1个分区

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JHSJoUjI-1624621141669)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210525120031461.png)]

        • 注意:随着数据越来越多,达到阈值,这个分区会自动分裂为两个分裂
      • 手动创建多个分区

        create 'itcast:t3','cf',SPLITS => ['20', '40', '60', '80']
        

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uBiRVVqI-1624621141670)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210525120214465.png)]

        • 写入数据

          put 'itcast:t3','0300000','cf:name','laoda'
          put 'itcast:t3','7890000','cf:name','laoer'
          

          [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jy12q16K-1624621141670)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210525120338729.png)]

  • 小结

    • 掌握Hbase中表的Region的划分规则是什么?
      • 如何划分每个Region的范围:根据Rowkey的前缀
      • 如果决定数据写入哪个Region:根据Rowkey的前缀,属于哪个region的范围就写入哪个region中

知识点14:存储设计:Region的内部结构

  • 目标掌握Region的内部存储结构

  • 实施

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ENVvhHhB-1624621141670)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210524171934125.png)]

    • 问题:数据在Region的内部是如何存储的?

      put tbname,rowkey,cf:col,value
      
      • tbname:决定了这张表的数据最终要读写哪些分区
      • rowkey:决定了具体读写哪个分区
      • cf:决定具体写入哪个Store
    • 分析

      • Table/RegionServer:数据指定写入哪张表,提交给对应的某台regionserver

        • Region:对整张表的数据划分,按照范围划分,实现分布式存储

          • Store:对分区的数据进行划分,按照列族划分,一个列族对应一个Store

            - 不同列族的数据写入不同的Store中,实现了按照列族将列进行分组
            - 根据用户查询时指定的列族,可以快速的读取对应的store
            
            • MemStore:每个Store都有一个,内存存储区域
            • StoreFile:每个Store中可能有0个或者多个StoreFile文件
              • 逻辑:属于Store的
              • 物理:StoreFile是存储在HDFS中的HFILE【二进制】文件
  • 小结

    • Region的内部存储结构是什么样的?
      • RegionServer
        • Region:表的数据的划分
          • Store:分区数据的划分,按照列族划分
            • memstore:一个memstore
            • storefile:memstore满了,将memstore的数据写入HDFS变成storefile文件,0个或者多个

知识点15:存储设计:HDFS中的存储结构

  • 目标掌握Hbase在HDFS中的存储结构

  • 实施

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EYNpQLSt-1624621141671)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210524172229613.png)]

    • 问题:Hbase的数据在HDFS中是如何存储的?

    • 分析

      • 整个Hbase在HDFS中的存储目录

      hbase.rootdir=hdfs://node1:8020/hbase

      
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CvozeaEo-1624621141671)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210625154845286.png)]
      
      
      
      - NameSpace:目录结构
      
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CA4bXxrj-1624621141672)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210625154914849.png)]
      
      
      
      - Table:目录结构
      
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WCsVewJh-1624621141672)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210625154949303.png)]
      
      
      
      - Region:目录结构
      
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9id2B91f-1624621141673)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210625155037410.png)]
      
      
      
      - Store/ColumnFamily:目录结构
      
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lUvwSJnz-1624621141674)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210625155222749.png)]
      
      
      
      - StoreFile
      
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AY1Kyykk-1624621141674)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210625155242858.png)]
      
      - 如果HDFS上没有storefile文件,可以通过flush,手动将表中的数据从内存刷写到HDFS中
      
        ```
        flush 'itcast:t3'
        ```
      
        
      
      
  • 小结

    • 掌握Hbase在HDFS中的存储结构

知识点16:热点问题:现象及原因

  • 目标掌握热点问题的现象及原因

  • 实施

    • 现象

      在某个时间段内,大量的读写请求全部集中在某个Region中,导致这台RegionServer的负载比较高,其他的Region和RegionServer比较空闲
      
    • 问题:这台RegionServer故障的概率就会增加,整体性能降低,效率比较差

    • 原因:本质上的原因,数据分配不均衡

      • 情况一:如果这张表只有一个分区

        • 所有数据都存储在一个分区中,这个分区要响应所有读写请求,出现了热点
      • 情况二:如果这张表有多个分区,而且你的Rowkey写入时是连续的

        • 一张表有5个分区

          region0:-oo  20
          region1:20   40
          region2:40   60
          region3:60    80
          region4:80    +oo
          
        • 000001:region0

        • 000002:region0

        • ……

        • 199999:region0

        • 都写入了同一个region0分区

        • 200000:region1

        • 200001:region1

        • ……

        • 399999:region1

    • 解决:避免热点的产生

      • 构建多个分区
      • 构建不连续的rowkey
  • 小结

    • 掌握热点问题的现象及原因

知识点17:分布式设计:预分区

  • 目标:实现建表时指定多个分区

  • 实施

    • 需求:在创建表的时候,指定一张表拥有多个Region分区

    • 规则

      • 划分的目标:划分多个分区,实现分布式并行读写,将无穷区间划分为几段,将数据存储在不同分区中,实现分区的负载均衡
      • 划分的规则Rowkey或者Rowkey的前缀来划分
        • 如果不按照这个规则划分,预分区就可能没有作用
        • Rowkey:00 ~ 99
          • region0: -oo ~ 30
          • ……
          • regionN : 90 ~ +oo
        • 如果分区的设计不按照rowkey来
          • region0:-oo ~ b
          • region1: b ~ g
          • ……
          • regionN:z ~ +oo
    • 实现

      • 方式一:指定分隔段,实现预分区

        • 前提:先设计rowkey
        create 'ns1:t1', 'f1', SPLITS => ['10', '20', '30', '40']
        #将每个分割的段写在文件中,一行一个
        

      create ‘t1’, ‘f1’, SPLITS_FILE => ‘splits.txt’

      
      - 方式二:指定Region个数,自动进行Hash划分:字母和数字的组合
      
      ```shell
      #你的rowkey的前缀是数字和字母的组合 
      create 'itcast:t4', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
      

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EFWvEiaP-1624621141674)(Day27_分布式NoSQL列存储数据库HBASE(二).assets/image-20210525152034069.png)]

      • 方式三:Java API

        HBASEAdmin admin = conn.getAdmin
        admin.create(表的描述器对象,byte[][] splitsKey)
        
  • 小结

    • 实现建表时指定多个分区

知识点18:Hbase表设计:Rowkey设计

  • 目标掌握Hbase表的Rowkey的设计规则

  • 实施

    • 功能

      • 唯一标记一条数据
      • 唯一索引:rowkey的前缀是什么,决定了可以按照什么条件走索引查询
      • Region的划分,数据的分区划分
    • 需求:根据不同业务需求,来合理的设计rowkey,实现高性能的数据存储

    • 分析:不同的业务需求的表,Rowkey设计都不一样

    • 设计规则

      • 业务原则:Rowkey的设计必须贴合业务的需求,一般选择最常用的查询条件作为rowkey的前缀

        • 举个栗子:一般最常用的查询条件肯定是时间
          • timestamp_userid_orderid:订单表
          • 所有时间的查询都是走索引的
      • 唯一原则:Rowkey必须具有唯一性,不能重复,一个Rowkey唯一标识一条数据

      • 组合原则:将更多的经常作为的查询条件的列放入Rowkey中,可以满足更多的条件查询可以走索引查询

        • 举个栗子:一般最常用的查询条件肯定是时间
          • timestamp_userid_orderid:订单表
          • 查询条件:订单id、用户id、时间、价格、商品id
          • 走索引查询
            • timestamp
            • timestamp_userid
            • timestamp_userid_orderid
          • 不走索引:userid、orderid
      • 散列原则:为了避免出现热点问题,需要将数据的rowkey生成规则,构建散列的rowkey

        • 举个栗子:一般最常用的查询条件肯定是时间

          • timestamp_userid_orderid:订单表

            1624609420000_u001_o001
            1624609420001_u002_o002
            1624609420002_u003_o003
            1624609421000_u001_o004
            ……
            
          • 预分区:数值

            • region0:-oo ~ 1624
            • region1:1624 ~ 1924
            • region2:1924 ~ 2100
            • region3:2100 -2400
            • region4:2400 ~ +oo
          • 问题:出现热点

        • 解决:构建散列

          • 方案一:更换不是连续的字段作为前缀,例如用户id

            • 优点:构建散列,数据存储相对均衡
            • 缺点:必须以前缀的字段作为查询条件
          • 方案二:反转

            • 一般用于时间作为前缀,查询时候必须将数据反转再查询
            0000249064261_u001_o001
            1000249064261_u002_o002
            2000249064261_u003_o003
            0010249064261_u001_o004
            ……
            
            - region0:-oo  ~ 2
            - region1:2  ~ 4
            - region2:4 ~ 6
            - region3:6  ~ 8
            - region4:8 ~ +oo
            
          • 方案三:加盐(Slat),本质对数据进行编码

            1624609420000_u001_o001
            1624609420001_u002_o002
            1624609420002_u003_o003
            1624609421000_u001_o004
            |
            df34343jed_u001_o001
            09u9jdjkfd_u002_o002
            
            • 缺点:查询时候,也必须对查询条件加盐以后再进行查询
      • 长度原则:在满足业务需求情况下,rowkey越短越好,一般建议Rowkey的长度小于100字节

        • 原因:rowkey越长,比较性能越差,rowkey在底层的存储是冗余的
        • 问题:为了满足组合原则,rowkey超过了100字节怎么办?
        • 解决:实现编码,将一个长的rowkey,编码为8位,16位,32位
  • 小结

    • Rowkey的设计要符合哪些设计原则?
      • 业务原则:使用最常用的查询条件作为rowkey的前缀
      • 唯一原则:一个rowkey唯一标识一条数据
      • 组合原则:尽量将多个常用查询条件放在rowkey中
      • 散列原则:构建不连续的rowkey
        • 选择不连续的字段作为前缀、反转、加盐
      • 长度原则:保证业务的情况下,越短越好,100字节以内

附录一: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>
        <!-- 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>
    </dependencies>

:为了满足组合原则,rowkey超过了100字节怎么办?
- 解决:实现编码,将一个长的rowkey,编码为8位,16位,32位

  • 小结

    • Rowkey的设计要符合哪些设计原则?
      • 业务原则:使用最常用的查询条件作为rowkey的前缀
      • 唯一原则:一个rowkey唯一标识一条数据
      • 组合原则:尽量将多个常用查询条件放在rowkey中
      • 散列原则:构建不连续的rowkey
        • 选择不连续的字段作为前缀、反转、加盐
      • 长度原则:保证业务的情况下,越短越好,100字节以内

附录一: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>
        <!-- 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>
    </dependencies>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值