HBase

打印 上一主题 下一主题

主题 560|帖子 560|积分 1680

1.hbase的先容

hbase的基本简介:hbase依赖于hdfs,hbase是一个nosql数据库,是一个非关系型数据库,支持读写查询操作
hbase当中所有的数据都是byte[]
HBase中的表有如许的特点:


  • 海量存储:一个表可以有上十亿行,上百万列
  • 面向列:面向列(族)的存储和权限控制,列(族)独立检索。
  • 易扩展:Hbase的扩展性主要表现在两个方面,一个是基于上层处理本事(RegionServer)的扩展,一个是基于存储的扩展(HDFS)。
  • 高并发:开发情况下,hbase单个io延迟下降不多。实现高并发,低延迟
  • 希奇:对于为空(null)的列,并不占用存储空间,因此,表可以设计的非常希奇。
2.hbase与hadoop的关系

1、HDFS

* 为分布式存储提供文件系统

* 针对存储大尺寸的文件进行优化,不需要对HDFS上的文件进行随机读写

* 直接使用文件

* 数据模型不灵活

* 使用文件系统和处理框架

* 优化一次写入,多次读取的方式

2、HBase

* 提供表状的面向列的数据存储

* 针对表状数据的随机读写进行优化

* 使用key-value操作数据

* 提供灵活的数据模型

* 使用表状存储,支持MapReduce,依赖HDFS

* 优化了多次读,以及多次写

hbase是基于hdfs的,hbase的数据都是存储在hdfs上的,hbase是一个数据库,支持随机读写

3.Hbase数据存储架构

主节点:HMaster


  • 用于监控regionServer的健康状态,
  • 处理regionServer的故障转移
  • 处理元数据变更
  • 处理region的分配或者移除
  • 空闲时间做数据的负载平衡
从节点:HRegionServer


  • 负责存储HBase的实际数据
  • 处理分配给他的region
  • 革新缓存数据到HDFS上
  • 维护HLog
  • 负责处理region的切片
一个HRegionServer=1个HLog+很多个region
一个region=很多个store模块
一个store模块=1个memoryStore+很多个storeFile
组件:
Write-Ahead logs:Hbase读写数据的时间数据会先写在一个叫做Write-Ahead logfile的文件中,然后再写入内存中。所以在系统出现故障的时间,数据可以通过这个日志文件重修。
HFile:这是在磁盘中生存原始数据的古迹物理文件,是实际的存储文件
Store:HFile存储在Store中,一个Store对应Hbase表中的一个列族
MemStore:内存存储,位于内存中,用来生存当前数据操作,所有当数据生存在WAL中后,RegionServer会在内存中存储键值对
Region:Hbase表的分片,HBase表会根据RowKey值被切分成差别的region存储在RegionServer中,在一高RegionServer中可以有多个差别的region
4.Hbase的集群环境搭建

注意事项:Hbase强依赖于HDFS以及zookeeper,所以安装Hbase之前一定要保证Hadoop和zookeeper正常启动
第一步:下载对应的HBase的安装包

下载Hbase的安装包,下载地址如下:
http://archive.apache.org/dist/hbase/2.0.0/hbase-2.0.0-bin.tar.gz

第二步:HBASE集群部署



  • 目的实现Hbase分布式集群部署
  • 实施

    • 解压安装

      • 上传HBASE安装包到第一台机器的/export/software目录下
        1. cd /export/software/
        2. rz
        复制代码
      • 解压安装
        1. tar -zxvf hbase-2.1.0.tar.gz -C /export/server/
        2. cd /export/server/hbase-2.1.0/
        复制代码
        修改配置



      • 切换到配置文件目录下
        1. cd /export/server/hbase-2.1.0/conf/
        复制代码
      • 修改hbase-env.sh
        1. #28行
        2. export JAVA_HOME=/export/server/jdk1.8.0_65
        3. #125行
        4. export HBASE_MANAGES_ZK=false
        复制代码
      • 修改hbase-site.xml
        1. cd /export/server/hbase-2.1.0/
        2. mkdir datas
        3. vim conf/hbase-site.xml
        复制代码
        1.  <property >
        2.    <name>hbase.tmp.dir</name>
        3.    <value>/export/server/hbase-2.1.0/datas</value>
        4.  </property>
        5.    <property >
        6.    <name>hbase.rootdir</name>
        7.    <value>hdfs://node1:8020/hbase</value>
        8.  </property>
        9.  <property >
        10.    <name>hbase.cluster.distributed</name>
        11.    <value>true</value>
        12.  </property>
        13.  <property>
        14.    <name>hbase.zookeeper.quorum</name>
        15.    <value>node1,node2,node3</value>
        16.  </property>
        17.  <property>
        18.    <name>hbase.unsafe.stream.capability.enforce</name>
        19.    <value>false</value>
        20.  </property>
        复制代码
      • 修改regionservers
        1. vim conf/regionservers
        2. node1
        3. node2
        4. node3
        复制代码
      • 配置环境变量
        1. vim /etc/profile
        2. #HBASE_HOME
        3. export HBASE_HOME=/export/servers/hbase-2.1.0
        4. export PATH=:$PATH:$HBASE_HOME/bin
        5. source /etc/profile
        复制代码
      • 复制jar包
        1. cp lib/client-facing-thirdparty/htrace-core-3.1.0-incubating.jar lib/
        复制代码

    • 分发
      1. cd /export/server/
      2. scp -r hbase-2.1.0 node2:$PWD
      3. scp -r hbase-2.1.0 node3:$PWD
      复制代码
    • 服务端启动与关闭

      • step1:启动HDFS
      • step2:启动ZK
      • step3:启动Hbase
        1. start-hbase.sh
        复制代码
      • 关闭:先关闭Hbase再关闭zk
        1. stop-hbase.sh
        2. stop-zk-all.sh
        3. stop-dfs.sh
        复制代码

    • 测试

      • 访问Hbase Web UI
        1. node1:16010
        2. Apache Hbase 1.x之前是60010,1.x开始更改为16010
        3. CDH版本:一直使用60010
        复制代码

    • Web无法访问的几个问题及缘故原由
      1.假如报错404,那么则是网页访问不了
      这个问题起首检查hbase-site.xml中的端口是否和本身的hdfs端口一至,我的就是这个问题连接不上
      1. <property>
      2.         <name>hbase.rootdir</name>
      3.         <value>hdfs://node01:9000/HBase</value>
      4. </property>
      复制代码

      其次更换版本
      我更换成了2.2.4版本
      起首:删除zookeeper的注册信息;删除hbase的数据目录
      报错:

      2.报错500连接不上hadoop102:16010的web页面情况2:org.apache.hadoop.hbase.PleaseHoldException: Master is initializing

      可能前面启动失败一次或者安装过其他版本导致,因为hbase基于zookeeper,所以在zookeeper上已经注册了之前的hbase信息,导致第二次启动失败,所以先在zookeeper中删除hbase的注册信息:
       
      1. # 切换到zookeeper的bin目录下
      2. cd zookeeper/bin
      3. 2
      4. # 然后执行 ./zkCli.sh 命令
      5. [XXhadoop102 bin]$ ./zkCli.sh
      6. # 输入 ls / 命令行查看所有的内容
      7. [zk: localhost:2181(CONNECTED) 0] ls /
      8. [hbase, kafka, servers, spark, zookeeper]
      9. # 使用 rmr /hbase 或者 deleteall /hbase 删除zookeeper中的所有 hbase的目录
      10. [zk: localhost:2181(CONNECTED) 3] rmr /hbase
      11. # 此时,可以看到Zookeeper中已经没有HBase了
      12. [zk: localhost:2181(CONNECTED) 5] ls /
      13. [kafka, servers, spark, zookeeper]
      复制代码
    • 搭建Hbase HA

      • 关闭Hbase所有节点
        1. stop-hbase.sh
        复制代码
      • 创建并编辑配置文件
        1. vim conf/backup-masters
        复制代码
        1. node2
        复制代码
      • 启动Hbase集群

    • 测试HA

      • 启动两个Master,强制关闭Active Master,观察StandBy的Master是否切换为Active状态
        1. hbase-daemon.sh stop master
        复制代码
      • 【测试完成以后,删除配置,只保留单个Master模式】



  • 小结

    • 实现Hbase分布式集群部署

  1. node01
  2. node02
  3. node03
复制代码
创建back-masters配置文件,实现HMaster的高可用

node01机器进行修改配置文件
cd /export/servers/hbase-2.0.0/conf
vim backup-masters
  1. node02
复制代码
第四步:安装包分发到其他机器

将我们node01服务器的hbase的安装包拷贝到其他机器上面去
cd /export/servers/
scp -r hbase-2.0.0/ node02PWD
scp -r hbase-2.0.0/ node03PWD
第五步:三台机器创建软连接

因为hbase需要读取hadoop的core-site.xml以及hdfs-site.xml当中的配置文件信息,所以我们三台机器都要执行以下下令创建软连接
ln -s /export/servers/hadoop-2.7.5/etc/hadoop/core-site.xml /export/servers/hbase-2.0.0/conf/core-site.xml
ln -s /export/servers/hadoop-2.7.5/etc/hadoop/hdfs-site.xml /export/servers/hbase-2.0.0/conf/hdfs-site.xml

第六步:三台机器添加HBASE_HOME的环境变量

三台机器执行以下下令,添加HBASE_HOME环境变量
vim /etc/profile
export HBASE_HOME=/export/servers/hbase-2.0.0
export PATH=HBASE_HOME/binPATH
第七步:HBase集群启动

第一台机器执行以下下令进行启动
cd /export/servers/hbase-2.0.0
bin/start-hbase.sh

警告提示:HBase启动的时间会产生一个警告,这是因为jdk7与jdk8的问题导致的,假如linux服务器安装jdk8就会产生如许的一个警告
我们可以只是掉所有机器的hbase-env.sh当中的
“HBASE_MASTER_OPTS”和“HBASE_REGIONSERVER_OPTS”配置 来解决这个问题。不外警告不影响我们正常运行,可以不用解决

另外一种启动方式:
我们也可以执行以下下令单节点进行启动
启动HMaster下令
bin/hbase-daemon.sh start master
启动HRegionServer下令
bin/hbase-daemon.sh start regionserver
第八步:页面访问

欣赏器页面访问
http://node01:16010/master-status
5.Hbase的表模型

rowkey:行键,每一条数据都是使用行键来进行唯一标识的
columnFamily:列族,列族下面可以有很多列
column:列,每一个列都必须归属于某一个列族
timestamp:时间戳,每条数据都会有时间戳的概念
versionNum:版本号,每条数据都有版本号,当数据更新时,版本号也改变
创建一张HBase表最少需要两个条件:表名+列族名
注意:rowkey是我们在插入数据的时间本身指定的,列名也是插入数据的时间动态指定的
时间戳是自动天生的,versionNum也是本身维护的
6、HBase常用shell操作

1、进入HBase客户端下令操作界面

node01服务器执行以下下令进入hbase的shell客户端
cd /export/servers/hbase-2.0.0
bin/hbase shell
2、检察资助下令

hbase(main):001:0> help
3、检察当前数据库中有哪些表

hbase(main):002:0> list
4、创建一张表

创建user表,包含info、data两个列族
hbase(main):010:0> create 'user', 'info', 'data'
或者
hbase(main):010:0> create 'user', {NAME => 'info', VERSIONS => '3'},{NAME => 'data'}
5、添加数据操作

向user表中插入信息,row key为rk0001,列族info中添加name列标示符,值为zhangsan
hbase(main):011:0> put 'user', 'rk0001', 'info:name', 'zhangsan'
向user表中插入信息,row key为rk0001,列族info中添加gender列标示符,值为female
hbase(main):012:0> put 'user', 'rk0001', 'info:gender', 'female
向user表中插入信息,row key为rk0001,列族info中添加age列标示符,值为20
hbase(main):013:0> put 'user', 'rk0001', 'info:age', 20
向user表中插入信息,row key为rk0001,列族data中添加pic列标示符,值为picture
hbase(main):014:0> put 'user', 'rk0001', 'data:pic', 'picture'
6、查询数据操作

1、通过rowkey进行查询

获取user表中row key为rk0001的所有信息
hbase(main):015:0> get 'user', 'rk0001'
2、检察rowkey下面的某个列族的信息

获取user表中row key为rk0001,info列族的所有信息
hbase(main):016:0> get 'user', 'rk0001', 'info'
3、检察rowkey指定列族指定字段的值

获取user表中row key为rk0001,info列族的name、age列标示符的信息
hbase(main):017:0> get 'user', 'rk0001', 'info:name', 'info:age'
4、检察rowkey指定多个列族的信息

获取user表中row key为rk0001,info、data列族的信息
hbase(main):018:0> get 'user', 'rk0001', 'info', 'data'
或者你也可以如许写
hbase(main):019:0> get 'user', 'rk0001', {COLUMN => ['info', 'data']}
或者你也可以如许写,也行
hbase(main):020:0> get 'user', 'rk0001', {COLUMN => ['info:name', 'data:pic']}
4、指定rowkey与列值查询

获取user表中row key为rk0001,cell的值为zhangsan的信息
hbase(main):030:0> get 'user', 'rk0001', {FILTER => "ValueFilter(=, 'binary:zhangsan')"}
5、指定rowkey与值模糊查询

获取user表中row key为rk0001,列标示符中含有a的信息
hbase(main):031:0> get 'user', 'rk0001', {FILTER => "(QualifierFilter(=,'substring:a'))"}
继续插入一批数据
hbase(main):032:0> put 'user', 'rk0002', 'info:name', 'fanbingbing'
hbase(main):033:0> put 'user', 'rk0002', 'info:gender', 'female'
hbase(main):034:0> put 'user', 'rk0002', 'info:nationality', '中国'
hbase(main):035:0> get 'user', 'rk0002', {FILTER => "ValueFilter(=, 'binary:中国')"}
6、查询所有数据

查询user表中的所有信息
scan 'user'
7、列族查询

查询user表中列族为info的信息
scan 'user', {COLUMNS => 'info'}
scan 'user', {COLUMNS => 'info', RAW => true, VERSIONS => 5}
scan 'user', {COLUMNS => 'info', RAW => true, VERSIONS => 3}
8、多列查询

查询user表中列族为info和data的信息
scan 'user', {COLUMNS => ['info', 'data']}
scan 'user', {COLUMNS => ['info:name', 'data:pic']}
9、指定列族与某个列名查询

查询user表中列族为info、列标示符为name的信息
scan 'user', {COLUMNS => 'info:name'}
10、指定列族与列名以及限定版本查询

查询user表中列族为info、列标示符为name的信息,并且版本最新的5个
scan 'user', {COLUMNS => 'info:name', VERSIONS => 5}
11、指定多个列族与按照数据值模糊查询

 查询user表中列族为info和data且列标示符中含有a字符的信息
scan 'user', {COLUMNS => ['info', 'data'], FILTER => "(QualifierFilter(=,'substring:a'))"}
12、rowkey的范围值查询

查询user表中列族为info,rk范围是[rk0001, rk0003)的数据
scan 'user', {COLUMNS => 'info', STARTROW => 'rk0001', ENDROW => 'rk0003'}
13、指定rowkey模糊查询

查询user表中row key以rk字符开头的
scan 'user',{FILTER=>"refixFilter('rk')"}
14、指定数据范围值查询

查询user表中指定范围的数据
scan 'user', {TIMERANGE => [1392368783980, 1392380169184]}
7.Hbase 总结一:


  • Hbase的功能和应用场景是什么?
    功能:可以或许实实际时分布式随机数据存储
    场景:大量的结构化数据,及时,随机,长期化存储
  • Hbase的基本存储结构是什么?
    设计:分布式大量数据及时存储
              分布式内存【进程】+分布式磁盘【HDFS】
    实现:NameSpace:雷同于数据库概念,访问表的时间必须加上NS
               Table:就是表概念,表是分布式的,一张表可以有多个分区Region,每个分区可以                           存储在差别的节点上
                Rowkey:雷同于主键的概念,唯一标识一行,作为Hbase的唯一索引,每张表都自                                    带一列,值由用户自定义
               ColumnFamily:对列的分组,将差别的列分到差别的组中,用于加快查询效率,任                                         何一列都必须数据某个列族
               Qualifier:列标签,按照平常的列名称进行标志,不许使用列族+对应的列的名称才                                能唯一标志一列
                VERSIONS:列族级别,可以指定某个列族下的列允许存储多个版本的值,默认只                                      查询最新版本,根据timestamp来区分
    存储:KV结构:一列就是一条KV数据
               K:rowkey+cf+col+ts。所有的Kiev写入底层存储都按照K进行排序
     
               V:   值。存储类型:字节
  • Hbase的架构和角色是什么?
    1.Hbase:分布式主从架构
    主:HMaster:管理
    从:HRegionServer:存储:构建分布式内存
    2.Zooleeper:辅助Master推举,存储管理元数据
    3.HDFS:构建分布式磁盘
  • Hbase的常用下令是什么?
    DDL:create_namespace,list_namespace.create[表名+列族],drop,exist,desc,disable,enable
    DML:
    put 'ns:tbname','rowkey','cf:col',value
    delete 'ns:tbname','rowkey','cf:col'
    get 'ns:tbname','rowkey','cf:col'
    scan 'ns:tbname',Filter
  • Hbase的JavaAPI如何实现DDL
    1.构建连接:Connection
    2.构建操作:DDL:HbaseAdmin   DML:Table
    3.释放资源
8.JavaAPI

DML:Table

使用Hbase API实现Table的实例开发
DML的所有操作都必须构建Hbase表的对象进行操作
代码:
  1. public Table getHbaseTable() throws IOException {
  2.         TableName tbname = TableName.valueOf("itcast:t1");
  3.         Table table = conn.getTable(tbname);
  4.         return table;
  5.     }
复制代码
DML:Put

实现Put插入或者更新数据
代码:
  1. @Test
  2.     public void testPut() throws IOException {
  3.         Table table = getHbaseTable();
  4.         //构建Put对象,一个Put对象表示写入一个Rowkey的数据
  5.         Put put = new Put(Bytes.toBytes("20210101_001"));
  6.         //添加列的信息
  7.         put.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("name"),Bytes.toBytes("laoda"));
  8.         put.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("age"),Bytes.toBytes("18"));
  9.         put.addColumn(Bytes.toBytes("other"),Bytes.toBytes("phone"),Bytes.toBytes("110"));
  10.         //执行Put
  11.         table.put(put);
  12.         table.close();
  13.     }
复制代码
小结:


  • 调用:Table.put(Put)
  • Put:Put操作类对象
  • new Put(rowkey)

    • .addColumn(cf,col,value)

DML:Get

实现Get读取数据
先用下令行插入数据,便于测试:
  1. put 'itcast:t1','20210201_000','basic:name','laoda'
  2. put 'itcast:t1','20210201_000','basic:age',18
  3. put 'itcast:t1','20210101_001','basic:name','laoer'
  4. put 'itcast:t1','20210101_001','basic:age',20
  5. put 'itcast:t1','20210101_001','basic:sex','male'
  6. put 'itcast:t1','20210228_002','basic:name','laosan'
  7. put 'itcast:t1','20210228_002','basic:age',22
  8. put 'itcast:t1','20210228_002','other:phone','110'
  9. put 'itcast:t1','20210301_003','basic:name','laosi'
  10. put 'itcast:t1','20210301_003','basic:age',20
  11. put 'itcast:t1','20210301_003','other:phone','120'
  12. put 'itcast:t1','20210301_003','other:addr','shanghai'
复制代码
代码: 
  1. @Test
  2.     public void testGet() throws IOException {
  3.         Table table = getHbaseTable();
  4.         //构建Get:get tbname,rowkey,[cf:col]
  5.         Get get = new Get(Bytes.toBytes("20210301_003"));
  6.         //配置Get
  7.         get.addFamily(Bytes.toBytes("basic"));//指定列族读取
  8. //        get.addColumn()//指定列读取
  9.         //执行:一个Result代表一个Rowkey的数据
  10.         Result result = table.get(get);
  11.         //打印这个rowkey每一列的结果:一个Cell对象就是一列的对象:20210301_003 column=other:phone, timestamp=1624590747738, value=120
  12.         for(Cell cell : result.rawCells()){
  13.             System.out.println(
  14.                     Bytes.toString(CellUtil.cloneRow(cell)) + "\t" +
  15.                     Bytes.toString(CellUtil.cloneFamily(cell)) + "\t" +
  16.                     Bytes.toString(CellUtil.cloneQualifier(cell)) + "\t" +
  17.                     Bytes.toString(CellUtil.cloneValue(cell)) + "\t" +
  18.                     cell.getTimestamp()
  19.             );
  20.         }
  21.         table.close();
  22.     }
复制代码
小结:


  • Get:实现Get操作对象
  • new Get(rowkey)

    • .addFamily:指定读取这个rowkey的列族
    • .addColumn:指定读取某一列

  • table.get(Get):实现get操作
  • Result:一个Result代表一个Rowkey的数据

    • rawCells/listCells

  • Cell:一个Cell代表一列的数据
  • CellUtil:用于对Cell取值的工具类

    • cloneValue
    • cloneRow
    • cloneQualifier
    • cloneFamily

DML:Delete

实现Delete删除数据
代码:
  1. @Test
  2.     public void testDel() throws IOException {
  3.         Table table = getHbaseTable();
  4.         //构建Delete
  5.         Delete del = new Delete(Bytes.toBytes("20210301_003"));
  6.         //删除列族
  7. //        del.addFamily()
  8.         //删除列
  9.         del.addColumn(Bytes.toBytes("other"),Bytes.toBytes("phone"));
  10. //        del.addColumns(Bytes.toBytes("other"),Bytes.toBytes("phone"));//删除所有版本
  11.         //执行删除
  12.         table.delete(del);
  13.         table.close();
  14.     }
复制代码
小结:


  • Delete:删除操作对象

    • .addColumn:指定删除的列

  • table.delete(Delete)
DML:Scan

实现Scan读取数据
代码:
  1.   @Test
  2.     public void testScan () throws IOException {
  3.         Table table = getHbaseTable();
  4.         //构建Scan对象
  5.         Scan scan = new Scan();
  6.         //执行scan:ResultScanner用于存储多个Rowkey的数据,是Result的集合
  7.         ResultScanner scanner = table.getScanner(scan);
  8.         //取出每个Rowkey的数据
  9.         for (Result result : scanner) {
  10.             //先打印当前这个rowkey的内容
  11.             System.out.println(Bytes.toString(result.getRow()));
  12.             for(Cell cell : result.rawCells()){
  13.                 System.out.println(
  14.                         Bytes.toString(CellUtil.cloneRow(cell)) + "\t" +
  15.                                 Bytes.toString(CellUtil.cloneFamily(cell)) + "\t" +
  16.                                 Bytes.toString(CellUtil.cloneQualifier(cell)) + "\t" +
  17.                                 Bytes.toString(CellUtil.cloneValue(cell)) + "\t" +
  18.                                 cell.getTimestamp()
  19.                 );
  20.             }
  21.             System.out.println("------------------------------------------------------------");
  22.         }
  23.         table.close();
  24.     }
复制代码
存储设计:Table,Region,RS的关系



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

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

      • 数据写入表以后的物理存储:分区
      • 一张表会有多个分区Region,每个分区存储在差别的机器上
      • 默认每张表只有1个Region分区

    • Region:Hbase中数据负载平衡的最小单元

      • 雷同于HDFS中Block,Kafka中Partition、用于实现Hbase中分布式
      • 就是分区的概念,每张表都可以划分为多个Region,实现分布式存储

        • 默认一张表只有一个分区

      • 每个Region由一台RegionServer所管理,Region存储在RegionServer
      • 一台RegionServer可以管理多个Region

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

      • 雷同于HDFS中DataNode或者Kafka中的Broker
      • 一个Regionserver可以管理多个Region
      • 一个Region只能被一个RegionServer所管理


  • 小结
    Hbase中Table与Region,RS三者的关系是什么?
    1.Table:提供用户读写的逻辑概念,并不是实际存在的
    2.Region:分区的概念,一张表可以划分为多个分区,每个分区都被某一台RegionServer所管理
    3.RegionServer:真正的存储数据的物理概念
存储设计:Region及数据的划分规则



  • 回顾:划分规则

  • HDFS:划分分区规则:按照巨细划分,文件按照每128M划分为一个Block
  • Redis:将0~16383划分为多个段,每个小的集群分配一个段的内容
  • Kafka:本身制定一个Topic有多少个分区,数据分配规则:指定分区,按照Key的hash地区,或者粘性分区,自定义分区


  • Hbase分区划分规则:范围划分【根据Rowkey范围】
           任何一个Region都会对应一个范围,

    • 假如只有一个Region,范围:-oo ~ +oo

      • 范围划分:从整个-oo ~ +oo区间上进行范围划分

        • 每个分区都会有一个范围:根据Rowkey属于哪个范围就写入哪个分区
          1. [startKey,stopKey)
          复制代码

          • 前闭后开区间


      • 默认:一张表创建时,只有一个Region

        • 范围:-oo ~ +oo


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

        • 举个栗子:创建一张表,有2个分区Region
          1. create 'itcast:t3',{SPLITS => [50]}
          复制代码

      • region0:-oo ~ 50
      • region1:50 ~ +oo

    • 数据分配的规则:==根据Rowkey属于哪个范围就写入哪个分区==

      • 举个栗子:创建一张表,有4个分区Region,20,40,60
        1. create 'itcast:t3',{SPLITS => [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

      • 比较规则:ASCII码前缀逐位比较


  • 小结

  • 分区划分规则:将整个-00到+00区间进行划分,划分多个分区段
    根据Rowkey进行划分
  • 数据分区规则:根据rowkey的asc码逐位匹配,rowkey属于谁人范围,就写入谁人分区
存储设计:Region的内部结构



  • 数据在Region的内部是如何存储的?
    put tbname,rowkey,cf:col,value

  • tbname:决定了这张表的数据终极要读写那些分区
  • rowkey:决定了详细读写哪个分区
  • cf:决定了详细写入哪个Store


  • Table/RegionServer:数据指定写入哪张表,提交给对应的某台regionserver
  • Region:对整张表的数据划分,按照范围划分,实现分步式存储
  • Store:对分区的数据进行划分,按照列族划分,一个列族对应一个Store
    差别列族的数据写入差别的Store中,实现了按照列族将列进行分组
    根据用户查询时指定的列族,可以快速的读取对应的store
    MemStore:每个Store都有一个,内存存储地区
    数据写入memstore后直接返回
  • StoreFile:每个Store中可能有0个或者多个StoreFile文件
    逻辑上:Store
    物理上:HDFS:HFILE(二进制文件)


  • HDFS中的存储

  • 问题:Hbase的数据是如何在HDFS中存储的?
  • 分析:整个Hbase在HDFS中的存储目录

    1. hbase.rootdir=hdfs://node1:8020/hbase
    复制代码
  • NameSpace:目录结构

  • Table:目录结构

  • Region:目录结构

  • Store/ColumnFamily:目录结构

  • StoreFile


    • 假如HDFS上没有storefile文件,可以通过flush,手动将表中的数据从内存刷写到HDFS中
      1. flush 'itcast:t3'
      复制代码



  • 小结

    • Region的内部存储结构是什么样的?

      • NS:Table|RegionServer:整个Hbase数据划分

        • Region:划分表的数据,按照Rowkey范围划分

          • Store:划分分区数据,按照列族划分

            • MemStore:物理内存存储
            • StoreFile:物理磁盘存储

              • 逻辑:Store
              • 物理:HDFS[HFile]






Hbase读写流程:基本流程





  • 实施

    • step1:根据表名获取这张表对应的所有Region的信息

      • 整个Hbase的所有Regionserver中有很多个Region:100
      • 先根据表名找到这张表有哪些region:5

    • step2:根据Rowkey判定详细写入哪个Region

      • 知道了这张表的所有region
      • 根据rowkey属于哪个region范围,来确定详细写入哪个region

    • step3:将put操作提交给这个Region地点的RegionServer

      • 获取这个Region地点的RegionServer地址

    • step4:RegionServer将数据写入Region,根据列族判定写入哪个Store
    • step5:将数据写入MemStore中



  • 小结

    • 表名:决定了这条数据要写入哪些region中
    • Rowkey:决定了这条数据详细写入哪个Region中
    • 列族:决定了写入这个region哪个Store中

Hbase读写流程:meta表



  • 实施

    • Hbase自带的两张系统表

      • hbase:namespace:存储了Hbase中所有namespace的信息
      • hbase:meta:存储了表的元数据

    • hbase:meta表结构

      • Rowkey:每张表每个Region的名称
        1. itcast:t3,20,1632627488682.fba4b18252cfa72e48ffe99cc63f7604
        2. 表名,startKey,时间,唯一id
        复制代码

    • Hbase中每张表的每个region对应元数据表中的一个Rowkey



        • info:regioninfo
          1. STARTKEY => 'eeeeeeee', ENDKEY => ''
          复制代码
        • info:server/info:sn
          1. column=info:sn, timestamp=1624847993004, value=node1,16020,1624847978508  
          复制代码


    • 实现

      • 根据表名读取meta表,基于rowkey的前缀匹配,获取这张表的所有region信息


  • 小结

    • meta表的功能是什么?

      • 存储了表的元数据

    • 每个Rowkey代表了一个Region的信息

      • Region的范围
      • Region的地址


Hbase读写流程:写入流程



  • 实施

    • step1:获取表的元数据

      • ==先连接zk,从zk获取meta表地点的regionserver==
      • 根据查询的表名读取meta表,获取这张表的所有region的信息

        • meta表是一张表,数据存储在一个region,这个region存在某个regionserver上
        • 怎么知道meta表的regionserver在哪?
        • 这个地址纪录在ZK中

      • 得到这张表的所有region的范围和地址

    • step2:找到对应的Region

      • 根据Rowkey和所有region的范围,来匹配详细写入哪个region
      • 获取这个region地点的regionserver的地址

    • step3:写入数据

      • 哀求对应的regionserver
      • regionserver根据提交的region的名称和数据来操作对应的region
      • 根据列族来判定详细写入哪个store

        • ==先写WAL==:write ahead log

          • 为了避免内存数据丢失,所有数据写入内存之前会
          • 先纪录这个内存操作

        • 然后写入==这个Store的Memstore中==


    • 思索:hbase的region没有选择副本机制来保证安全,假如RegionServer故障,Master发现故障,怎么保证数据可用性?

      • step1:Master会根据元数据将这台RegionServe中的Region恢复到别的机器上
      • step2:怎么实现数据恢复?

        • Memstore:WAL进行恢复

          • 怎么保证WAL安全性:WAL纪录在HDFS上

        • StoreFile:HDFS有副本机制



Hbase读写流程:读取流程



  • 实施

  • 获取元数据
    客户端哀求Zookeeper,获取meta表地点的regionserver的地址
    读取meta表的数据
    注意:客户端会缓存meta表的数据,只有第一次会连接ZK,读取meta表的数据,缓存会定期失效,要重新缓存,避免每次哀求都要先连接zk,再读取meta表
  • 找到对应的Region
    根据meta表中的元数据,找到表对应的region
    根据region的范围和读取的RowKey,判定需要读取详细哪一个Region
    根据region的RegionServer地址,哀求对应的RegionServer
  • 读取数据
    先查询memstore,假如开启了缓存,就读BlockCache,假如缓存中没有,就读storefile,从storefile读取完成之后,放入缓存中,假如没有缓存,就读StoreFile
    第一次查询一定是先度memstore,然后storefile假如开启了缓存,就将这次读取到的数据放到缓存中
     
LSM模型:Flush



  • 功能:将内存memstore中的数据溢写到HDFS中酿成磁盘文件storefile【HFILE】
  • 关闭集群:自动Flush
  • 参数配置:自动触发机制
     
    1. #2.x版本以后的机制
    2. #设置了一个flush的最小阈值
    3. #memstore的判断发生了改变:max("hbase.hregion.memstore.flush.size / column_family_number",hbase.hregion.percolumnfamilyflush.size.lower.bound.min)
    4. #如果memstore高于上面这个结果,就会被flush,如果低于这个值,就不flush,如果整个region所有的memstore都低于,全部flush
    5. #水位线 = max(128 / 列族个数,16),列族一般给3个 ~ 42M
    6. #如果memstore的空间大于42,就flush,如果小于就不flush,如果都小于,全部flush
    7. 举例:3个列族,3个memstore,90/30/30   90会被Flush
    8. 举例:3个列族,3个memstore,30/30/30  全部flush
    9. hbase.hregion.percolumnfamilyflush.size.lower.bound.min=16M
    10. #2.x中多了一种机制:In-Memory-compact,如果开启了【不为none】,会在内存中对需要flush的数据进行合并
    11. #合并后再进行flush,将多个小文件在内存中合并后再flush
    12. hbase.hregion.compacting.memstore.type=None|basic|eager|adaptive
    复制代码
    小结
  • Hbase利用Flush实现将内存数据溢写到HDFS,保持内存中不断存储最新的数据
  • 注意:工作中一样平常进行手动Flush

    • 缘故原由:避免大量的Memstore将大量的数据同时Flush到HDFS上,占用大量的内存和磁盘的IO带宽,会影响业务
    • 解决:手动触发,定期执行
      1. hbase> flush 'TABLENAME'
      2. hbase> flush 'REGIONNAME'
      3. hbase> flush 'ENCODED_REGIONNAME'
      4. hbase> flush 'REGION_SERVER_NAME'
      复制代码
    • 封装一个文件,通过hbase shell filepath来定期的运行这个脚本

LSM模型:Compaction





    • 实现

      • 功能:什么是Compaction?

        • 将多个单独有序StoreFile文件进行合并,合并为整体有序的大文件,加快读取速度
        • file1:1 2 3 4 5
        • file2:6 7 9
        • file3 :1 8 10
        • || 每个文件都读取,可能读取无效的数据
        • file:1 1 2 3 4 5 6 7 8 9 10

      • 版本功能

        • 2.0版本之前,只有StoreFile文件的合并

          • 磁盘中合并:minor compaction、major compaction

        • 2.0版本开始,内存中的数据也可以先合并后Flush

          • 内存中合并:In-memory compaction
          • 磁盘中合并:minor compaction、major compaction


      • In-memory compaction:2.0版本开始新增加的功能

        • 原理:将当前写入的数据划分segment【数据段】

          • 当数据不断写入MemStore,划分差别的segment,终极酿成storefile文件

        • 假如开启了内存合并,先将第一个segment放入一个队列中,与其他的segment进行合并

          • 合并以后的结果再进行flush

        • 内存中合并的方式
          1. hbase.hregion.compacting.memstore.type=None|basic|eager|adaptive
          2. none:不开启,不合并
          复制代码
        • basic(基础型)
          1. Basic compaction策略不清理多余的数据版本,无需对cell的内存进行考核
          2. basic适用于所有大量写模式
          复制代码
        • eager(饥渴型)
          1. eager compaction会过滤重复的数据,清理多余的版本,这会带来额外的开销
          2. eager模式主要针对数据大量过期淘汰的场景,例如:购物车、消息队列等
          复制代码
        • adaptive(顺应型)
          1. adaptive compaction根据数据的重复情况来决定是否使用eager策略
          2. 该策略会找出cell个数最多的一个,然后计算一个比例,如果比例超出阈值,则使用eager策略,否则使用basic策略
          复制代码

      • minor compaction:轻量级

        • 功能:将最早天生的几个小的StoreFile文件进行合并,成为一个大文件,不定期触发
        • 特点

          • 只实现将多个小的StoreFile合并成一个相对较大的StoreFile,占用的资源不多
          • 不会将标志为更新或者删除的数据进行处理

        • 属性
          1. hbase.hstore.compaction.min=3
          复制代码

      • major compaction:重量级合并

        • 功能:将整个Store中所有StoreFile进行合并为一个StoreFile文件,整体有序的一个大文件
        • 特点

          • 将所有文件进行合并,构建整体有序
          • 合并过程中会进行清理过期和标志为删除的数据
          • 资源消耗比较大

        • 参数配置
          1. hbase.hregion.majorcompaction=7天
          复制代码


    • 小结

      • Hbase通过Compaction实现将零散的有序数据合并为整体有序大文件,进步对HDFS数据的查询性能
      • 在工作中要避免自动触发majorcompaction,影响业务
        1. hbase.hregion.majorcompaction=0
        复制代码
      • 在不影响业务的时间,手动处理,每天在业务不繁忙的时间,调度工具实现手动进行major compact
        1. Run major compaction on passed table or pass a region row
        2.          to major compact an individual region. To compact a single
        3.          column family within a region specify the region name
        4.          followed by the column family name.
        5.          Examples:
        6.          Compact all regions in a table:
        7.          hbase> major_compact 't1'
        8.          hbase> major_compact 'ns1:t1'
        9.          Compact an entire region:
        10.          hbase> major_compact 'r1'
        11.          Compact a single column family within a region:
        12.          hbase> major_compact 'r1', 'c1'
        13.          Compact a single column family within a table:
        14.          hbase> major_compact 't1', 'c1'
        15.          Compact table with type "MOB"
        16.          hbase> major_compact 't1', nil, 'MOB'
        17.          Compact a column family using "MOB" type within a table
        18.          hbase> major_compact 't1', 'c1', 'MOB'
        复制代码


Region分裂Split设计及规则



  • 分析

    • 什么是Split分裂机制?

      • 为了避免一个Region存储的数据过多,提供了Region分裂机制
      • 实现将一个Region分裂为两个Region
      • 由RegionServer实现Region的分裂,得到两个新的Region
      • 由Master负责将两个新的Region分配到Regionserver上


  • 实现

    • 参数配置
       
      1. #规则:return tableRegionsCount  1  ? this.initialSize : getDesiredMaxFileSize();
      2. #判断region个数是否为1,如果为1,就按照256分,如果不为1,就按照10GB来分
      3. hbase.regionserver.region.split.policy=org.apache.hadoop.hbase.regionserver.SteppingSplitPolicy
      复制代码

  • 小结

    • Hbase通过Split战略来保证一个Region存储的数据量不会过大,通过分裂实现分摊负载,避免热门,降低故障率
    • 注意:工作作中避免自动触发,影响集群读写,发起关闭
      1. hbase.regionserver.region.split.policy=org.apache.hadoop.hbase.regionserver.DisabledRegionSplitPolicy
      复制代码
    • 手动操作
      1. split 'tableName'
      2. split 'namespace:tableName'
      3. split 'regionName' # format: 'tableName,startKey,id'
      4. split 'tableName', 'splitKey'
      5. split 'regionName', 'splitKey'
      复制代码

热门问题:现象及缘故原由



  • 现象
    1. 在某个时间段内,大量的读写请求全部集中在某个Region中,导致这台RegionServer的负载比较高,其他的Region和RegionServer比较空闲
    复制代码
  • 缘故原由:本质上的缘故原由,数据分配不平衡

    • 情况一:Region范围不合理

      • Rowkey:字母开头
      • Region:3个分区

        • region0:-oo ~ 30
        • region1:30 ~ 70
        • region2:70 ~ +oo

      • 所有数据都写入了region2

    • 情况二:假如这张表有多个分区,而且你的Rowkey写入时是连续的

      • 一张表有5个分区
        1. region0:-oo  20
        2. region1:20   40
        3. region2:40   60
        4. region3:60    80
        5. region4:80    +oo
        复制代码
      • 000001:region0
      • 000002:region0
      • ……
      • 199999:region0
      • 都写入了同一个region0分区
      • 200000:region1
      • 200001:region1
      • ……
      • 399999:region1

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

      • 所有数据都存储在一个分区中,这个分区要相应所有读写哀求,出现了热门

    • 解决:避免热门的产生

      • 构建多个分区,分区范围必须要Rowkey设计
      • 构建不连续的rowkey


分布式设计:预分区



  • 实施

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

      • 划分的目的:划分多个分区,实现分布式并行读写,将无穷区间划分为几段,将数据存储在差别分区中,实现分区的负载平衡
      • 划分的规则==Rowkey或者Rowkey的前缀来划分==

        • 假如不按照这个规则划分,预分区就可能没有作用
        • Rowkey:00 ~ 99

          • region0: -oo ~ 30
          • ……
          • regionN : 90 ~ +oo

        • 假如分区的设计不按照rowkey来

          • region0:-oo ~ b
          • region1: b ~ g
          • ……
          • regionN:z ~ +oo



    • 实现

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

        • 条件:先设计rowkey
        1. create 'ns1:t1', 'f1', SPLITS => ['10', '20', '30', '40']
        2. #将每个分割的段写在文件中,一行一个
        3. create 't1', 'f1', SPLITS_FILE => 'splits.txt'
        复制代码

      • 方式二:指定Region个数,自动进行Hash划分:==字母和数字的组合==
        1. #你的rowkey的前缀是数字和字母的组合
        2. create 'itcast:t4', 'f1', {NUMREGIONS => 15, SPLITALGO => 'HexStringSplit'}
        复制代码
      • 方式三:Java API
        1. HBASEAdmin admin = conn.getAdmin
        2. admin.create(表的描述器对象,byte[][] splitsKey)
        复制代码


  • 小结

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

Hbase表设计:Rowkey设计

设计规则


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

    • 数据
      1. oid     uid     pid     stime ……
      复制代码
    • Rowkey:stime
      1. rowkey                  oid     uid     pid     stime ……
      2. 20211201000000          o001    u001    p001    2021-12-01 00:00:00
      复制代码
    • 按照时间就走索引

  • 唯一原则:Rowkey必须具有唯一性,不能重复,一个Rowkey唯一标识一条数据
  • 组合原则:将更多的经常作为的查询条件的列放入Rowkey中,可以满意更多的条件查询可以走索引查询

    • Rowkey:stime

      • 缺点:不唯一、只有按照时间才走索引

    • Rowkey:stime_uid_oid
      1. rowkey                              oid     uid     pid     stime ……
      2. 20211201000000_u001_o001            o001    u001    p001    2021-12-01 00:00:00
      3. 20211201000000_u002_o002            o001    u001    p001    2021-12-01 00:00:00
      4. ……
      5. 20211201000000_u00N_o00N            o001    u001    p001    2021-12-01 00:00:00
      6. ……
      7. 20211201000001_u001_o001            o001    u001    p001    2021-12-01 00:00:00
      复制代码

      • 订单id唯一、一个用户在同一时间只能下一个订单
      • 索引查询:stime、s_time+uid、stime+uid+oid
      • 想查询订单id为001:不走

        • 全表扫表:对列的值进行过滤:SingleColumnValueFilter



  • 散列原则:为了避免出现热门问题,需要将数据的rowkey天生规则构建散列的rowkey

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

      • timestamp_userid_orderid:订单表
        1. 1624609420000_u001_o001
        2. 1624609420001_u002_o002
        3. 1624609420002_u003_o003
        4. 1624609421000_u001_o004
        5. ……
        复制代码
      • 预分区:数值

        • region0:-oo ~ 1624
        • region1:1624 ~ 1924
        • region2:1924 ~ 2100
        • region3:2100 -2400
        • region4:2400 ~ +oo

      • 问题:出现热门

    • 解决:构建散列

      • 方案一:更换不是连续的字段作为前缀,例如用户id
        1. dfd342
        2. 3432sd
        复制代码

        • 缺点:可能与业务原则产生冲突

      • 方案二:反转

        • 一样平常用于时间作为前缀,查询时间必须将数据反转再查询
        1. 0000249064261_u001_o001
        2. 1000249064261_u002_o002
        3. 2000249064261_u003_o003
        4. 0010249064261_u001_o004
        5. ……
        复制代码
              

        • region0:-oo ~ 2
        • region1:2 ~ 4
        • region2:4 ~ 6
        • region3:6 ~ 8
        • region4:8 ~ +oo

      • ==方案三:加盐(Salt)==,本质对数据进行编码,天生数字加字母组合的结果
        1. 1624609420000_u001_o001
        2. 1624609420001_u002_o002
        3. 1624609420002_u003_o003
        4. 1624609421000_u001_o004
        5. |
        6. df34343jed_u001_o001
        7. 09u9jdjkfd_u002_o002
        复制代码

        • 缺点:查询时间,也必须对查询条件加盐以后再进行查询



  • 长度原则:在满意业务需求情况下,rowkey越短越好,一样平常发起Rowkey的长度小于100字节

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

小结:
rowkey的设计要符合以下原则:


  • 业务原则:Rowkey设计贴合实际业务需求,尽量使用最常用的查询条件作为前缀
  • 唯一原则:每个Rowkey唯一标识是一条数据
  • 组合原则:尽量将更多的常用条件放入rowkey中
  • 散列原则:构建不连续的Rowkey
  • 长度原则:在满意上面原则的情况,rowkey越短越好
Hbase表设计:其他设计



  • NS的设计:雷同于数据库名称的设计,明白标识每个业务域,一样平常包含业务域名称
  • 表明设计:雷同于数据库中表明的设计包含业务名称即可
  • 列族设计:名称没有太多意义,个数发起一样平常不超过3个
  • 标签设计:按照实际业务字段名称标识即可,发起缩写,避免过长
BulkLoad的先容



  • 问题:有一批大数据量的数据,要写入Hbase中,假如按照传统的方案来写入Hbase,必须先写入内存,然后内存溢写到HDFS,导致Hbase的内存负载和HDFS的磁盘负载过高,影响业务
  • 解决:
  • 方式一:构建Put对象,先写内存
  • 方式二:BulkLoad,直接将数据酿成StoreFile文件,放入Hbase对应的HDFS目录中

    • 数据不颠末内存,读取数据时可以直接读取到

  • 步骤:先将要写入的数据转换为HFILE文件,然后将HFILE加载到Hbase表中
  • 特点
    长处:不颠末内存,降低了内存和磁盘的IO吞吐
    缺点:性能上相对来说要慢,所有的数据都不会在内存中被读取
  • 小结
    应用场景:Hbase提供BulkLoad来实现大数据量不颠末内存直接写入Hbase
BulkLoad的实现



  • 作用:一种加载数据到Hbase中的方式
  • 过程:先将要写入的数据转换为HFILE文件,然后将HFILE文件加载到Hbase的表中
  • 实现:
    数据文件bank_record.csv,每一行以逗号分割


  • 创建表:create“TRANSFENR_RECORD”,{NAME=>"C1"}
  • 上传测试文件
    hdfs dfs -mkdir -p /bulkload/input
    hdfs dfs -put bank_record.csv /bulkload/input/




  • 开发转换程序:将CSV文件转换为HFILE文件
  • 上传jar包到Linux上

  • 启动YARN
    start-yarn.sh 启动过就不用了
  • 转换HFILE
    1. yarn jar bulkload.jar bigdata.itcast.cn.hbase.bulkload.BulkLoadDriver  /bulkload/input/ /bulkload/output
    复制代码
 运行找不到Hbase的jar包,手动申明HADOOP的环境变量即可,只在当前窗口有用
   export HADOOP_CLASSPATH=$HADOOP_CLASSPATH:/export/server/hbase-2.1.0/lib/shaded-clients/hbase-shaded-mapreduce-2.1.0.jar:/export/server/hbase-2.1.0/lib/client-facing-thirdparty/audience-annotations-0.5.0.jar:/export/server/hbase-2.1.0/lib/client-facing-thirdparty/commons-logging-1.2.jar:/export/server/hbase-2.1.0/lib/client-facing-thirdparty/findbugs-annotations-1.3.9-1.jar:/export/server/hbase-2.1.0/lib/client-facing-thirdparty/htrace-core4-4.2.0-incubating.jar:/export/server/hbase-2.1.0/lib/client-facing-thirdparty/log4j-1.2.17.jar:/export/server/hbase-2.1.0/lib/client-facing-thirdparty/slf4j-api-1.7.25.jar
   重新运行
  检察结果

 step2:加载到Hbase表中
 
  1.   hbase org.apache.hadoop.hbase.tool.LoadIncrementalHFiles /bulkload/output TRANSFER_RECORD
复制代码
检察数据
  1. get 'TRANSFER_RECORD','ffff98c5-0ca0-490a-85f4-acd4ef873362',{FORMATTER=> 'toString'}
复制代码
协处理器的先容



  • 功能:协处理器指的是可以自定义开发一些功能集成到Hbase中,雷同于Hive的UDF,当没有这个功能是,可以使用协处理器来自定义开发,
  • observer类:观察者类,雷同于监听机制,MvSQL中的触发器,Zookeeper中的监听
    实现:监听A,假如A触发了,就执行B
    监听对象Region,Table ,RegionServer,Master
  • endpioint类:终端者类,雷同于Mysql中的存储过程,Java中的方法
    实现:固定一个代码逻辑,可以随时根据需求调用代码逻辑
  • 小结
    Hbase通过协处理器来弥补一些用户自定义功能的实现,例如二级索引,一样平常通过第三方工具实现
协处理器的实现

路径


  • step1:开发协处理器,监听原表的put哀求
  • step2:拦截原表put哀求,获取put操作,获取rowkey以及值
  • step3:构建索引表的rowkey,往索引表写入数据
  • step4:释放原表哀求,往原表写入数据
​​​​
需求:当往第一张表写入数据时,自动往第二张表写入一条数据,并且将rowkey中的字段换位


  • put 'proc1','20191211_001','info:name','zhangsan'
  • proc1:rowkey:20191211_001
  • proc2:rowkey:001_20191211


  • 创建两张表
    1. #rowkey:time_id
    2. create 'proc1','info'
    3. #rowkey:id_time
    4. create 'proc2','info'
    复制代码
  • 将开发好的协处理器jar包上传到hdfs上
    1. hdfs dfs -mkdir -p /coprocessor/jar
    2. mv bulkload.jar cop.jar
    3. hdfs dfs -put cop.jar /coprocessor/jar/
    复制代码
  • 添加协处理器到proc1中,用于监听proc1的操作
    1. disable 'proc1'
    2. alter 'proc1',METHOD => 'table_att','Coprocessor'=>'hdfs://node1:8020/coprocessor/jar/cop.jar|bigdata.itcast.cn.hbase.coprocessor.SyncCoprocessor|1001|'
    3. enable 'proc1'
    复制代码
  • 测试
    1. put 'proc1','20191211_001','info:name','zhangsan'
    2. scan 'proc1'
    3. scan 'proc2'
    复制代码
  • 卸载协处理器
    1. disable 'proc1'
    2. alter 'proc1',METHOD=>'table_att_unset',NAME=>'coprocessor$1'
    3. enable 'proc1'
    复制代码
Hbase优化:压缩机制

实施


  • 本质:Hbase的压缩源自于Hadoop对于压缩的支持
  • 检查Hadoop支持的压缩类型

    • hadoop checknative

  • 需要将Hadoop的本地库配置到Hbase中
  • 关闭Hbase的服务,配置Hbase的压缩本地库:lib/native/linux-amd64-64
    cd /export/servers/hbase-2.1.0/
    mkdir lib/native
  • 将Hadoop的压缩本地库创建一个软连接到Hbase的lib/native目录下
    ln -s /export/server/hadoop/lib/native /export/server/hbase-2.1.0/lib/native/Linux-amd64-64
  • 启动Hbase服务
    start-hbase.sh

    hbase shell
Hbase优化:布隆过滤:



  • 实施:什么是布隆过滤?:是列族的一个属性,用于数据查询时对数据的过滤,雷同于ORC文件中的布隆索引,BLOOMFILTER => NONE | 'ROW' | ROWCOL
  • NONE:不开启布隆过滤器
  • ROW:开启行级布隆过滤器

  • 天生StoreFile文件时,会将这个文件中有哪些RowKey的数据纪录在文件的头部
  • 当读取StoreFile文件时,会从文件头部获取这个StoreFile中所有的rowkey,自动判定是否包含需要的rowkey、假如包含就读取这个文件,假如不包含就过滤
小结


  • Hbase通过布隆过滤器,在写入数据时,创建布隆索引,读取数据时,根据布隆索引加快数据的检索
9.Hbase 总结二:

1、Hbase如何使用JavaAPI实现DML


  • step1:构建连接对象:Connection
  • step2:构建操作对象:HbaseAdmin | Table
  • step3:调用操作对象方法实现操作:put,Scan+Filter,Delete,Get,
2、Hbase的存储结构是什么


  • NS:Table | RegionServer
    Region:表的分区,对标的数据进行划分,按照Rowkey的范围,为了实现分布式
    Store:分区的数据划分,按照列族划分,为了加快查询效率
    Memstore:内存地区,写缓存
    StoreFile:磁盘地区,HDFS文件
3、Hbase的读写流程是什么


  • 元数据检索
    1.所有客户端必须先读取表的元数据,元数据存储在Hbase的meta表里,但是meta表在Zookeeper中,所以Hbase要先访问Zookeeper
    2.对表地点的region的regionserver进行哀求

  • 1.先写WAL:预写日志,保证内存数据安全
    2.再写内存
    3.Flush机制:将内存的数据溢写到磁盘
    4.Compaction机制:将多个有序小文件合并为整体有序的大文件
    5.Split机制:将一个分区划分为两个分区,减轻分区负载压力

  • 1.先读MemberStore
    2.可选:再读BlockCache【读缓存】,列族级别配置:默认True
    3.Split机制:将一个分区划分为两个分区,减轻分区负载压力
4、Hbase怎么保证数据的安全性


  • Region的安全性
  • memstore:WAL[HDFS]
  • StoreFile:HDFS副本机制
5、Hbase的热门问题是什么,怎么解决


  • 现象:短时间内,大量读写哀叱责部会合在一个Region分区中
  • 缘故原由:数据存储不平衡
    1.没有做预分区,
    2.做了多个分区,分区划分规则与Rowkey设计不匹配
    3.做了多个分区,Rowkey是连续的
  • 解决
    合理设计rowkey,遵照五大原则:业务原则,组合原则,散列原则【加盐】,长度原则
 6、Hbase的分区规则是什么


  • 划分分区:将-00~+00区间划分为多个端,每个段是前闭后开区间,一定是按照rowkey进行划分的
  • 数据分区:根据rowkey属于哪个Region的范围,就写入哪个Region
10.SQL On Hbase

11.Hive On Hbase 

先容:


  • 问题:
    Hbase是安列斯存储NoSQL.不支持SQL
    开发接口不方便大部门用户使用,怎么办?
    大数据开发:Hbase下令、Hbase Java AP
    Java开发【JDBC】、数据分析师【SQL】:怎么用Hbase?
  • 分析:需要一个工具能让Hbase支持SQL,支持JDBC方式对Hbase进行处理


    •            平常表数据:按行操作
      id     name         age     sex     addr
            001  zhangsan   18      male   shanghai
            002  lisi               20      female null
            003  wangwu     null     male   beijing ……
    • Hbase数据:按列操作
               
      1. rowkey          cf1:id      cf1:name        cf1:age     cf2:sex  cf2:addr
      2. zhangsan_001    001         zhangsan        18          null     shanghai
      3. lisi_002        002         lisi            20          female       null
      4. wangwu_003      003         wangwu          null         male     beijing
      5. ……
      复制代码

  • 可以基于Hbase数据构建结构化的数据形式
  • 可以用SQL来实现处理
  • 实现
  • 将Hbase表中每一行对应的所有列构建一张完备的结构化表
  • 假如这一行没有这一列,就补null

    • Hive:通过MapReduce来实现
    • Phoenix:通过Hbase API封装实现的



    • 功能实现Hive与Hbase集成,使用Hive SQL对Hbase的数据进行处理

      • Hbase:itcast:t1
        | 构建一个映射关系:数据存储在Hbase
      • Hive:itcast.t1
      • 用户可以通过SQL操作Hive中表
        1. select * from itcast.t1
        复制代码

    • 原理

      • 本质:在Hive中对Hbase关联的Hive表执行SQL语句,底层通过Hadoop中的Input和Output对Hbase表进行处理

    • 特点

      • 长处:支持完善的SQL语句,可以实现各种复杂SQL的数据处理及盘算,通过分布式盘算程序实现,对大数据量的数据处理比较友好
      • 缺点:不支持二级索引,数据量不是特别大的情况下,性能一样平常

    • 应用

      • 基于大数据高性能的离线读写,并且使用SQL来开发


  • 小结

    • Hive如何实现通过SQL读写Hbase数据?

      • 通过Hadoop中的Input类和Output类来实现
      • 长处:SQL支持非常全面
      • 缺点:不能解决查询不走索引问题,数据量小性能一样平常
      • 应用:离线架构中用于存储离线数据,离线开发,加快性能或者存储用户行为数据


配置: 


  • 修改hive-site.xml:Hive通过SQL访问Hbase,就是Hbase的客户端,就要连接zookeeper
    cd /export/server/hive
    vim conf/hive-site.xml
    1. <property>
    2.     <name>hive.zookeeper.quorum</name>
    3.     <value>node01,node02,node03</value>
    4. </property>
    5. <property>
    6.     <name>hbase.zookeeper.quorum</name>
    7.     <value>node01,node02,node03</value>
    8. </property>
    9. <property>
    10.     <name>hive.server2.enable.doAs</name>
    11.     <value>false</value>
    12. </property>
    复制代码
  • 修改hive-env.sh
    1. export HBASE_HOME=/export/servers/hbase-2.1.0
    复制代码
测试:


  • 实施
    假如Hbase中表已经存在,只能创建外部表
    1. --创建测试数据库
    2. create database course;
    3. use course;
    4. --创建测试表
    5. create external table course.t1(
    6. key string,
    7. name string,
    8. age  string,
    9. addr string,
    10. phone string
    11. )   
    12. stored by 'org.apache.hadoop.hive.hbase.HBaseStorageHandler'   
    13. with serdeproperties("hbase.columns.mapping"
    14. = ":key,basic:name,basic:age,other:addr,other:phone")  
    15. tblproperties("hbase.table.name" = "itcast:t1");
    复制代码
    查询
    1. select age,count(*) as cnt from t1 group by age order by cnt desc;
    复制代码
  • 注意

  • Hive的只是关联表,并没有数据,数据存储在Hbase表中
  • 在Hive中创建Hbase的关联表,关联乐成后,使用SQL通过MapReduce处理关联表
  • 假如Hbase中表已经存在,只能创建外部表,使用Key来表现rowkey
  • Hive中与Hbase关联的表,不能使用load写入数据,是能通过insert通过MR读写数据
二级索引的设计及问题

二级索引设计


  • 问题

    • 构建二级索引表:index_Table:查询条件 + 原表的rowkey
      1. rowkey:age_name_id          col:x
      2. 18_zhangsan_001             x
      3. 18_lisi_002                 x
      4. 20_zhangsan_003             x
      5. 109_wangwu_004              x
      6. ……
      复制代码
    • 需求:按照age查询,查询所有age = 20的人的信息

      • 通例方案:全表扫描用列值过滤器对每一行的这一列的值进行过滤

    • 构建数据表:source Table
      1. rowkey:name_id          id          name            age         sex         addr
      2. zhangsan_001            001         zhangsan        18          male        shanghai
      3. lisi_002                002         lisi            18          female      beijing
      4. zhangsan_003            003         zhangsan        20          male        
      5. wangwu_004              004         wangwu         109
      6. ……
      复制代码
    • 解决:二级索引

      • 思想:通过走两次索引来代替全表扫描
      • step1:基于存储和常用查询需求,构建原始数据表
      • step2:基于其他查询需求,构建索引表
      • step3:先查询索引表,再查询数据表


    • Hbase使用Rowkey作为唯一索引,只有按照rowkey的前缀查询才是走索引查询,其他查询都是全表扫描,性能比较差
      1. rowkey:name_id          id          name            age         sex         addr
      2. zhangsan_001            001         zhangsan        18          male        shanghai
      复制代码

      • 走索引:name、name + id
      • 现在有40%的需求是按照id、age、setx、addr来查询,不走索引,性能差,怎么办?

    • 二级索引问题
    • 问题:Hbase使用Rowkey作为唯一索引,只有按照rowkey的前缀查询才是走索引查询,其他查询都是全表扫描,性能比较差

        1. rowkey:name_id          id          name            age         sex   addr
        2. zhangsan_001            001         zhangsan        18        maleshanghai
        复制代码
      • 现在有40%的需求是按照id、age、setx、addr来查询,不走索引,性能差,怎么办?
      • 走索引:name、name + id

    • 解决:二级索引

      • 思想:通过走两次索引来代替全表扫描
      • step1:基于存储和常用查询需求,构建原始数据表
      • step2:基于其他查询需求,构建索引表
      • step3:先查询索引表,再查询数据表



      • 解决:基于差别查询条件构建差别二级索引表,先根据条件查询对应索引表,再查询原表
      • 缺点

        • 必须保证索引表与原表数据一致性问题
        • 差别条件需要差别的索引表,每次原表发生数据变化,所有的索引表都要同步变化:管理非常贫苦

      • 解决方案

        • 方案一:手动维护

          • 本身手动建索引表,本身手动维护同步,本身手动实现检索过程
            1. create(source_t1)
            2. create(index_age_t1)
            3. put 't1','zhangsan_001'
            4. put 'index_age_t1','20_zhangsan_001'
            5. if (condition = age)
            6.     rk = scan (index_age_t1)
            7.     scan(source_t1,rk)
            复制代码
          • 缺点:代码开发贫苦,无法保证一致性
          • 肯定不用

        • 方案二:本身开发协处理器

          • 监听原表,只要原表数据发生变化,自动对索引表进行操作
          • 长处:实现索引表与原表的同步
          • 缺点:索引表非常多,同步需求非常多,协处理器API非常繁琐,开发协处理器成本非常高

        • 方案三:第三方工具

          • Phoenix:底层是大量已经开发好的封装好的协处理器来实现的API操作

            • 开发者只要写SQL
              1. create index
              复制代码
            • 自动创建索引表
            • 自动维护索引表
            • 自动查询索引表





  • 小结

    • 什么是Hbase的二级索引?

      • 思想:通过走两次索引来代替全表扫描
      • 实现

        • step1:先基于查询条件构建条件索引表
        • step2:查询时,先根据查询条件查询索引表,得到原表的rowey

          • index table rowkey:查询条件 + 原表的Rowkey

        • step3:在根据获取的原表的rowkey查询原表

      • 问题:索引表非常多,索引同步非常贫苦
      • 解决:用第三方工具来实现:Phoenix


12.Phoenix

Phoenix的先容



  • 功能

    • 使用Phoenix自动构建二级索引并维护二级索引
    • 使用Phoenix实现基于SQL操作Hbase
    • 专门基于Hbase所设计的SQL on Hbase 工具
    • 原理

      • 上层提供了SQL接口

        • 底层全部通过Hbase Java API来实现,通过构建一系列的Scan和Put来实现数据的读写

      • 功能非常丰富

        • 底层封装了大量的内置的协处理器,可以实现各种复杂的处理需求,例如二级索引等


    • 特点

      • 长处

        • 支持SQL接口
        • 支持自动维护二级索引

      • 缺点

        • SQL支持的语法不友好,不是通用性SQL
        • Bug比较多

      • Hive on Hbase对比

        • Hive:SQL更加全面,但是不支持二级索引,底层通过分布式盘算工具来实现
        • Phoenix:SQL相对支持不全面,但是性能比较好,直接使用HbaseAPI,支持索引实现


    • 应用

      • Phoenix适用于任何需要使用SQL或者JDBC来快速的读写Hbase的场景
      • 或者需要构建及维护二级索引场景


Phoenix的安装配置



  • 下载:Phoenix Downloads | Apache Phoenix
  • 第一台机器上传
    1. cd /export/softwares/
    2. rz
    复制代码
  • 第一台机器解压
    1. tar -zxvf apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -C /export/servers/
    2. cd /export/servers/
    3. mv apache-phoenix-5.0.0-HBase-2.0-bin phoenix-5.0.0-HBase-2.0-bin
    复制代码
  • 修改三台Linux文件句柄数
    1. vim /etc/security/limits.conf
    复制代码
    1. #在文件的末尾添加以下内容,*号不能去掉
    2. * soft nofile 65536
    3. * hard nofile 131072
    4. * soft nproc 2048
    5. * hard nproc 4096
    复制代码
  • 将Phoenix所有jar包分发到Hbase的lib目录下
    1. #拷贝到第一台机器
    2. cd /export/servers/phoenix-5.0.0-HBase-2.0-bin/
    3. cp phoenix-* /export/servers/hbase-2.1.0/lib/
    4. #分发给第二台和第三台
    5. cd /export/servers/hbase-2.1.0/lib/
    6. scp phoenix-* node02:$PWD
    7. scp phoenix-* node03:$PWD
    复制代码
  • 修改hbase-site.xml,添加一下属性
    1. cd /export/servers/hbase-2.1.0/conf/
    2. vim hbase-site.xml
    复制代码
    1. <!-- 关闭流检查,从2.x开始使用async -->
    2. <property>
    3.    <name>hbase.unsafe.stream.capability.enforce</name>
    4.    <value>false</value>
    5. </property>
    6. <!-- 支持HBase命名空间映射 -->
    7. <property>
    8.    <name>phoenix.schema.isNamespaceMappingEnabled</name>
    9.    <value>true</value>
    10. </property>
    11. <!-- 支持索引预写日志编码 -->
    12. <property>
    13.    <name>hbase.regionserver.wal.codec</name>
    14.    <value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value>
    15. </property>
    复制代码
  • 同步给其他两台机器
    1. scp hbase-site.xml node02:$PWD
    2. scp hbase-site.xml node03:$PWD
    复制代码
  • 同步给Phoenix
    1. cp hbase-site.xml /export/servers/phoenix-5.0.0-HBase-2.0-bin/bin/
    复制代码
  • 重启Hbase
    1. stop-hbase.sh
    2. start-hbase.sh
    复制代码
  • 安装依赖
    1. yum -y install python-argparse
    复制代码
  • 启动Phoenix
    1. cd /export/servers/phoenix-5.0.0-HBase-2.0-bin/
    2. bin/sqlline.py node01:2181
    复制代码
  • 测试
    1. !tables
    复制代码
  • 退出
    1. !quit
    复制代码
Phoenix的DDL语法:NS



  • 实施

    • 创建NS
      1. create schema if not exists student;,
      复制代码
    • 切换NS
      1. use student;
      复制代码
    • 删除NS
      1. drop schema if exists student;
      复制代码

  • 小结

    • 基本与SQL语法一致
    • 注意:Phoenix中默认会将所有字符转换为大写,假如想要使用小写字母,必须加上双引号

Phoenix的DDL语法:Table



  • 列举
    !tables
  • 创建
    注意规则:
    建表的时间要指定字段
    谁是primary key 谁就是rowkey,每张表必须有主键
    定义字段时,要指定列族,列族的属性可以在建表语句中指定
    split:指定建表构建多个分区,每个分区段划分

    语法示例:
    1. CREATE TABLE my_schema.my_table (
    2.      id BIGINT not null primary key,
    3.      date Date
    4. );
    5. CREATE TABLE my_table (
    6.      id INTEGER not null primary key desc,
    7.      m.date DATE not null,
    8.      m.db_utilization DECIMAL,
    9.      i.db_utilization
    10. ) m.VERSIONS='3';
    11. CREATE TABLE stats.prod_metrics (
    12.      host char(50) not null,
    13.      created_date date not null,
    14.      txn_count bigint
    15.      CONSTRAINT pk PRIMARY KEY (host, created_date)
    16. );
    17.    CREATE TABLE IF NOT EXISTS "my_case_sensitive_table"(
    18.        "id" char(10) not null primary key,
    19.        "value" integer
    20.    ) DATA_BLOCK_ENCODING='NONE',VERSIONS=5,MAX_FILESIZE=2000000
    21.    split on (?, ?, ?);
    22.    CREATE TABLE IF NOT EXISTS my_schema.my_table (
    23.        org_id CHAR(15),
    24.        entity_id CHAR(15),
    25.        payload binary(1000),
    26.        CONSTRAINT pk PRIMARY KEY (org_id, entity_id)
    27.    ) TTL=86400
    复制代码
    假如Hbase中已存在会自动关联【常用】
    ​​​​​Hbase中建表并导入数据
    1. Hbase shell ORDER_INFO.txt
    复制代码
    ​​Phoenix中建表
    1. create table if not exists ORDER_INFO(){
    2. "id" varchar primary key,
    3. "C1"."USER_ID" varchar,
    4. "C1"."OPERATION_DATE" varchar,
    5. "C1"."PAYWAY" varchar,
    6. "C1"."PAY_MONEY" varchar,
    7. "C1"."STATUS" varchar,
    8. "C1"."CATEGORY" varchar
    9. } column_encoded_bytes=0;
    复制代码
    表名与列名都必须一致,巨细写严酷区分
  • 检察
    1. !desc order_info;
    复制代码
  • 删除 
    1. drop table if exists order_dt1;
    复制代码

小结 :


  • 创建表时,必须指定主键作为Rowkey,主键列不能加列族
  • Phoenix 4.8版本之前只要创建同名的Hbase表,会自动关联数据
  • Phoenix 4.8版本以后,不保举关联表的方式
  • 保举使用视图关联的方式来实现,假如要使用关联表的方式,必须加上以下参数
    1. column_encoded_bytes=0;
    复制代码
Phoenix的DML语法:upsert

基于order_info订单数据实现DML插入数据


  • 插入一条数据
    1. upsert into order_info values('z8f3ca6f-2f5c-44fd-9755-1792de183845','4944191','2020-04-25 12:09:16','1','4070','未提交','电脑');
    复制代码
  •  更新USERID为123456
    1. upsert into order_info("id","USER_ID") values('z8f3ca6f-2f5c-44fd-9755-1792de183845','123456');
    复制代码

Phoenix的DML语法:delete



  • 语法及示例
    1. DELETE FROM TEST;
    2. DELETE FROM TEST WHERE ID=123;
    3. DELETE FROM TEST NAME LIKE 'foo%';
    复制代码
  •  删除USER_ID为123456的rowkey数据
    1. delete from order_info where USER_ID='123456';
    复制代码
总结:与MySQL是一致的 
Phoenix的DQL语法:select

基于order_info订单数据实现DQL查询数据


  • 语法及示例
    1. SELETE * FROM TEST LIMIT 1000;
    2. SELECT * FROM TEST LIMIT 1000 OFFSET 100;
    3. SELECT full_name FROM SALES_PERSON WHERE ranking >= 5.0 UNION ALL SELECT
    4. reviewer_name FROM CUSTOMER_REVIEW WHERE score >= 8.0
    复制代码
  • 查询支付方式为1的数据
    1. selete "id",payway,pay_money,category from order_info where payway='1';
    复制代码

  •  查询每种支付方式对应的用户人数,并且按照用户人数降序排序
    1. selete
    2. payway,
    3. count(distinct user_id) as numb
    4. from order_info
    5. group by payway
    6. order by numb desc;
    复制代码
  •  查询数据的第60行到66行
    1. select * from order_info limit 7 offset 59;
    复制代码
  •  小结:
    基本查询与MySQL也是一致的,写的时间注意数据类型以及巨细写的问题即可,假如碰到SQL报错,检查语法是否支持
Phoenix的使用:预分区

创建表的时间,需要根据Rowkey来设计多个分区


  • Hbase下令建表
    1. create Ns;tbname,列族,预分区
    复制代码
  •  Phoenix也提供了创建表时,指定分区范围的语法
    1. CREATE TABLE IF NOT EXISTS "my_case_sensitive_table"(
    2.      "id" char(10) not null primary key,
    3.      "value" integer
    4. )
    5. DATA_BLOCK_ENCODING='NONE',VERSIONS=5,MAX_FILESIZE=2000000 split on (?, ?, ?)
    复制代码
  •  创建数据表,四个分区
    1. drop table if exists ORDER_DTL;
    2. create table if not exists ORDER_DTL(
    3.      "id" varchar primary key,
    4.      C1."status" varchar,
    5.      C1."money" float,
    6.      C1."pay_way" integer,
    7.      C1."user_id" varchar,
    8.      C1."operation_time" varchar,
    9.      C1."category" varchar
    10. )
    11. CONPRESSION='GZ'
    12. SPLIT ON ('3','5','7');
    复制代码

Phoenix的使用:加盐salt

Rowkey设计的时间为了避免连续,构建Rowkey的散列,假如rowkey设计是连续的,怎么解决?
正常表:tb1:3个分区:
       r1:-oo ~ 3
       r2: 3 ~ 6
       r3: 6 ~ +oo
       rowkey:数值开头
盐表:
        t2:3个分区
        每个分区的前缀是16进制的值
        rowkey:数值开头,但是Phoenix会自动为每个rowkey前面加上一个16进制的值
  Phoenix的使用:视图

直接关联Hbase中的表,会导致误删除,对数据的权限会有影响,容易出现问题,如何避免?
答:Phoenix中发起使用视图的方式来关联Hbase中已有的表,通过构建关联视图,可以解决大部门数据查询的数据,不影响数据,视图:可以理解为只读的表


  • 删除Phoenix中的ORDER_INFO
    1. create Table table (
    2. a_key varchar primary key,
    3. a_col varchar
    4. )salt_buckets=20;//20个分区
    复制代码
    会发现,Hbase中的表也会被删除
  •  重新加载
    1. drop table if exists ORDER_DTL;
    2. create table if not exists ORDER_DTL(
    3.     "id" varchar primary key;
    4.      C1."status" varchar,
    5.      C1."money" float,
    6.      C1."pay_way" integer,
    7.      C1."user_id" varchar,
    8.      C1."operation_time" varchar,
    9.      C1."category" varchar
    10. )CONPRESSION="GZ",SALT_BUCKETS=10;
    复制代码
  •  创建视图,关联Hbase中已经存在的表
    1. UPSERT INTO "ORDER_DTL" VALUES('02602f66-adc7-40d4-8485-76b5632b5b53','已提交',4070,1,'4944191','2020-04-25 12:09:16','手机;');
    2. UPSERT INTO "ORDER_DTL" VALUES('0968a418-f2bc-49b4-b9a9-2157cf214cfd','已完成',4350,1,'1625615','2020-04-25 12:09:37','家用电器;;电脑;');
    3. UPSERT INTO "ORDER_DTL" VALUES('0e01edba-5e55-425e-837a-7efb91c56630','已提交',6370,3,'3919700','2020-04-25 12:09:39','男装;男鞋;');
    4. UPSERT INTO "ORDER_DTL" VALUES('0f46d542-34cb-4ef4-b7fe-6dcfa5f14751','已付款',9380,1,'2993700','2020-04-25 12:09:46','维修;手机;');
    5. UPSERT INTO "ORDER_DTL" VALUES('1fb7c50f-9e26-4aa8-a140-a03d0de78729','已完成',6400,2,'5037058','2020-04-25 12:10:13','数码;女装;');
    6. UPSERT INTO "ORDER_DTL" VALUES('23275016-996b-420c-8edc-3e3b41de1aee','已付款',280,1,'3018827','2020-04-25 12:09:53','男鞋;汽车;');
    7. UPSERT INTO "ORDER_DTL" VALUES('2375a7cf-c206-4ac0-8de4-863e7ffae27b','已完成',5600,1,'6489579','2020-04-25 12:08:55','食品;家用电器;');
    8. UPSERT INTO "ORDER_DTL" VALUES('269fe10c-740b-4fdb-ad25-7939094073de','已提交',8340,2,'2948003','2020-04-25 12:09:26','男装;男鞋;');
    9. UPSERT INTO "ORDER_DTL" VALUES('2849fa34-6513-44d6-8f66-97bccb3a31a1','已提交',7060,2,'2092774','2020-04-25 12:09:38','酒店;旅游;');
    10. UPSERT INTO "ORDER_DTL" VALUES('28b7e793-6d14-455b-91b3-0bd8b23b610c','已提交',640,3,'7152356','2020-04-25 12:09:49','维修;手机;');
    11. UPSERT INTO "ORDER_DTL" VALUES('2909b28a-5085-4f1d-b01e-a34fbaf6ce37','已提交',9390,3,'8237476','2020-04-25 12:10:08','男鞋;汽车;');
    12. UPSERT INTO "ORDER_DTL" VALUES('2a01dfe5-f5dc-4140-b31b-a6ee27a6e51e','已提交',7490,2,'7813118','2020-04-25 12:09:05','机票;文娱;');
    13. UPSERT INTO "ORDER_DTL" VALUES('2b86ab90-3180-4940-b624-c936a1e7568d','已付款',5360,2,'5301038','2020-04-25 12:08:50','维修;手机;');
    14. UPSERT INTO "ORDER_DTL" VALUES('2e19fbe8-7970-4d62-8e8f-d364afc2dd41','已付款',6490,0,'3141181','2020-04-25 12:09:22','食品;家用电器;');
    15. UPSERT INTO "ORDER_DTL" VALUES('2fc28d36-dca0-49e8-bad0-42d0602bdb40','已付款',3820,1,'9054826','2020-04-25 12:10:04','家用电器;;电脑;');
    16. UPSERT INTO "ORDER_DTL" VALUES('31477850-8b15-4f1b-9ec3-939f7dc47241','已提交',4650,2,'5837271','2020-04-25 12:08:52','机票;文娱;');
    17. UPSERT INTO "ORDER_DTL" VALUES('39319322-2d80-41e7-a862-8b8858e63316','已提交',5000,1,'5686435','2020-04-25 12:08:51','家用电器;;电脑;');
    18. UPSERT INTO "ORDER_DTL" VALUES('3d2254bd-c25a-404f-8e42-2faa4929a629','已完成',5000,1,'1274270','2020-04-25 12:08:43','男装;男鞋;');
    19. UPSERT INTO "ORDER_DTL" VALUES('42f7fe21-55a3-416f-9535-baa222cc0098','已完成',3600,2,'2661641','2020-04-25 12:09:58','维修;手机;');
    20. UPSERT INTO "ORDER_DTL" VALUES('44231dbb-9e58-4f1a-8c83-be1aa814be83','已提交',3950,1,'3855371','2020-04-25 12:08:39','数码;女装;');
    21. UPSERT INTO "ORDER_DTL" VALUES('526e33d2-a095-4e19-b759-0017b13666ca','已完成',3280,0,'5553283','2020-04-25 12:09:01','食品;家用电器;');
    22. UPSERT INTO "ORDER_DTL" VALUES('5a6932f4-b4a4-4a1a-b082-2475d13f9240','已提交',50,2,'1764961','2020-04-25 12:10:07','家用电器;;电脑;');
    23. UPSERT INTO "ORDER_DTL" VALUES('5fc0093c-59a3-417b-a9ff-104b9789b530','已提交',6310,2,'1292805','2020-04-25 12:09:36','男装;男鞋;');
    24. UPSERT INTO "ORDER_DTL" VALUES('605c6dd8-123b-4088-a047-e9f377fcd866','已完成',8980,2,'6202324','2020-04-25 12:09:54','机票;文娱;');
    25. UPSERT INTO "ORDER_DTL" VALUES('613cfd50-55c7-44d2-bb67-995f72c488ea','已完成',6830,3,'6977236','2020-04-25 12:10:06','酒店;旅游;');
    26. UPSERT INTO "ORDER_DTL" VALUES('62246ac1-3dcb-4f2c-8943-800c9216c29f','已提交',8610,1,'5264116','2020-04-25 12:09:14','维修;手机;');
    27. UPSERT INTO "ORDER_DTL" VALUES('625c7fef-de87-428a-b581-a63c71059b14','已提交',5970,0,'8051757','2020-04-25 12:09:07','男鞋;汽车;');
    28. UPSERT INTO "ORDER_DTL" VALUES('6d43c490-58ab-4e23-b399-dda862e06481','已提交',4570,0,'5514248','2020-04-25 12:09:34','酒店;旅游;');
    29. UPSERT INTO "ORDER_DTL" VALUES('70fa0ae0-6c02-4cfa-91a9-6ad929fe6b1b','已付款',4100,1,'8598963','2020-04-25 12:09:08','维修;手机;');
    30. UPSERT INTO "ORDER_DTL" VALUES('7170ce71-1fc0-4b6e-a339-67f525536dcd','已完成',9740,1,'4816392','2020-04-25 12:09:51','数码;女装;');
    31. UPSERT INTO "ORDER_DTL" VALUES('71961b06-290b-457d-bbe0-86acb013b0e3','已完成',6550,3,'2393699','2020-04-25 12:08:49','男鞋;汽车;');
    32. UPSERT INTO "ORDER_DTL" VALUES('72dc148e-ce64-432d-b99f-61c389cb82cd','已提交',4090,1,'2536942','2020-04-25 12:10:12','机票;文娱;');
    33. UPSERT INTO "ORDER_DTL" VALUES('7c0c1668-b783-413f-afc4-678a5a6d1033','已完成',3850,3,'6803936','2020-04-25 12:09:20','酒店;旅游;');
    34. UPSERT INTO "ORDER_DTL" VALUES('7fa02f7a-10df-4247-9935-94c8b7d4dbc0','已提交',1060,0,'6119810','2020-04-25 12:09:21','维修;手机;');
    35. UPSERT INTO "ORDER_DTL" VALUES('820c5e83-f2e0-42d4-b5f0-83802c75addc','已付款',9270,2,'5818454','2020-04-25 12:10:09','数码;女装;');
    36. UPSERT INTO "ORDER_DTL" VALUES('83ed55ec-a439-44e0-8fe0-acb7703fb691','已完成',8380,2,'6804703','2020-04-25 12:09:52','男鞋;汽车;');
    37. UPSERT INTO "ORDER_DTL" VALUES('85287268-f139-4d59-8087-23fa6454de9d','已取消',9750,1,'4382852','2020-04-25 12:10:00','数码;女装;');
    38. UPSERT INTO "ORDER_DTL" VALUES('8d32669e-327a-4802-89f4-2e91303aee59','已提交',9390,1,'4182962','2020-04-25 12:09:57','机票;文娱;');
    39. UPSERT INTO "ORDER_DTL" VALUES('8dadc2e4-63f1-490f-9182-793be64fed76','已付款',9350,1,'5937549','2020-04-25 12:09:02','酒店;旅游;');
    40. UPSERT INTO "ORDER_DTL" VALUES('94ad8ee0-8898-442c-8cb1-083a4b609616','已提交',4370,0,'4666456','2020-04-25 12:09:13','维修;手机;');
    41. UPSERT INTO "ORDER_DTL" VALUES('994cbb44-f0ee-45ff-a4f4-76c87bc2b972','已付款',3190,3,'3200759','2020-04-25 12:09:25','数码;女装;');
    42. UPSERT INTO "ORDER_DTL" VALUES('9ff3032c-8679-4247-9e6f-4caf2dc93aff','已提交',850,0,'8835231','2020-04-25 12:09:40','男鞋;汽车;');
    43. UPSERT INTO "ORDER_DTL" VALUES('9ff4032c-1223-4247-9e6f-123456dfdsds','已付款',850,0,'8835231','2020-04-25 12:09:45','食品;家用电器;');
    44. UPSERT INTO "ORDER_DTL" VALUES('a467ba42-f91e-48a0-865e-1703aaa45e0e','已提交',8040,0,'8206022','2020-04-25 12:09:50','家用电器;;电脑;');
    45. UPSERT INTO "ORDER_DTL" VALUES('a5302f47-96d9-41b4-a14c-c7a508f59282','已付款',8570,2,'5319315','2020-04-25 12:08:44','机票;文娱;');
    46. UPSERT INTO "ORDER_DTL" VALUES('a5b57bec-6235-45f4-bd7e-6deb5cd1e008','已提交',5700,3,'6486444','2020-04-25 12:09:27','酒店;旅游;');
    47. UPSERT INTO "ORDER_DTL" VALUES('ae5c3363-cf8f-48a9-9676-701a7b0a7ca5','已付款',7460,1,'2379296','2020-04-25 12:09:23','维修;手机;');
    48. UPSERT INTO "ORDER_DTL" VALUES('b1fb2399-7cf2-4af5-960a-a4d77f4803b8','已提交',2690,3,'6686018','2020-04-25 12:09:55','数码;女装;');
    49. UPSERT INTO "ORDER_DTL" VALUES('b21c7dbd-dabd-4610-94b9-d7039866a8eb','已提交',6310,2,'1552851','2020-04-25 12:09:15','男鞋;汽车;');
    50. UPSERT INTO "ORDER_DTL" VALUES('b4bfd4b7-51f5-480e-9e23-8b1579e36248','已提交',4000,1,'3260372','2020-04-25 12:09:35','机票;文娱;');
    51. UPSERT INTO "ORDER_DTL" VALUES('b63983cc-2b59-4992-84c6-9810526d0282','已提交',7370,3,'3107867','2020-04-25 12:08:45','数码;女装;');
    52. UPSERT INTO "ORDER_DTL" VALUES('bf60b752-1ccc-43bf-9bc3-b2aeccacc0ed','已提交',720,2,'5034117','2020-04-25 12:09:03','机票;文娱;');
    53. UPSERT INTO "ORDER_DTL" VALUES('c808addc-8b8b-4d89-99b1-db2ed52e61b4','已提交',3630,1,'6435854','2020-04-25 12:09:10','酒店;旅游;');
    54. UPSERT INTO "ORDER_DTL" VALUES('cc9dbd20-cf9f-4097-ae8b-4e73db1e4ba1','已付款',5000,0,'2007322','2020-04-25 12:08:38','维修;手机;');
    55. UPSERT INTO "ORDER_DTL" VALUES('ccceaf57-a5ab-44df-834a-e7b32c63efc1','已提交',2660,2,'7928516','2020-04-25 12:09:42','数码;女装;');
    56. UPSERT INTO "ORDER_DTL" VALUES('d7be5c39-e07c-40e8-bf09-4922fbc6335c','已付款',8750,2,'1250995','2020-04-25 12:09:09','食品;家用电器;');
    57. UPSERT INTO "ORDER_DTL" VALUES('dfe16df7-4a46-4b6f-9c6d-083ec215218e','已完成',410,0,'1923817','2020-04-25 12:09:56','家用电器;;电脑;');
    58. UPSERT INTO "ORDER_DTL" VALUES('e1241ad4-c9c1-4c17-93b9-ef2c26e7f2b2','已付款',6760,0,'2457464','2020-04-25 12:08:54','数码;女装;');
    59. UPSERT INTO "ORDER_DTL" VALUES('e180a9f2-9f80-4b6d-99c8-452d6c037fc7','已完成',8120,2,'7645270','2020-04-25 12:09:32','男鞋;汽车;');
    60. UPSERT INTO "ORDER_DTL" VALUES('e4418843-9ac0-47a7-bfd8-d61c4d296933','已付款',8170,2,'7695668','2020-04-25 12:09:11','家用电器;;电脑;');
    61. UPSERT INTO "ORDER_DTL" VALUES('e8b3bb37-1019-4492-93c7-305177271a71','已完成',2560,2,'4405460','2020-04-25 12:10:05','男装;男鞋;');
    62. UPSERT INTO "ORDER_DTL" VALUES('eb1a1a22-953a-42f1-b594-f5dfc8fb6262','已完成',2370,2,'8233485','2020-04-25 12:09:24','机票;文娱;');
    63. UPSERT INTO "ORDER_DTL" VALUES('ecfd18f5-45f2-4dcd-9c47-f2ad9b216bd0','已付款',8070,3,'6387107','2020-04-25 12:09:04','酒店;旅游;');
    64. UPSERT INTO "ORDER_DTL" VALUES('f1226752-7be3-4702-a496-3ddba56f66ec','已付款',4410,3,'1981968','2020-04-25 12:10:10','维修;手机;');
    65. UPSERT INTO "ORDER_DTL" VALUES('f642b16b-eade-4169-9eeb-4d5f294ec594','已提交',4010,1,'6463215','2020-04-25 12:09:29','男鞋;汽车;');
    66. UPSERT INTO "ORDER_DTL" VALUES('f8f3ca6f-2f5c-44fd-9755-1792de183845','已付款',5950,3,'4060214','2020-04-25 12:09:12','机票;文娱;');
    复制代码

  •  应用场景:
    视图:Hbase中已经有这张表,写操作都是操作Hbase,Phoenix只提供读操作
    建表:建表:对这张表既要读也要使用Phoenix来写
Phoenix的使用:JDBC

工作中实际使用SQL,会基于程序中使用JDBC的方式来提交SQL语句,在Phoenix中如何实现?


  • Phoenix支持使用JDBC的方式来提交SQL语句
    1. select "id" from ORDER_DTL;
    复制代码
二级索引:全局索引设计

功能:当为某一列创建全局索引时,Phoenix自动创建一张索引表,将创建索引这一列加上原表rowkey作为新的rowkey




    • 原始数据表
      1. scan'ORDER_DTL';
      复制代码
    • 需求:根据name进行数据查询

      • 不走索引

    • 创建全局索引
      1. drop table if exists ORDER_INFO;
      复制代码
    • 自动构建索引表
      1. hbase shell ORDER_INFO.txt
      复制代码
    • 查询

      • 先查询索引表:通过rowkey获取名称对应的id
      • 再查询数据表:通过id查询对应的数据

    • 特点:默认只能对构建索引的字段做索引查询,假如查询中包含了不是索引的字段或者条件不是索引字段,不走索引
    • 应用:写少读多

      • 当原表的数据发生更新操作提交时,会被拦截
      • 先更新所有索引表,然后再更新原表


  • 小结

    • 了解二级索引中全局索引的设计思想

二级索引:覆盖索引设计



  • 功能:在构建全局索引时,将经常作为查询条件或者结果的列放入索引表中,直接通过索引来返回数据结果
  • 创建全局索引
    drop table if exists ORDER_INFO;
  • 自动构建索引
    rowkey:name_id        col:占位值
  • 假如需求发生改变,查询name和age,上面的全局索引会失效
  • 创建全局+覆盖索引
    create index index01 on tbname (name)include(age);
  • 自动构建索引表
    rowkey:name_id        col:age
  • 查询
    select name from table;
    select name from table where age = 20
    select name , age from table
  • 特点:基于全局索引构建,将常用的查询结果放入索引表中,直接从索引表返回结果,不用再查询原表
  • 应用:适合查询条件比较固定,数据量比较小的场景(不发起将大部门列都放入覆盖索引)
小结


  • 覆盖索引是基于全局索引实现的

    • 全局索引:用查询条件作为索引表rowkey,先查询索引表,再查询原表

  • 覆盖索引:用查询条件作为索引表rowkey,将经常查询的列直接放入索引表,查询直接从索引表返回
  • 目的是将常用的查询结果放入索引表中,直接从索引表返回数据
二级索引:本地索引设计

功能:将索引数据与对应的原始数据放在同一台机器,避免跨网络传输,进步写的性能


  • 与全局和覆盖的区别是什么?

    • 全局和覆盖都是单独构建一张索引表

  • 本地索引构建索引数据时,将索引数据直接存储在原表中,这条数据和这条数据的索引存储同一个region中

    • 满意:进步读的性能,也能降低对写的影响
    • 本地索引不会创建索引表
    • 怎么保证数据和索引能写入同一个region呢?

      • 数据:rowkey:id:001 name=zhangsan
      • 索引:rowkey:zhangsan_001
      • 为了保证索引和原始数据能写入同一个region,将这条数据对应的region的startkey作为索引rowkey前缀


  • 本地索引的设计方式是:
    原表数据地点region的start key+“\x00”+第一个二级索引字段+"\x00"+第二个二级索引字段…+"\x00"+原表rowkey
  • 本地索引的创建举例:
      create local index LOCAL_IDX_ORDER_DTL on ORDER_DTL("id", "status", "money", "pay_way", "user_id") ;
  • region划分

    • region0: -oo 30
    • region1:30 60
    • region2:60 +oo

  • 写入一条Rowkey:456 lisi 到原表的region中

    • region1

  • 目的:将这条rowkey的索引存在同一台机器的region中

    • 30_索引字段 _ 原表的Rowkey
    • 30_lisi_456

  • 特点:
    即使查询数据中包含了非索引字段,也会走本地索引

    本地索引会修改原始数据表
    假如构建了本地索引,不能通过Hbase的API来读写数据的,必须通过Phoenix来实现读写本地

    索引对盐表不生效的
  • 应用:写的操作比较多,进步了构建索引对写的性能影响,

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

圆咕噜咕噜

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表