Hbase DML、DDL命令 JavaAPI

Hbase理论

  1. Hbase的介绍

    • 功能:分布式NoSQL列存储数据库,用于实现大数据高性能的实时随机的数据存储
    • 应用场景:所有实时数据存储的场景
    • 特点
      • 分布式内存 + 分布式磁盘:大数据存储
      • 基于内存的列存储:高性能数据的存储
      • MySQL:只能满足小数据量的存储
      • HDFS:纯离线的文件系统
        • Hbase虽然基于HDFS,但是整体是一个实时的技术
        • Hbase上层是分布式内存,底层才是HDFS
        • Hbase将数据存储在HDFS上,也做了性能优化
          • 列族
          • 按列存储
          • 构建有序
      • Hive:Hive本质还是HDFS,只是对HDFS封装了表的结构接口
        • Hive功能:构建离线数据仓库:直接使用HDFS存储
        • Hbase功能:用于解决实时数据存储:内存+优化HDFS存储
    • 设计思想:怎么实现?
      • 大数据:分布式的设计
      • 高性能:基于内存
      • 核心:冷热数据分离
        • 刚产生的数据,被读的概率最大,称为热数据,将热数据写入分布式内存
        • 产生很久的数据,被读的概率比较小,就是冷数据,将冷数据存储在磁盘中【HDFS】
  2. Hbase的设计概念

    • 数据库:Namespace

    • 表:Table

      • 任何一张表,都属于某个NS

      • 在访问表时,除了default的Namespace中的表,其他所有Namespace的表在使用时,必须加上NS名称

        nsName:tbName
        
      • 表:分布式表

        • 任何一张表都可以划分为多个分区,每个分区存储在不同的机器上
        • Region:表的分区,存储在RegionServer
    • 行:Rowkey

      • 行健:每张表都自带行健这一列,这一列的值由我们自己定义
      • 唯一标记一行
      • 作为唯一索引:Hbase中不支持创建索引,默认只有rowkey作为唯一索引
        • 只有按照rowkey查询,才是走索引的查询
    • 列:column family : column,唯一标识一列

      • column family :列族,对列的分组,将所有列划分不同的组中,为了提高性能
        • 假设100列,如果不分组,查询时,最多比较100列才能找到我要的列
        • 假设100列,如果分组了,每组50列,查询时,告诉你在第一列,最多比较51次
      • column:每一列都属于某一个列族,相同列族中列会存储在一起
    • 支持多版本:一列中可以存储多个版本的值,通过时间戳来区分,列族级别的属性

  3. Hbase架构组成

    • 架构中角色
      • Hbase:主从架构
        • HMaster:管理节点
        • HRegionServer:存储结构,用于构建分布式内存
      • HDFS:分布式磁盘
      • Zookeeper:存储元数据、辅助选举
    • 集群搭建【搭建成功即可】

Hbase使用场景

1、集群管理

  • 应用场景:运维做运维集群管理,我们开发用的不多

  • 需求:封装Hbase集群管理命令脚本

    • 类似于hive -f xxx.sql

    • 举个栗子:每天Hbase集群能定时的自动创建一张表

    • 分析

      • 要实现运行Hbase脚本:创建表:/export/data/hbase_create_day.sh

        #!/bin/bash
        create 'tbname','cf1'
        
        • 问题是:怎么能通过Linux命令行运行Hbase的命令呢?
      • 要实现定时调度:Linux Crontab、Oozie、Azkaban

        00 00 *		*	*		sh /export/data/hbase_create_day.sh
        
  • 实现:通过Hbase的客户端运行命令文件,通过调度工具进行调度实现定时运行

  • 用法:hbase shell 文件路径

    • step1:将Hbase的命令封装在一个文件中:vim /export/data/hbase.txt

      list
      exit
      
    • step2:运行Hbase命令文件

      hbase shell /export/data/hbase.txt
      
    • step3:封装到脚本

      #!/bin/bash
      hbase shell /export/data/hbase.txt
      
  • 注意:所有的Hbase命令文件,最后一行命令必须为exit

2、测试开发

  • 需求:一般用于测试开发,执行DDL操作,类似于SQL之类的命令

  • 实现:Hbase shell命令行

  • 用法:hbase shell

在这里插入图片描述

3、生产开发

  • 需求:一般用于生产开发,通过MapReduce或者Spark等程序读写Hbase,类似于JDBC

    • 举个栗子:读取Hbase中的数据,进行分析处理,统计UV、PV
    • 分析
      • step1:通过分布式计算程序Spark、Flink读取Hbase数据
      • step2:对读取到的数据进行统计分析
      • step3:保存结果
  • 实现:分布式计算程序通过Java API读写Hbase,实现数据处理

  • 用法:在MapReduce或者Spark中集成API‘

Hbase运行命令脚本文件

  • 运行文件

    hbase shell /export/data/ORDER_INFO.txt
    
  • 查看表中数据

    scan 'ORDER_INFO',{FORMATTER=>'toString'}
    
  • 注意事项

    • 这种运行文件的方式一般不用导入数据的,一般用于做集群管理

    • 工作中会定时的执行这个Hbase文件,这个文件的最后一行必须加上exit

DDL命令:NS的管理

0、命令行的使用

  • 启动

    hbase shell
    
  • 查看帮助

    help
    
    • DDL:数据库命令

      Group name: namespace
        Commands: alter_namespace, create_namespace, describe_namespace, drop_namespace, list_namespace, list_namespace_tables
      
    • DDL:表命令

      Group name: ddl
        Commands: alter, alter_async, alter_status, clone_table_schema, create, describe, disable, disable_all, drop, drop_all, enable, enable_all, exists, get_table, is_disabled, is_enabled, list, list_regions, locate_region, show_filters
      
    • DML:数据操作命令

      Group name: dml
        Commands: append, count, delete, deleteall, get, get_counter, get_splits, incr, put, scan, truncate, truncate_preserve
      
    • 工具命令

      Group name: tools
        Commands: assign, balance_switch, balancer, balancer_enabled, catalogjanitor_enabled, catalogjanitor_run, catalogjanitor_switch, cleaner_chore_enabled, cleaner_chore_run, cleaner_chore_switch, clear_block_cache, clear_compaction_queues, clear_deadservers, close_region, compact, compact_rs, compaction_state, flush, is_in_maintenance_mode, list_deadservers, major_compact, merge_region, move, normalize, normalizer_enabled, normalizer_switch, split, splitormerge_enabled, splitormerge_switch, stop_master, stop_regionserver, trace, unassign, wal_roll, zk_dump
      
  • 查看命令的用法

    help '命令'
    
  • 退出

    exit
    

1、列举所有的NS

  • 命令:list_namespace

    • SQL:show databases
  • 语法

    list_namespace
    

2、列举NS中的所有表

  • 命令:list_namespace_tables

    • SQL:show tables dbname
  • 语法

    list_namespace_tables 'Namespace的名称'
    

3、创建NS

  • 命令:create_namespace

    • SQL:create database dbname
  • 语法

    create_namespace 'Namespace的名称'
    

4、删除NS

  • 命令:drop_namespace

    • 只能删除空数据库,如果数据库中存在表,不允许删除
  • 语法

    drop_namespace 'Namespace的名称'
    

DDL命令:Table的管理

1、列举表

  • 命令:list

    • SQL:show tables
  • 语法

  list

2、创建表

  • 命令:create

    • SQL:表名 + 列的信息【名称和类型】
    
      create table tbname(
      col1 type1 ,
      ……
      colN typeN
      );
    
    • Hbase:必须指定表名 + 至少一个列族
  • 语法

    //表示在ns1的namespace中创建一张表t1,这张表有一个列族叫f1,这个列族中的所有列可以存储5个版本的值
    create 'ns1:t1', {NAME => 'f1', VERSIONS => 5}
    //在default的namespace中创建一张表t1,这张表有三个列族,f1,f2,f3,每个列族的属性都是默认的
    create 't1', 'f1', 'f2', 'f3'
    
  • 示例

    #如果需要更改列族的属性,使用这种写法
    create 't1',{NAME=>'cf1'},{NAME=>'cf2',VERSIONS => 3}
    #如果不需要更改列族属性
    create 'itcast:t2','cf1','cf3' = create 't1',{NAME=>'cf1'},{NAME=>'cf2'}{NAME=>'cf3'}
    

3、查看表

  • 命令:desc

    • SQL :desc tbname
  • 语法

    desc '表名'
    
  • 示例

    desc 't1'
    
    hbase(main):022:0> desc 't1'
    Table t1 is ENABLED                                                                                                  
    t1                                                                                                                   
    COLUMN FAMILIES DESCRIPTION                                                                                          
    {NAME => 'cf1', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'}                                                                                                                 
    {NAME => 'cf2', VERSIONS => '3', EVICT_BLOCKS_ON_CLOSE => 'false', NEW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', MIN_VERSIONS => '0', REPLICATION_SCOPE => '0', BLOOMFILTER => 'ROW', CACHE_INDEX_ON_WRITE => 'false', IN_MEMORY => 'false', CACHE_BLOOMS_ON_WRITE => 'false', PREFETCH_BLOCKS_ON_OPEN => 'false', COMPRESSION => 'NONE', BLOCKCACHE => 'true', BLOCKSIZE => '65536'} 
    

4、删除表

  • 命令:drop

    • SQL:drop table tbname
  • 语法

    drop '表名'
    
  • 示例

    drop 't1'
    
  • 注意:如果要对表进行删除,必须先禁用表,再删除表

5、禁用/启用表

  • 命令:disable / enable

  • 功能

    • Hbase为了避免修改或者删除表,这张表正在对外提供读写服务
    • 规则:修改或者删除表时,必须先禁用表,表示这张表暂时不能对外提供服务
    • 如果是删除:禁用以后删除
    • 如果是修改:先禁用,然后修改,修改完成以后启用
  • 语法

    disable '表名'
    enable '表名'
    
  • 示例

    disable 't1'
    enable 't1'
    

6、判断表是否存在

  • 命令:exists

  • 语法

    exists '表名'
    
  • 示例

    exists 't1'
    

DML命令:put

  • 功能:插入 / 更新数据【某一行的某一列】

    • SQL
      • insert:用于插入一条新的数据
      • update:用于更新一条数据
      • replace:插入更新命令,如果不存在就插入,如果存在就更新
        • 原理:先做判断,如果不存在,就直接插入,如果存在,就删除再插入
    • Hbase:put
      • 等同于replace
  • 语法

    put  NS名称:表的名称,'Rowkey','列族:列','值'
    put 'ns1:t1', 'r1', 'cf:c1', 'value'
    
  • 示例

    put 'itcast:t2','20210201_001','cf1:name','laoda'
    put 'itcast:t2','20210201_001','cf1:age',18
    put 'itcast:t2','20210201_001','cf3:phone','110'
    put 'itcast:t2','20210201_001','cf3:addr','shanghai'
    put 'itcast:t2','20210101_000','cf1:name','laoer'
    put 'itcast:t2','20210101_000','cf3:addr','bejing'
    
  • 注意

    • put:如果不存在,就插入,如果存在就更新

      put 'itcast:t2','20210101_000','cf1:name','laosan'
      

在这里插入图片描述

    • 写入的数据按照Rowkey进行了排序:按照Rowkey的字典顺序进行排序

      • 每个rowkey内部按照列做了排序
    • Hbase中的更新是假的,伪更新

      • 只是让用户看起来更新了,但实际没有更新

      • 实现更新数据时,插入了一条新的数据,老的数据被标记为更新不显示,没有被真正删除

      • 只显示最新的数据,不显示老的数据

      • 为什么要这么做?为什么不直接对数据进行修改,而是插入一条新的数据来代替更新操作

        • 保证性能
  • 观察结果
    在这里插入图片描述

DML命令:get

  • 功能:读取某个Rowkey的数据

    • 注意:缺点:get命令最多只能返回一个rowkey的数据,根据Rowkey进行检索数据
    • Rowkey作为Hbase的唯一索引,按照rowkey是走索引的
    • 优点:Get是Hbase中查询数据最快的方式
  • 语法

    get	表名		rowkey		[列族,列]
    get 'ns:tbname','rowkey'
    get 'ns:tbname','rowkey',[cf]
    get 'ns:tbname','rowkey',[cf] | [cf:col]
    
  • 示例

    get 'ORDER_INFO','f8f3ca6f-2f5c-44fd-9755-1792de183845'
    get 'ORDER_INFO','f8f3ca6f-2f5c-44fd-9755-1792de183845','C1'
    get 'ORDER_INFO','f8f3ca6f-2f5c-44fd-9755-1792de183845','C1:USER_ID'
    

在这里插入图片描述

DML命令:scan

  • 功能:根据条件匹配读取多个Rowkey的数据

  • 语法

    scan 'tbname'//一般不用
    scan 'tbname',{Filter} //用到最多
    
  • 示例

    hbase> scan 't1', {ROWPREFIXFILTER => 'row2', FILTER => "
        (QualifierFilter (>=, 'binary:xyz')) AND (TimestampsFilter ( 123, 456))"}
      hbase> scan 't1', {FILTER =>
        org.apache.hadoop.hbase.filter.ColumnPaginationFilter.new(1, 0)}
    
    scan 'itcast:t2'
    #rowkey前缀过滤器
    scan 'itcast:t2', {ROWPREFIXFILTER => '2021'}
    scan 'itcast:t2', {ROWPREFIXFILTER => '202101'}
    #rowkey范围过滤器
    #STARTROW:从某个rowkey开始,包含,闭区间
    #STOPROW:到某个rowkey结束,不包含,开区间
    scan 'itcast:t2',{STARTROW=>'20210101_000'}
    scan 'itcast:t2',{STARTROW=>'20210201_001'}
    scan 'itcast:t2',{STARTROW=>'20210101_000',STOPROW=>'20210201_001'}
    scan 'itcast:t2',{STARTROW=>'20210201_001',STOPROW=>'20210301_007'}
    |
    
    

在这里插入图片描述
注意

  • 在Hbase数据检索,尽量走索引查询:按照Rowkey条件查询

  • 尽量避免走全表扫描

    • 索引查询:有一本新华字典,这本字典可以根据拼音检索,找一个字,先找目录,找字
    • 全表扫描:有一本新华字典,这本字典没有检索目录,找一个字,一页一页找
  • Hbase所有Rowkey的查询都是前缀匹配

    Rowkey的是什么至关重要:20210201_001,时间_用户id
    如果我要查询:2021年2月到2021年9月所有的数据
    	scan 'itcast:t2',{startrow => 202102 ,stoprow = 202110}
    	走索引查询
    	
    如果rowkey的前缀是用户id,001_20210201
    如果我要查询:2021年2月到2021年9月所有的数据
    	scan 'itcast:t2',{columnValuePrefix(time >= 202102 and time <= 202109)}
    	不走索引
    

DML命令:delete

  • 功能:删除Hbase中的数据

  • 语法

    #删除某列的数据
    delete     tbname,rowkey,cf:col
    #删除某个rowkey数据
    deleteall  tbname,rowkey
    #清空所有数据
    truncate   tbname
    
  • 示例

    delete 'itcast:t2','20210101_000','cf3:addr'
    deleteall 'itcast:t2','20210101_000'
    truncate 'itcast:t2'
    

在这里插入图片描述

DML命令:incr

  • 功能:一般用于自动计数的,不用记住上一次的值,直接做自增

    • 需求:一般用于做数据的计数

    • 与Put区别

      • put:需要记住上一次的值是什么

        put 'NEWS_VISIT_CNT','0000000001_00:00-01:00','C1:CNT',12
        put 'NEWS_VISIT_CNT','0000000001_00:00-01:00','C1:CNT',13
        put 'NEWS_VISIT_CNT','0000000001_00:00-01:00','C1:CNT',14
        
      • incr:不需要知道上一次的值是什么,自动计数

        incr 'NEWS_VISIT_CNT','0000000001_00:00-01:00','C1:CNT',12
        incr 'NEWS_VISIT_CNT','0000000001_00:00-01:00','C1:CNT'
        incr 'NEWS_VISIT_CNT','0000000001_00:00-01:00','C1:CNT'
        
  • 语法

    incr  '表名','rowkey','列族:列'
    get_counter '表名','rowkey','列族:列'
    
  • 示例

    create 'NEWS_VISIT_CNT', 'C1'
    incr 'NEWS_VISIT_CNT','0000000001_00:00-01:00','C1:CNT',12
    get_counter 'NEWS_VISIT_CNT','0000000001_00:00-01:00','C1:CNT'
    incr 'NEWS_VISIT_CNT','0000000001_00:00-01:00','C1:CNT'
    

在这里插入图片描述

DML命令:count

  • 功能:统计某张表的行数【rowkey的个数】

  • 语法

    count  '表名'
    
  • 示例

    count 'ORDER_INFO'
    
  • 面试题:Hbase中如何统计一张表的行数最快?

    • 方案一:分布式计算程序,读取Hbase数据,统计rowkey的个数

      #在第三台机器启动
      start-yarn.sh
      #在第一台机器运行
      hbase org.apache.hadoop.hbase.mapreduce.RowCounter 'ORDER_INFO'
      
    - 方案二:count命令,相对比较常用,速度中等
    
    

    count ‘ORDER_INFO’

    - 方案三:协处理器,最快的方式
    
      - 类似于Hive中的UDF,自己开发一个协处理器,监听表,表中多一条数据,就加1
      - 直接读取这个值就可以得到行数了
    

    JavaAPI:构建工程依赖

指定Hbase集群的服务端地址:给定ZK的地址 hbase-site.xml

1、创建工程,以及模块
maven
在这里插入图片描述

2、导入Hbase的依赖到模块

  • 导入Maven依赖
    在这里插入图片描述
    pom文件:
<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>

3、将log4j日志记录配置文件放入resources目录下

在这里插入图片描述
log4j.properties

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Define some default values that can be overridden by system properties
hbase.root.logger=INFO,console
hbase.security.logger=INFO,console
hbase.log.dir=.
hbase.log.file=hbase.log
hbase.log.level=INFO

# Define the root logger to the system property "hbase.root.logger".
log4j.rootLogger=${hbase.root.logger}

# Logging Threshold
log4j.threshold=ALL

#
# Daily Rolling File Appender
#
log4j.appender.DRFA=org.apache.log4j.DailyRollingFileAppender
log4j.appender.DRFA.File=${hbase.log.dir}/${hbase.log.file}

# Rollver at midnight
log4j.appender.DRFA.DatePattern=.yyyy-MM-dd

# 30-day backup
#log4j.appender.DRFA.MaxBackupIndex=30
log4j.appender.DRFA.layout=org.apache.log4j.PatternLayout

# Pattern format: Date LogLevel LoggerName LogMessage
log4j.appender.DRFA.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2}: %.1000m%n

# Rolling File Appender properties
hbase.log.maxfilesize=256MB
hbase.log.maxbackupindex=20

# Rolling File Appender
log4j.appender.RFA=org.apache.log4j.RollingFileAppender
log4j.appender.RFA.File=${hbase.log.dir}/${hbase.log.file}

log4j.appender.RFA.MaxFileSize=${hbase.log.maxfilesize}
log4j.appender.RFA.MaxBackupIndex=${hbase.log.maxbackupindex}

log4j.appender.RFA.layout=org.apache.log4j.PatternLayout
log4j.appender.RFA.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2}: %.1000m%n

#
# Security audit appender
#
hbase.security.log.file=SecurityAuth.audit
hbase.security.log.maxfilesize=256MB
hbase.security.log.maxbackupindex=20
log4j.appender.RFAS=org.apache.log4j.RollingFileAppender
log4j.appender.RFAS.File=${hbase.log.dir}/${hbase.security.log.file}
log4j.appender.RFAS.MaxFileSize=${hbase.security.log.maxfilesize}
log4j.appender.RFAS.MaxBackupIndex=${hbase.security.log.maxbackupindex}
log4j.appender.RFAS.layout=org.apache.log4j.PatternLayout
log4j.appender.RFAS.layout.ConversionPattern=%d{ISO8601} %p %c: %.1000m%n
log4j.category.SecurityLogger=${hbase.security.logger}
log4j.additivity.SecurityLogger=false
#log4j.logger.SecurityLogger.org.apache.hadoop.hbase.security.access.AccessController=TRACE
#log4j.logger.SecurityLogger.org.apache.hadoop.hbase.security.visibility.VisibilityController=TRACE

#
# Null Appender
#
log4j.appender.NullAppender=org.apache.log4j.varia.NullAppender

#
# console
# Add "console" to rootlogger above if you want to use this
#
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.target=System.err
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%d{ISO8601} %-5p [%t] %c{2}: %.1000m%n

log4j.appender.asyncconsole=org.apache.hadoop.hbase.AsyncConsoleAppender
log4j.appender.asyncconsole.target=System.err

# Custom Logging levels

log4j.logger.org.apache.zookeeper=${hbase.log.level}
#log4j.logger.org.apache.hadoop.fs.FSNamesystem=DEBUG
log4j.logger.org.apache.hadoop.hbase=${hbase.log.level}
log4j.logger.org.apache.hadoop.hbase.META=${hbase.log.level}
# Make these two classes INFO-level. Make them DEBUG to see more zk debug.
log4j.logger.org.apache.hadoop.hbase.zookeeper.ZKUtil=${hbase.log.level}
log4j.logger.org.apache.hadoop.hbase.zookeeper.ZKWatcher=${hbase.log.level}
#log4j.logger.org.apache.hadoop.dfs=DEBUG
# Set this class to log INFO only otherwise its OTT
# Enable this to get detailed connection error/retry logging.
# log4j.logger.org.apache.hadoop.hbase.client.ConnectionImplementation=TRACE


# Uncomment this line to enable tracing on _every_ RPC call (this can be a lot of output)
#log4j.logger.org.apache.hadoop.ipc.HBaseServer.trace=DEBUG

# Uncomment the below if you want to remove logging of client region caching'
# and scan of hbase:meta messages
# log4j.logger.org.apache.hadoop.hbase.client.ConnectionImplementation=INFO

# EventCounter
# Add "EventCounter" to rootlogger if you want to use this
# Uncomment the line below to add EventCounter information
# log4j.appender.EventCounter=org.apache.hadoop.log.metrics.EventCounter

# Prevent metrics subsystem start/stop messages (HBASE-17722)
log4j.logger.org.apache.hadoop.metrics2.impl.MetricsConfig=WARN
log4j.logger.org.apache.hadoop.metrics2.impl.MetricsSinkAdapter=WARN
log4j.logger.org.apache.hadoop.metrics2.impl.MetricsSystemImpl=WARN

JavaAPI:DDL

在这里插入图片描述
HbaseClientDDLTest

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 java.io.IOException;
import java.util.List;

/**
 * @ClassName HbaseClientDDLTest
 * @Description TODO 通过Java API来实现DDL操作
 *            Namespace:列举、创建、删除
 *            Table:列举、创建、删除
 * @Date 2021/3/17 11:11
 * @Create By     Frank
 */
public class HbaseClientDDLTest {
    public static void main(String[] args) throws IOException {
        //构建实例
        HbaseClientDDLTest ddlTest = new HbaseClientDDLTest();
        //todo:1-构建连接对象
        //构建配置对象,用于管理当前程序的所有配置:用于加载hbase-default和hbase-site文件
        Configuration conf = HBaseConfiguration.create();
        //在配置中指定服务端地址:Hbase服务端地址:Zookeeper地址
        conf.set("hbase.zookeeper.quorum","node1:2181,node2:2181,node3:2181");
        //构建连接对象
        Connection conn = ConnectionFactory.createConnection(conf);
        //todo:2-DDL,必须构建管理员
        HBaseAdmin admin = (HBaseAdmin) conn.getAdmin();
        //todo:3-NS操作:列举、创建、删除
//        ddlTest.listNs(admin);
//        ddlTest.createNs(admin);
//        ddlTest.dropNs(admin);
        //todo:4-Table操作:列举、创建、删除
//        ddlTest.listTable(admin);
        ddlTest.ceateTable(admin);
        //todo:5-释放资源
        admin.close();
        conn.close();
    }

    /**
     * 创建表
     * @param admin
     * @throws IOException
     */
    private void ceateTable(HBaseAdmin admin) throws IOException {
        //定义一个表对象:两个列族,basic【3个版本】和other
        TableName tbname = TableName.valueOf("itcast:t1");
        //先判断表是否存在,如果已经存在,先删除再创建
        if(admin.tableExists(tbname)){
            //先禁用
            admin.disableTable(tbname);
            //然后删除
            admin.deleteTable(tbname);
        }
        //构建列族的描述器对象
        ColumnFamilyDescriptor basic = ColumnFamilyDescriptorBuilder
                .newBuilder(Bytes.toBytes("basic"))//指定列族名称
                .setMaxVersions(3)//设置最大存储版本数
                .build();

        ColumnFamilyDescriptor other = ColumnFamilyDescriptorBuilder
                .newBuilder(Bytes.toBytes("other"))
                .build();

        //构建一个表的描述器对象
        TableDescriptor desc = TableDescriptorBuilder
                .newBuilder(tbname) //指定表的名称
                .setColumnFamily(basic)//指定列族
                .setColumnFamily(other)//指定列族
                .build();
        //创建表
        admin.createTable(desc);
    }

    /**
     * 列举所有的表
     * @param admin
     * @throws IOException
     */
    private void listTable(HBaseAdmin admin) throws IOException {
        //调用列举表的方法
        List<TableDescriptor> tableDescriptors = admin.listTableDescriptors();
        //迭代取出每张表的信息
        for (TableDescriptor tableDescriptor : tableDescriptors) {
            //打印表名
            System.out.println(tableDescriptor.getTableName().getNameAsString());
        }
    }

    /**
     * 删除NS
     * @param admin
     */
    private void dropNs(HBaseAdmin admin) throws IOException {
        admin.deleteNamespace("bigdata");
    }

    /**
     * 创建NS
     * @param admin
     */
    private void createNs(HBaseAdmin admin) throws IOException {
        //创建一个NS的描述器
        NamespaceDescriptor descriptor = NamespaceDescriptor
                .create("bigdata")//指定NS的名称
                .build();
        //调用创建的方法
        admin.createNamespace(descriptor);
    }

    /**
     * 用于实现列举当前所有的NS
     * @param admin
     */
    private void listNs(HBaseAdmin admin) throws IOException {
        //调用列举NS的方法
        NamespaceDescriptor[] descriptors = admin.listNamespaceDescriptors();
        //迭代取出每个NS的信息,打印名称即可
        for (NamespaceDescriptor descriptor : descriptors) {
            System.out.println(descriptor.getName());
        }
    }
}

JavaAPI:DML

Java API:DML:Table

  • 需求:通过JavaAPI实现对Hbase的DML操作

  • 分析

    • step1:构建连接对象
    • step2:构建Table对象
      • 所有的DML操作,必须构建一个表的对象
  • 实现

    //todo:1-构建一个连接对象
    Configuration conf = HBaseConfiguration.create();
    //指定Hbase集群的服务端地址:给定ZK的地址
    conf.set("hbase.zookeeper.quorum","node1:2181,node2:2181,node3:2181");
    Connection conn = ConnectionFactory.createConnection(conf);
    //todo:2-所有的DML必须构建一个Table表的对象
    //构建一个表的对象
    TableName tbname = TableName.valueOf("itcast:t1");
    Table table = conn.getTable(tbname);
    
  • 总结

    • step1:先构建一个连接对象:Connection
    • step2:根据所做的操作决定构建哪种对象
    • DDL:HbaseAdmin
      • DML:Table

Java API:DML:Put

  • 需求:JavaAPI实现往Hbase表中写入数据

    put		表		rowkey		列族:列		值
    
  • 分析

    • step1:Hbase中实现put操作,需要构建Put对象
    • step2:为Put对象添加列族、列名、值
    • step3:对表执行put操作
  • 实现

    /**
         * 用于实现使用Put插入数据到表中:put		表		rowkey		列族:列		值
         * @param table
         */
        private void putData(Table table) throws IOException {
            //step1:构建Put对象,必须指定插入的rowkey的数据
            Put put = new Put(Bytes.toBytes("20201001_888"));
            //step2:初始化put,添加列族、列、值
            put.addColumn(
                    Bytes.toBytes("basic"),//指定列族
                    Bytes.toBytes("name"),//指定列的名称
                    Bytes.toBytes("laoba")//指定列的值
            );
            //step3:让表执行put操作
            table.put(put);     
        }
    
  • 总结

在这里插入图片描述

  • Put对象:专门用于往Hbase写入数据的对象
    • 指定Rowkey,添加列族、列、值信息
  • table.put

Java API:DML:Get

  • 需求:JavaAPI实现从Hbase表中读取某个Rowkey的数据

    get 表名	rowkey	【列族,列】
    
  • 分析

    • step1:Hbase中实现get操作,需要构建Get对象

    • step2:为Get对象添加列族、列名

    • step3:对表执行get操作,获取返回值,打印输出

      table.get(Get)
      
  • 实现

    /**
         * 使用Get查询Hbase表中的数据:get 表名	rowkey	【列族,列】
         * @param table
         */
        private void getData(Table table) throws IOException {
            //step1:构建Get对象,必须指定Rowkey
            Get get = new Get(Bytes.toBytes("20201001_888"));
            //step2:可选的,配置Get需要的参数:列族、列
    //        get.addColumn()//指定获取某一列的数据
    //        get.addFamily()//指定获取某个列族的数据
            //step3:表执行get操作
            Result result = table.get(get);//Get操作的返回值为Result类型对象
            /**
             * 迭代输出Result对应的rowkey中的每一列的数据,一个Rowkey包含很多列,循环迭代输出每一列的数据
             *      Result:一个Result对象就是一个Rowkey的数据
             *      Cell:一个Cell代表一个rowkey中一列的数据
             *      一个Result对象中会包含多个Cell
             */
            for(Cell cell : result.rawCells()){
                //输出每一列Cell对象中的数据:20201001_888  column=basic:age, timestamp=1616029665232, value=20
                System.out.println(
                        Bytes.toString(CellUtil.cloneRow(cell)) //获取这一列的rowkey,转换为字符串类型
                        +"\t"+
                        Bytes.toString(CellUtil.cloneFamily(cell)) //获取这一列的列族,转换为字符串类型
                        +"\t"+
                        Bytes.toString(CellUtil.cloneQualifier(cell)) //获取这一列的名称,转换为字符串类型
                        +"\t"+
                        Bytes.toString(CellUtil.cloneValue(cell)) //获取这一列的值,转换为字符串类型
                        +"\t"+
                        cell.getTimestamp() //获取时间戳
                );
            }
    
        }
    
  • 总结

    • step1:先构建Get
    • step2:执行Get,得到返回值
    • Result:一个Result就是一个Rowkey的数据,包含一个Cell数组
      • Cell:一个Cell就是一列的数据

Java API:DML:Delete

  • 需求:JavaAPI实现从Hbase表删除某个Rowkey的某列的数据

  • 分析

    • step1:Hbase中实现delete操作,需要构建Delete对象

    • step2:为Delete对象添加列族、列名

    • step3:对表执行delete操作

      table.delete(Delete)
      
  • 实现

    /**
         * 删除某个Rowkey的某一列的数据:delete  表名     rowkey      列族:列
         * @param table
         */
        private void deleteData(Table table) throws IOException {
            //step1:构建删除对象
            Delete delete = new Delete(Bytes.toBytes("20201001_888"));
            //step2:添加删除配置
    //        delete.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("name")); //只删除最新版本
            delete.addColumns(Bytes.toBytes("basic"),Bytes.toBytes("name")); //删除所有版本
            //step3:执行删除
            table.delete(delete);
        }
    
  • 总结

    • step1:构建Delete对象
    • step2:根据需求调用配置方法:添加列族、列
    • step3:表对象指定delete对象即可

Java API:DML:Scan

  • 需求:JavaAPI实现从Hbase表中读取所有数据

    scan 表
    scan 表 + filter
    
  • 分析

    • step1:Hbase中实现scan操作,需要构建Scan对象
    • step2:对表执行scan操作
    • step3:获取返回值,取出每个Rowkey的数据,打印输出
  • 实现

    /**
         * 通过Scan,进行Hbase表的数据查询:scan 表
         * @param table
         */
        private void scanData(Table table) throws IOException {
            //step1:构建Scan对象
            Scan scan = new Scan();
            //step2:执行scan:返回值是ResultScanner,包含了多个Rowkey的数据
            ResultScanner rsScan = table.getScanner(scan);
            /**
             *      ResultScanner:包含多个Rowkey的数据,包含了多个Result对象:Iterator<Result>
             *      Result:一个Rowkey的数据,包含了这个Rowkey多列的数据:Cell[]
             *      Cell :一列的数据
             */
            //step3:打印数据
            //先获取每个rowkey
            for(Result rs:rsScan){
                //直接输出当前rowkey的值
                System.out.println(Bytes.toString(rs.getRow()));
                //再获取每个rowkey中的每一列
                for(Cell cell : rs.rawCells()){
                    //输出每一列Cell对象中的数据:20201001_888  column=basic:age, timestamp=1616029665232, value=20
                    System.out.println(
                            Bytes.toString(CellUtil.cloneRow(cell)) //获取这一列的rowkey,转换为字符串类型
                                    +"\t"+
                                    Bytes.toString(CellUtil.cloneFamily(cell)) //获取这一列的列族,转换为字符串类型
                                    +"\t"+
                                    Bytes.toString(CellUtil.cloneQualifier(cell)) //获取这一列的名称,转换为字符串类型
                                    +"\t"+
                                    Bytes.toString(CellUtil.cloneValue(cell)) //获取这一列的值,转换为字符串类型
                                    +"\t"+
                                    cell.getTimestamp() //获取时间戳
                    );
                }
                System.out.println("----------------------------------------------------------------------");
            }
        }
    
  • 总结

    • step1:先构建Scan对象
    • step2:执行Scan操作
    • step3:返回值
      • ResultScanner:包含多个Rowkey的数据的集合
        • Iter《Result》
      • Result:一个Rowkey的数据
        • Cell[]
      • Cell:一列的数据

Java API:DML:Filter

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

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

    • Rowkey的设计:时间【年月日】+id

      • Rowkey整体有序
      • 前缀匹配
    • 需求1

      • StartRow = 》202101
        • 包含
      • StopRow = 》202103
        • 不包含
    • 需求2

      • 方案一:使用startrow = 2021,stoprow = 2022

      • 方案二:使用Filter:PrefixFilter,Rowkey的前缀过滤器

在这里插入图片描述

  - 构建过滤器,指定Rowkey前缀
  - Scan加载过滤器,返回所有符合rowkey前缀的过滤器
  • 需求三:查询所有age = 20的数据

    where age = 20
    
    • Filter:列值过滤器
      • SingleColumnValueFilter:对某一列的值进行过滤
    • 指定列值过滤器:哪个列族下的哪一列的值等于什么值
    • scan加载过滤器
  • 需求4:查询所有数据的name和age这两列

    select name ,age from table
    
    • Filter:多列前缀过滤器
      • MutipleColumnPrefixFIlter:用于将每条数据中指定的列过滤出来
    • 指定需要过滤哪些列
    • scan加载过滤器即可
  • 需求5:查询所有年age = 20的人的name和age

    select name,age from table where age = 20
    
    • 支持组合过滤器:FilterList
      • 列的过滤:MutipleColumnPrefixFIlter
      • 列值过滤:SingleColumnValueFilter
    • 构建两个过滤器,将两个过滤器组合放入过滤器集合
    • Scan加载过滤器集合
  • 实现

    • 批量写入数据到Hbase

      /**
           * 批量写入数据到Hbase
           * @param table
           */
          private void putListData(Table table) throws IOException {
              //step1:构建Put对象,一个Put对象用于表示一个Rowkey写入的数据
              Put put1 = new Put(Bytes.toBytes("20210101_001"));
              Put put2 = new Put(Bytes.toBytes("20210201_002"));
              Put put3 = new Put(Bytes.toBytes("20210301_003"));
      
              //step2:为Put添加这个Rowkey的每一列
              put1.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("name"),Bytes.toBytes("laoda"));
              put1.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("age"),Bytes.toBytes("18"));
              put1.addColumn(Bytes.toBytes("other"),Bytes.toBytes("phone"),Bytes.toBytes("110"));
              put1.addColumn(Bytes.toBytes("other"),Bytes.toBytes("addr"),Bytes.toBytes("shanghai"));
      
              put2.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("name"),Bytes.toBytes("laoer"));
              put2.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("age"),Bytes.toBytes("20"));
              put2.addColumn(Bytes.toBytes("other"),Bytes.toBytes("phone"),Bytes.toBytes("120"));
      
              put3.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("name"),Bytes.toBytes("laosan"));
              put3.addColumn(Bytes.toBytes("basic"),Bytes.toBytes("age"),Bytes.toBytes("22"));
              put3.addColumn(Bytes.toBytes("other"),Bytes.toBytes("addr"),Bytes.toBytes("beijing"));
      
              //step3:将多个Put封装到List中
              List<Put> puts = new ArrayList<Put>();
              puts.add(put1);
              puts.add(put2);
              puts.add(put3);
      
              //step4:执行PutList
              table.put(puts);
          }
      

    在这里插入图片描述

    • 需求一:实现范围过滤

      //需求一:查询2021年1月和2月的数据
      scan.withStartRow(Bytes.toBytes("202101"));
      scan.withStopRow(Bytes.toBytes("202103"));
      

    在这里插入图片描述

    • 需求二实现

      //需求2:查询2021年的所有数据
      Filter prefixFiter = new PrefixFilter(Bytes.toBytes("2021"));
      
      //将过滤器加载到scan中
      scan.setFilter(prefixFiter);
      
    • 需求三实现

      //需求三:查询所有age = 20的数据
              /**
               *    * @param family name of column family
               *    * @param qualifier name of column qualifier
               *    * @param op operator
               *    * @param value value to compare column values against
               */
              Filter valueFilter = new SingleColumnValueFilter(
                      Bytes.toBytes("basic"),//指定列族
                      Bytes.toBytes("age"),//指定列
                      CompareOperator.EQUAL,//指定比较器类型
                      Bytes.toBytes("20")  //比较的值
              );
              //将过滤器加载到scan中
              scan.setFilter(valueFilter);
      

在这里插入图片描述

  • 需求四实现

    //需求4:查询所有数据的name和age这两列
            //构建所有需要过滤的列
            byte[][] prefixes = {
                Bytes.toBytes("name"),
                Bytes.toBytes("age")
            };
            Filter columnFilter = new MultipleColumnPrefixFilter(prefixes);
    
            //将过滤器加载到scan中
            scan.setFilter(columnFilter);
    

    在这里插入图片描述

  • 需求五实现

    Filter valueFilter = new SingleColumnValueFilter(
                    Bytes.toBytes("basic"),//指定列族
                    Bytes.toBytes("age"),//指定列
                    CompareOperator.EQUAL,//指定比较器类型
                    Bytes.toBytes("20")  //比较的值
            );
    
            //需求4:查询所有数据的name和age这两列
            //构建所有需要过滤的列
            byte[][] prefixes = {
                Bytes.toBytes("name"),
                Bytes.toBytes("age")
            };
            Filter columnFilter = new MultipleColumnPrefixFilter(prefixes);
            //需求5:查询所有age = 20的人的name和age
            //构建FIlterList
            FilterList lists = new FilterList();//MUST_PASS_ALL:and,MUST_PASS_ONE:or
            //添加过滤器
            lists.addFilter(valueFilter);
            lists.addFilter(columnFilter);
            //将过滤器加载到scan中
            scan.setFilter(lists);
    

在这里插入图片描述

  • 总结

    • Rowkey范围过滤:StartRow和StopRow
    • Rowkey前缀过滤:PrefixFilter
    • Rowkey中列的值的过滤:SingleColumnValueFilter
    • Rowkey中列的过滤:MultipleColumnPrefixFilter
    • 多种条件的组合过滤:FilterList
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值