Hbase DML、DDL命令 JavaAPI
Hbase理论
-
Hbase的介绍
- 功能:分布式NoSQL列存储数据库,用于实现大数据高性能的实时随机的数据存储
- 应用场景:所有实时数据存储的场景
- 特点
- 分布式内存 + 分布式磁盘:大数据存储
- 基于内存的列存储:高性能数据的存储
- MySQL:只能满足小数据量的存储
- HDFS:纯离线的文件系统
- Hbase虽然基于HDFS,但是整体是一个实时的技术
- Hbase上层是分布式内存,底层才是HDFS
- Hbase将数据存储在HDFS上,也做了性能优化
- 列族
- 按列存储
- 构建有序
- Hive:Hive本质还是HDFS,只是对HDFS封装了表的结构接口
- Hive功能:构建离线数据仓库:直接使用HDFS存储
- Hbase功能:用于解决实时数据存储:内存+优化HDFS存储
- 设计思想:怎么实现?
- 大数据:分布式的设计
- 高性能:基于内存
- 核心:冷热数据分离
- 刚产生的数据,被读的概率最大,称为热数据,将热数据写入分布式内存
- 产生很久的数据,被读的概率比较小,就是冷数据,将冷数据存储在磁盘中【HDFS】
-
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:每一列都属于某一个列族,相同列族中列会存储在一起
- column family :列族,对列的分组,将所有列划分不同的组中,为了提高性能
-
支持多版本:一列中可以存储多个版本的值,通过时间戳来区分,列族级别的属性
-
-
Hbase架构组成
- 架构中角色
- Hbase:主从架构
- HMaster:管理节点
- HRegionServer:存储结构,用于构建分布式内存
- HDFS:分布式磁盘
- Zookeeper:存储元数据、辅助选举
- Hbase:主从架构
- 集群搭建【搭建成功即可】
- 架构中角色
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
- SQL
-
语法
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:一列的数据
- ResultScanner:包含多个Rowkey的数据的集合
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
- 不包含
- StartRow = 》202101
-
需求2
-
方案一:使用startrow = 2021,stoprow = 2022
-
方案二:使用Filter:PrefixFilter,Rowkey的前缀过滤器
-
-
- 构建过滤器,指定Rowkey前缀
- Scan加载过滤器,返回所有符合rowkey前缀的过滤器
-
需求三:查询所有age = 20的数据
where age = 20
- Filter:列值过滤器
- SingleColumnValueFilter:对某一列的值进行过滤
- 指定列值过滤器:哪个列族下的哪一列的值等于什么值
- scan加载过滤器
- Filter:列值过滤器
-
需求4:查询所有数据的name和age这两列
select name ,age from table
- Filter:多列前缀过滤器
- MutipleColumnPrefixFIlter:用于将每条数据中指定的列过滤出来
- 指定需要过滤哪些列
- scan加载过滤器即可
- Filter:多列前缀过滤器
-
需求5:查询所有年age = 20的人的name和age
select name,age from table where age = 20
- 支持组合过滤器:FilterList
- 列的过滤:MutipleColumnPrefixFIlter
- 列值过滤:SingleColumnValueFilter
- 构建两个过滤器,将两个过滤器组合放入过滤器集合
- Scan加载过滤器集合
- 支持组合过滤器:FilterList
-
实现
-
批量写入数据到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