HBase入门至进阶以及开发等知识梳理
HBase入门至进阶以及开发等知识梳理
HBase简介
hadoop简介
Hadoop是一个由Apache基金会所开发的分布式系统基础架构。用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。Hadoop实现了一个分布式文件系统( Distributed File System),其中一个组件是HDFS(Hadoop Distributed File System)。HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,而MapReduce则为海量的数据提供了计算 。
从 1970 年开始,大多数的公司数据存储和维护使用的是关系型数据库
大数据技术出现后,很多拥有海量数据的公司开始选择像Hadoop的方式来存储海量数据
hadoop局限性
- Hadoop主要是实现批量数据的处理,并且通过顺序方式访问数据
- 要查找数据必须搜索整个数据集, 如果要进行随机读取数据,效率较低
HBase与NoSQL
NoSQL是一个通用术语,泛指一个数据库并不是使用SQL作为主要语言的非关系型数据库
HBase是BigTable的开源java版本。是建立在HDFS之上,提供高可靠性、高性能、列存储、可伸缩、实时读写NoSQL的数据库系统
HBase仅能通过主键(row key)和主键的range来检索数据,仅支持单行事务
主要用来存储结构化和半结构化的松散数据
Hbase查询数据功能很简单,不支持join等复杂操作,不支持复杂的事务(行级的事务),从技术上来说,HBase更像是一个「数据存储」而不是「数据库」,因为HBase缺少RDBMS中的许多特性,例如带类型的列、二级索引以及高级查询语言等
Hbase中支持的数据类型:byte[]
与Hadoop一样,Hbase目标主要依靠横向扩展,通过不断增加廉价的商用服务器,来增加存储和处理能力,例如,把集群从10个节点扩展到20个节点,存储能力和处理能力都会加倍
HBase中的表一般有这样的特点
大:一个表可以有上十亿行,上百万列
面向列:面向列(族)的存储和权限控制,列(族)独立检索
稀疏:对于为空(null)的列,并不占用存储空间,因此,表可以设计的非常稀疏
HBase应用场景
对象存储
- 不少的头条类、新闻类的的新闻、网页、图片存储在HBase之中,一些病毒公司的病毒库也是存储在HBase之中
时序数据
- HBase之上有OpenTSDB模块,可以满足时序类场景的需求
推荐画像
- 用户画像,是一个比较大的稀疏矩阵,蚂蚁金服的风控就是构建在HBase之上
时空数据
- 主要是轨迹、气象网格之类,滴滴打车的轨迹数据主要存在HBase之中,另外在技术所有大一点的数据量的车联网企业,数据都是存在HBase之中
CubeDB OLAP
- Kylin一个cube分析工具,底层的数据就是存储在HBase之中,不少客户自己基于离线计算构建cube存储在hbase之中,满足在线报表查询的需求
消息/订单
- 在电信领域、银行领域,不少的订单查询底层的存储,另外不少通信、消息同步的应用构建在HBase之上
Feeds流
- 典型的应用就是xx朋友圈类似的应用,用户可以随时发布新内容,评论、点赞。
NewSQL
- 之上有Phoenix的插件,可以满足二级索引、SQL的需求,对接传统数据需要SQL非事务的需求
其他
- 存储爬虫数据
- 海量数据备份
- 短网址
- …
发展历程
年份 | 重大事件 |
---|---|
2006年11月 | Google发布BigTable论文. |
2007年10月 | 发布第一个可用的HBase版本,基于Hadoop 0.15.0 |
2008年1月 | HBase称为Hadoop的一个子项目 |
2010年5月 | HBase称为Apache的顶级项目 |
HBase特点
- 强一致性读/写
- HBASE不是“最终一致的”数据存储
- 它非常适合于诸如高速计数器聚合等任务
- 自动分块
- HBase表通过Region分布在集群上,随着数据的增长,区域被自动拆分和重新分布
- 自动RegionServer故障转移
- Hadoop/HDFS集成
- HBase支持HDFS开箱即用作为其分布式文件系统
- MapReduce
- HBase通过MapReduce支持大规模并行处理,将HBase用作源和接收器
- Java Client API
- HBase支持易于使用的 Java API 进行编程访问
- Thrift/REST API
- 块缓存和布隆过滤器
- HBase支持块Cache和Bloom过滤器进行大容量查询优化
- 运行管理
- HBase为业务洞察和JMX度量提供内置网页。
RDBMS与HBase的对比
关系型数据库
关系型数据库-结构
- 数据库已表的形式存在
- 支持FAT、NTFS、EXT、文件系统
- 使用主键(pk)
- 通过外部中间件可以支持分库分表,但底层还是单机引擎
- 使用行、列、单元格
关系型数据库-功能
- 支持向上扩展(购买更好的硬件配置)
- 使用SQL查询
- 面向行,即每一行都是一个连续单元
- 数据总量依赖于服务器配置
- 具有ACID支持
- 适合结构化数据
- 传统关系型数据库一般都是中心化的
- 支持事务
- 支持join
HBase
HBase-结构
- 以表形式存在
- 支持HDFS文件系统
- 使用行键(row key)
- 原生支持分布式存储、计算引擎
- 使用行、列、列簇和单元格
HBase-功能
- 支持向外扩展
- 使用API和MapReduce、Spark、Flink来访问HBase表数据
- 面向列簇,即每一个列簇都是一个连续的单元
- 数据总量不依赖具体某台机器,而取决于机器数量
- HBase不支持ACID(Atomicity、Consistency、Isolation、Durability)
- 适合结构化数据和非结构化数据
- 一般都是分布式
- l HBase不支持事务,支持的是单行数据的事务操作
- 不支持join
HDFS与HBase
HDFS
- HDFS是一个非常适合存储大型文件的分布式文件系统
- HDFS它不是一个通用的文件系统,也无法在文件中快速查询某个数据
HBase
- HBase构建在HDFS之上,并为大型表提供快速记录查找(和更新)
- HBase内部将大量数据放在HDFS中名为「StoreFiles」的索引中,以便进行高速查找
- Hbase比较适合做快速查询等需求,而不适合做大规模的OLAP应用
Hive与HBase
Hive和Hbase是两种基于Hadoop的不同技术
Hive是一种类SQL的引擎,并且运行MapReduce任务
Hbase是一种在Hadoop之上的NoSQL 的Key/value数据库
这两种工具是可以同时使用的。就像用Google来搜索,用FaceBook进行社交一样,Hive可以用来进行统计查询,而HBase可以用来进行实时查询,数据也可以从Hive写到HBase,或者从HBase写回Hive
Hive
- 数据仓库分析工具
- Hive的本质其实就相当于将HDFS中已经存储的文件在Mysql中做了一个双射关系,以方便使用HQL去管理查询
- 用于数据分析、清洗
- Hive适用于离线的数据分析和清洗,延迟较高
- 基于HDFS、MapReduce
- Hive存储的数据依旧在DataNode上,编写的HQL语句终将是转换为MapReduce代码执行
HBase
- NoSQL数据库
- 是一种面向列存储的非关系型数据库
- 用于存储结构化和非结构化的数据
- 适用于单表非关系型数据的存储,不适合做关联查询,类似join等操作
- 基于HDFS
- 数据持久化存储的体现形式是Hfile,存放于DataNode中,被ResionServer以region的形式进行管理
- 延迟较低,接入在线业务使用
- 面对大量的企业数据,HBase可以直线单表大量数据的存储,同时提供了高效的数据访问速度
HBase集群搭建部署
【注意】
HBase的集群安装部署写在另外一篇文章中:https://blog.csdn.net/wt334502157/article/details/116837187
由于篇幅原因,将部署内容单独写在部署文章中。
HBase数据模型
结构简介
在HBASE中,数据存储在具有行和列的表中。这是看起来关系数据库(RDBMS)一样,但将HBASE表看成是多个维度的Map结构更容易理解
ROWKEY | C1列蔟 | C2列蔟 |
---|---|---|
rowkey | 列1 | 列2 | 列3 | 列4 | 列5 | 列6 |
rowkey | 0001 |
---|---|
C1(Map) | 列1 => 值1 列2 => 值2 列3 => 值3 |
C2(Map) | 列4 => 值4 列5 => 值5 列6 => 值6 |
HBase常用术语
表(Table)
- HBase中数据都是以表形式来组织的
- HBase中的表由多个行组成
列(row)
- HBase中的行由一个rowkey(行键)和一个或多个列组成,列的值与rowkey、列相关联
- 行在存储时按行键按字典顺序排序
- 行键的设计非常重要,尽量让相关的行存储在一起
- 例如:存储网站域。如行键是域,则应该将域名反转后存储(org.apache.www、org.apache.mail、org.apache.jira)。这样,所有Apache域都在表中存储在一起,而不是根据子域的第一个字母展开
列(Column)
- HBASE中的列由列蔟(Column Family)和列限定符(Column Qualifier)组成
- 例如 : [ 列蔟名:列限定符名]。C1:USER_ID、C1:NAME
列簇(Column Family)
- 出于性能原因,列簇将一组列及其值组织在一起
- 每个列簇都有一组存储属性
- 是否应该缓存在内容中
- 数据如何被压缩或行键如何编码等
- 表中的每一行都有相同的列蔟,但在列蔟中不存储任何内容
- 所有的列蔟的数据全部都存储在一块(文件系统HDFS)
- HBase官方建议所有的列蔟保持一样的列,并且将同一类的列放在一个列蔟中
列标识符(Column Qualifier)
- 列蔟中包含一个个的列限定符,这样可以为存储的数据提供索引
- 列蔟在创建表的时候是固定的,但列限定符是不作限制的
- 不同的行可能会存在不同的列标识符
单元格(Cell)
- 单元格是行、列系列和列限定符的组合
- 包含一个值和一个时间戳(表示该值的版本)
- 单元格中的内容是以二进制存储的
ROW | COLUMN+CELL |
---|---|
1250995 | column=C1:ADDRESS, timestamp=1588591604729, value=\xC9\xBD\xCE\xF7\xCA |
1250995 | column=C1:LATEST_DATE, timestamp=1588591604729, value=2019-03-28 |
1250995 | column=C1:NAME, timestamp=1588591604729, value=\xB7\xBD\xBA\xC6\xD0\xF9 |
1250995 | column=C1:NUM_CURRENT, timestamp=1588591604729, value=398.5 |
1250995 | column=C1:NUM_PREVIOUS, timestamp=1588591604729, value=379.5 |
1250995 | column=C1:NUM_USEAGE, timestamp=1588591604729, value=19 |
1250995 | column=C1:PAY_DATE, timestamp=1588591604729, value=2019-02-26 |
1250995 | column=C1:RECORD_DATE, timestamp=1588591604729, value=2019-02-11 |
1250995 | column=C1:SEX, timestamp=1588591604729, value=\xC5\xAE |
1250995 | column=C1:TOTAL_MONEY, timestamp=1588591604729, value=114 |
概念模型
Row Key | Time Stamp | ColumnFamily contents | ColumnFamily anchor | ColumnFamily people |
---|---|---|---|---|
“com.cnn.www” | t9 | anchor:cnnsi.com = “CNN” | ||
“com.cnn.www” | t8 | anchor:my.look.ca = “CNN.com” | ||
“com.cnn.www” | t6 | contents:html = “…” | ||
“com.cnn.www” | t5 | contents:html = “…” | ||
“com.cnn.www” | t3 | contents:html = “…” | ||
“com.example.www” | t5 | contents:html = “…” | people:author = “John Doe” |
- 上述表格有两行、三个列蔟(contens、ancho、people)
- “com.cnn.www”这一行anchor列蔟两个列(anchor:cssnsi.com、anchor:my.look.ca)、contents列蔟有个1个列(html)
- “com.cnn.www”在HBase中有 t3、t5、t6、t8、t9 5个版本的数据
- HBase中如果某一行的列被更新的,那么最新的数据会排在最前面,换句话说同一个rowkey的数据是按照倒序排序的
常用shell操作
HBase操作流程
从简单的需求实现来慢慢引出一系列对hbase的操作实施
场景需求:有以下订单数据,我们想要将这样的一些数据保存到HBase中
订单ID | 订单状态 | 支付金额 | 支付方式ID | 用户ID | 操作时间 | 商品分类 |
---|---|---|---|---|---|---|
001 | 已付款 | 200.5 | 1 | 001 | 2020-5-2 18:08:53 | 手机; |
接下来,我们将使用HBase shell来进行以下操作:
创建表
添加数据
更新数据
删除数据
查询数据
创建表
在HBase中,所有的数据也都是保存在表中的。要将订单数据保存到HBase中,首先需要将表创建出来
启动HBase-shell
启动HBase shell:hbase shell
wangting@ops01:/home/wangting >hbase shell
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
HBase Shell
Use "help" to get list of supported commands.
Use "exit" to quit this interactive shell.
For Reference, please visit: http://hbase.apache.org/2.0/book.html#shell
Version 2.0.5, r76458dd074df17520ad451ded198cd832138e929, Mon Mar 18 00:41:49 UTC 2019
Took 0.0043 seconds hbase(main):001:0>
创建表
语法:create ‘表名’,‘列蔟名’…
创建订单表,表名为ORDER_INFO,该表有一个列蔟为C1
hbase(main):036:0> create 'ORDER_INFO','C1'
Created table ORDER_INFO
Took 0.7355 seconds
=> Hbase::Table - ORDER_INFO
hbase(main):037:0>
【注意】:
- create要小写
- 一个表可以包含若干个列蔟
- 命令解析:调用hbase提供的ruby脚本的create方法,传递两个字符串参数
查看表
hbase(main):045:0> list
TABLE
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
7 row(s)
Took 0.0098 seconds
=> ["ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS"]
hbase(main):046:0>
删除表
要删除某个表,必须要先禁用表
禁用表
语法:disable “表名”
删除表
语法:drop “表名”
删除ORDER_INFO表
hbase(main):046:0> disable "ORDER_INFO"
Took 0.4547 seconds
hbase(main):047:0> drop "ORDER_INFO"
Took 0.4385 seconds
hbase(main):048:0> list
TABLE
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
6 row(s)
Took 0.0052 seconds
=> ["SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS"]
添加数据
接下来,我们需要往订单表中添加以下数据
订单ID | 订单状态 | 支付金额 | 支付方式ID | 用户ID | 操作时间 | 商品分类 |
---|---|---|---|---|---|---|
ID | STATUS | PAY_MONEY | PAYWAY | USER_ID | OPERATION_DATE | CATEGORY |
000001 | 已提交 | 4070 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机; |
使用put操作
HBase中的put命令,可以用来将数据保存到表中。但put一次只能保存一个列的值。以下是put的语法结构:
put ‘表名’,‘ROWKEY’,‘列蔟名:列名’,‘值’
hbase(main):049:0> create 'ORDER_INFO','C1'
Created table ORDER_INFO
Took 1.2423 seconds
=> Hbase::Table - ORDER_INFO
hbase(main):050:0> put 'ORDER_INFO','000001','C1:ID','000001'
Took 0.1005 seconds
hbase(main):051:0> put 'ORDER_INFO','000001','C1:STATUS','已提交'
Took 0.0044 seconds
hbase(main):052:0> put 'ORDER_INFO','000001','C1:PAY_MONEY',4070
Took 0.0045 seconds
hbase(main):053:0> put 'ORDER_INFO','000001','C1:PAYWAY',1
Took 0.0042 seconds
hbase(main):054:0> put 'ORDER_INFO','000001','C1:USER_ID',4944191
Took 0.0045 seconds
hbase(main):055:0> put 'ORDER_INFO','000001','C1:OPERATION_DATE','2020-04-25 12:09:16'
Took 0.0040 seconds
hbase(main):056:0> put 'ORDER_INFO','000001','C1:CATEGORY','手机;'
Took 0.0053 seconds
hbase(main):057:0>
查看添加的数据
要求将rowkey为:000001对应的数据查询出来
使用get操作
在HBase中,可以使用get命令来获取单独的一行数据。语法:get ‘表名’,‘rowkey’
hbase(main):057:0> get 'ORDER_INFO','000001'
COLUMN CELLC1:CATEGORY timestamp=1660558718961, value=\xE6\x89\x8B\xE6\x9C\xBA;C1:ID timestamp=1660558716878, value=000001C1:OPERATION_DATE timestamp=1660558716999, value=2020-04-25 12:09:16C1:PAYWAY timestamp=1660558716958, value=1C1:PAY_MONEY timestamp=1660558716936, value=4070C1:STATUS timestamp=1660558716907, value=\xE5\xB7\xB2\xE6\x8F\x90\xE4\xBA\xA4C1:USER_ID timestamp=1660558716978, value=4944191
1 row(s)
Took 0.0520 seconds
hbase(main):058:0>
查看数据value显示中文
在HBase shell中,如果在数据中出现了一些中文,默认HBase shell中显示出来的是十六进制编码。要想将这些编码显示为中文,我们需要在get命令后添加一个属性:{FORMATTER => ‘toString’}
格式为:get ‘ORDER_INFO’,‘000001’, {FORMATTER => ‘toString’}
- { key => value},这个是Ruby语法,表示定义一个HASH结构
- get是一个HBase Ruby方法,’ORDER_INFO’、’000001’、{FORMATTER => ‘toString’}是put方法的三个参数
- FORMATTER要使用大写
- 在Ruby中用{}表示一个字典,类似于hashtable,FORMATTER表示key、’toString’表示值
hbase(main):058:0> get 'ORDER_INFO','000001', {FORMATTER => 'toString'}
COLUMN CELLC1:CATEGORY timestamp=1660558718961, value=手机;C1:ID timestamp=1660558716878, value=000001C1:OPERATION_DATE timestamp=1660558716999, value=2020-04-25 12:09:16C1:PAYWAY timestamp=1660558716958, value=1C1:PAY_MONEY timestamp=1660558716936, value=4070C1:STATUS timestamp=1660558716907, value=已提交C1:USER_ID timestamp=1660558716978, value=4944191
1 row(s)
Took 0.0222 seconds
hbase(main):059:0>
更新操作
将订单ID为000001的状态,更改为「已付款」
使用put操作
同样,在HBase中,也是使用put命令来进行数据的更新,语法与之前的添加数据一模一样
hbase(main):060:0> get 'ORDER_INFO','000001', {FORMATTER => 'toString'}
COLUMN CELL C1:CATEGORY timestamp=1660558718961, value=手机; C1:ID timestamp=1660558716878, value=000001 C1:OPERATION_DATE timestamp=1660558716999, value=2020-04-25 12:09:16 C1:PAYWAY timestamp=1660558716958, value=1 C1:PAY_MONEY timestamp=1660558716936, value=4070 C1:STATUS timestamp=1660558716907, value=已提交 C1:USER_ID timestamp=1660558716978, value=4944191
1 row(s)
Took 0.0258 seconds
hbase(main):061:0> put 'ORDER_INFO','000001','C1:STATUS','已付款'
Took 0.0051 seconds
hbase(main):062:0> get 'ORDER_INFO','000001', {FORMATTER => 'toString'}
COLUMN CELL C1:CATEGORY timestamp=1660558718961, value=手机; C1:ID timestamp=1660558716878, value=000001 C1:OPERATION_DATE timestamp=1660558716999, value=2020-04-25 12:09:16 C1:PAYWAY timestamp=1660558716958, value=1 C1:PAY_MONEY timestamp=1660558716936, value=4070 C1:STATUS timestamp=1660615875423, value=已付款 C1:USER_ID timestamp=1660558716978, value=4944191
1 row(s)
Took 0.0143 seconds
hbase(main):063:0>
- HBase中会自动维护数据的版本
- 每次put操作的执行,都会重新生成新的时间戳
删除操作
删除状态列数据
将订单ID为000001的状态STSTUS列删除
使用delete操作
在HBase中,可以使用delete命令来将一个单元格的数据删除
语法格式:delete ‘表名’, ‘rowkey’, ‘列蔟:列’
注意:此处HBase默认会保存多个时间戳的版本数据,所以这里的delete删除的是最新版本的列数据
hbase(main):068:0> delete 'ORDER_INFO', '000001', 'C1:STATUS'
Took 0.0136 seconds
删除整行数据
将订单ID为000001的信息全部删除(删除所有的列)
使用deleteall操作
deleteall命令可以将指定rowkey对应的所有列全部删除
语法格式:deleteall ‘表名’,‘rowkey’
hbase(main):070:0> deleteall 'ORDER_INFO','000001'
Took 0.0091 seconds
清空表
将ORDER_INFO的数据全部删除
使用truncate操作
truncate命令用来清空某个表中的所有数据
语法格式:truncate “表名”
hbase(main):071:0> truncate 'ORDER_INFO'
Truncating 'ORDER_INFO' table (it may take a while):
Disabling table...
Truncating table...
Took 1.5176 seconds
导入测试数据文件
需求:ORDER_INFO.txt 中,有一份这样的HBase数据集,我们需要将这些指令放到HBase中执行,将数据导入到HBase中
资料下载:wget http://osswangting.oss-cn-shanghai.aliyuncs.com/hbase/ORDER_INFO.txt
注意是linux命令行操作,并非进入到交互界面后
wangting@ops01:/home/wangting/hbase >ll
total 52
-rw-r--r-- 1 wangting wangting 49366 Aug 5 16:34 ORDER_INFO.txt
wangting@ops01:/home/wangting/hbase >
wangting@ops01:/home/wangting/hbase >pwd
/home/wangting/hbase
wangting@ops01:/home/wangting/hbase >
wangting@ops01:/home/wangting/hbase >hbase shell /home/wangting/hbase/ORDER_INFO.txt
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html multiple_bindings for an explanation.
Took 0.5581 seconds Took 0.0072 seconds Took 0.0047 seconds Took 0.0046 seconds Took 0.0064 seconds
...
...
计数操作
查看HBase中的ORDER_INFO表,一共有多少条记录
使用count操作
hbase(main):074:0> count 'ORDER_INFO'
66 row(s)
Took 0.0570 seconds => 66
hbase(main):075:0>
注意:这个操作是比较耗时的。在数据量大的这个命令可能会运行很久,本次导入数据量非常少
大量数据的计数统计方式
当HBase中数据量大时,可以使用HBase中提供的MapReduce程序来进行计数统计
语法如下:$HBASE_HOME/bin/hbase org.apache.hadoop.hbase.mapreduce.RowCounter ‘表名’
wangting@ops01:/home/wangting/hbase >$HBASE_HOME/bin/hbase org.apache.hadoop.hbase.mapreduce.RowCounter 'ORDER_INFO'
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
2022-08-16 10:51:15,013 INFO [main] client.RMProxy: Connecting to ResourceManager at ops02/11.8.36.63:8032
2022-08-16 10:51:17,747 INFO [main] zookeeper.ReadOnlyZKClient: Connect 0x0e27ba81 to
...
...HBase CountersBYTES_IN_REMOTE_RESULTS=5616BYTES_IN_RESULTS=5616MILLIS_BETWEEN_NEXTS=591NOT_SERVING_REGION_EXCEPTION=0NUM_SCANNER_RESTARTS=0NUM_SCAN_RESULTS_STALE=0REGIONS_SCANNED=1REMOTE_RPC_CALLS=1REMOTE_RPC_RETRIES=0ROWS_FILTERED=0ROWS_SCANNED=66RPC_CALLS=1RPC_RETRIES=0org.apache.hadoop.hbase.mapreduce.RowCounter$RowCounterMapper$CountersROWS=66File Input Format Counters Bytes Read=0File Output Format Counters Bytes Written=0
wangting@ops01:/home/wangting/hbase >
最终可以看到ROWS=66的输出信息
扫描操作
查看ORDER_INFO表中所有的数据
使用scan操作
在HBase,我们可以使用scan命令来扫描HBase中的表。语法:scan ‘表名’
hbase(main):075:0> scan 'ORDER_INFO',{FORMATTER => 'toString'}
ROW COLUMN+CELL02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:CATEGORY, timestamp=1660617813649, value=手机;02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:OPERATION_DATE, timestamp=1660617813327, value=2020-04-25 12:09:1602602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAYWAY, timestamp=1660617812628, value=102602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAY_MONEY, timestamp=1660617812242, value=407002602f66-adc7-40d4-8485-76b5632b5b53 column=C1:STATUS, timestamp=1660617811677, value=已提交02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:USER_ID, timestamp=1660617812988, value=49441910968a418-f2bc-49b4-b9a9-2157cf214cfd column=C1:CATEGORY, timestamp=1660617813651, value=家用电器;电脑;........
注意:尽量避免scan一张大表
查询订单数据(只显示3条)
hbase(main):076:0> scan 'ORDER_INFO', {LIMIT => 3, FORMATTER => 'toString'}
只查询订单状态以及支付方式,并且只展示3条数据
hbase(main):077:0> scan 'ORDER_INFO', {LIMIT => 3, COLUMNS => ['C1:STATUS', 'C1:PAYWAY'], FORMATTER => 'toString'}
ROW COLUMN+CELL02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAYWAY, timestamp=1660617812628, value=102602f66-adc7-40d4-8485-76b5632b5b53 column=C1:STATUS, timestamp=1660617811677, value=已提交0968a418-f2bc-49b4-b9a9-2157cf214cfd column=C1:PAYWAY, timestamp=1660617812632, value=10968a418-f2bc-49b4-b9a9-2157cf214cfd column=C1:STATUS, timestamp=1660617811735, value=已完成0e01edba-5e55-425e-837a-7efb91c56630 column=C1:PAYWAY, timestamp=1660617812639, value=30e01edba-5e55-425e-837a-7efb91c56630 column=C1:STATUS, timestamp=1660617811748, value=已付款
3 row(s)
Took 0.0127 seconds
查询指定订单ID的数据并以中文展示
根据ROWKEY来查询对应的数据,ROWKEY为02602f66-adc7-40d4-8485-76b5632b5b53,只查询订单状态、支付方式,并以中文展示
要查询指定ROWKEY的数据,需要使用ROWPREFIXFILTER
语法格式:scan ‘表名’, {ROWPREFIXFILTER => ‘rowkey’}
hbase(main):078:0> scan 'ORDER_INFO', {ROWPREFIXFILTER => '02602f66-adc7-40d4-8485-76b5632b5b53', COLUMNS => ['C1:STATUS', 'C1:PAYWAY'], FORMATTER => 'toString'}
ROW COLUMN+CELL02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAYWAY, timestamp=1660617812628, value=102602f66-adc7-40d4-8485-76b5632b5b53 column=C1:STATUS, timestamp=1660617811677, value=已提交
1 row(s)
Took 0.0103 seconds
hbase(main):079:0>
过滤器
在HBase中,如果要对海量的数据来进行查询,此时基本的操作是比较无力的。需要借助HBase中的高级语法——Filter来进行查询。Filter可以根据列簇、列、版本等条件来对数据进行过滤查询。因为在HBase中,主键、列、版本都是有序存储的,所以借助Filter,可以高效地完成查询。当执行Filter时,HBase会将Filter分发给各个HBase服务器节点来进行查询。
HBase中的过滤器也是基于Java开发的,只不过在Shell中,我们是使用基于JRuby的语法来实现的交互式查询。
在HBase的shell中,通过show_filters指令,可以查看到HBase中内置的一些过滤器
内置过滤器
hbase(main):079:0> show_filters
DependentColumnFilter # 允许用户指定一个参考列或引用列来过滤其他列的过滤器
KeyOnlyFilter # 只对单元格的键进行过滤和显示,不显示值
ColumnCountGetFilter # 限制每个逻辑行返回键值对的个数,在get方法中使用
SingleColumnValueFilter # 在指定的列蔟和列中进行比较的值过滤器
PrefixFilter # rowkey前缀过滤器
SingleColumnValueExcludeFilter # 排除匹配成功的值
FirstKeyOnlyFilter # 只扫描显示相同键的第一个单元格,其键值对会显示出来
ColumnRangeFilter # 过滤列名称的范围
ColumnValueFilter # 过滤列值的范围
TimestampsFilter # 时间戳过滤,支持等值,可以设置多个时间戳
FamilyFilter # 列簇过滤器
QualifierFilter # 列标识过滤器,只显示对应列名的数据
ColumnPrefixFilter # 对列名称的前缀进行过滤
RowFilter # 实现行键字符串的比较和过滤
MultipleColumnPrefixFilter # 可以指定多个前缀对列名称过滤
InclusiveStopFilter # 替代 ENDROW 返回终止条件行
PageFilter # 对显示结果按行进行分页显示
ValueFilter # 值过滤器,找到符合值条件的键值对
ColumnPaginationFilter # 对一行的所有列分页,只返回 [offset,offset+limit] 范围内的列
Took 0.0075 seconds
过滤器用法
scan ‘表名’, { Filter => "过滤器(比较运算符, ‘比较器表达式’)” }
比较器
比较器 | 描述 | 表达式语言缩写 |
---|---|---|
BinaryComparator | 匹配完整字节数组 | binary:值 |
BinaryPrefixComparator | 匹配字节数组前缀 | binaryprefix:值 |
BitComparator | 匹配比特位 | bit:值 |
NullComparator | 匹配空值 | null |
RegexStringComparator | 匹配正则表达式 | regexstring:正则表达式 |
SubstringComparator | 匹配子字符串 | substring:值 |
基本语法:比较器类型:比较器的值
需求1:使用RowFilter查询指定订单ID的数据
只查询订单的ID为:02602f66-adc7-40d4-8485-76b5632b5b53、订单状态以及支付方式
分析:
- 因为要订单ID就是ORDER_INFO表的rowkey,所以,我们应该使用rowkey过滤器来过滤
- 通过HBase的JAVA API,找到RowFilter构造器
- 参数1:op——比较运算符
- 参数2:rowComparator——比较器
所以构建该Filter的时候,只需要传入两个参数即可
hbase(main):081:0> scan 'ORDER_INFO', {FILTER => "RowFilter(=,'binary:02602f66-adc7-40d4-8485-76b5632b5b53')",FORMATTER => 'toString'}
ROW COLUMN+CELL02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:CATEGORY, timestamp=1660617813649, value=手机;02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:OPERATION_DATE, timestamp=1660617813327, value=2020-04-25 12:09:1602602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAYWAY, timestamp=1660617812628, value=102602f66-adc7-40d4-8485-76b5632b5b53 column=C1:PAY_MONEY, timestamp=1660617812242, value=407002602f66-adc7-40d4-8485-76b5632b5b53 column=C1:STATUS, timestamp=1660617811677, value=已提交02602f66-adc7-40d4-8485-76b5632b5b53 column=C1:USER_ID, timestamp=1660617812988, value=4944191
1 row(s)
Took 0.0139 seconds
hbase(main):082:0>
需求2:查询状态为「已付款」的订单
分析:
- 因为此处要指定列来进行查询,所以,我们不再使用rowkey过滤器,而是要使用列过滤器
- 我们要针对指定列和指定值进行过滤,比较适合使用SingleColumnValueFilter过滤器,查看JAVA API
- 参数1:列簇
- 参数2:列标识(列名)
- 比较运算符
- 比较器
注意:
- 列名STATUS的大小写一定要对!此处使用的是大写!
- 列名写错了查不出来数据,但HBase不会报错,因为HBase是无模式的
hbase(main):082:0> scan 'ORDER_INFO', {FILTER => "SingleColumnValueFilter('C1', 'STATUS', =, 'binary:已付款')", FORMATTER => 'toString'}
ROW COLUMN+CELL0e01edba-5e55-425e-837a-7efb91c56630 column=C1:CATEGORY, timestamp=1660617813657, value=男装;男鞋;0e01edba-5e55-425e-837a-7efb91c56630 column=C1:OPERATION_DATE, timestamp=1660617813337, value=2020-04-25 12:09:440e01edba-5e55-425e-837a-7efb91c56630 column=C1:PAYWAY, timestamp=1660617812639, value=30e01edba-5e55-425e-837a-7efb91c56630 column=C1:PAY_MONEY, timestamp=1660617812254, value=63700e01edba-5e55-425e-837a-7efb91c56630 column=C1:STATUS, timestamp=1660617811748, value=已付款 .........
需求3:查询支付方式为1,且金额大于3000的订单
分析:
- 此处需要使用多个过滤器共同来实现查询,多个过滤器,可以使用AND或者OR来组合多个过滤器完成查询
- 使用SingleColumnValueFilter实现对应列的查询
查询支付方式为1:SingleColumnValueFilter(‘C1’, ‘PAYWAY’, = , ‘binary:1’)
查询金额大于3000的订单:SingleColumnValueFilter(‘C1’, ‘PAY_MONEY’, > , ‘binary:3000’)
hbase(main):083:0> scan 'ORDER_INFO', {FILTER => "SingleColumnValueFilter('C1', 'PAYWAY', = , 'binary:1') AND SingleColumnValueFilter('C1', 'PAY_MONEY', > , 'binary:3000')", FORMATTER => 'toString'}
注意:
- HBase shell中比较默认都是字符串比较,所以如果是比较数值类型的,会出现不准确的情况
- 例如:在字符串比较中4000是比100000大的
INCR
某新闻APP应用为了统计每个新闻的每隔一段时间的访问次数,他们将这些数据保存在HBase中
该表格数据如下所示:
新闻ID | 访问次数 | 时间段 | ROWKEY |
---|---|---|---|
0000000001 | 12 | 00:00-01:00 | 0000000001_00:00-01:00 |
0000000002 | 12 | 01:00-02:00 | 0000000002_01:00-02:00 |
要求:原子性增加新闻的访问次数值
incr可以实现对某个单元格的值进行原子性计数。
语法:incr ‘表名’,‘rowkey’,‘列蔟:列名’,累加值(默认累加1)
- 如果某一列要实现计数功能,必须要使用incr来创建对应的列
- 使用put创建的列是不能实现累加的
导入测试数据
下载地址:wget http://osswangting.oss-cn-shanghai.aliyuncs.com/hbase/NEWS_VISIT_CNT.txt
该脚本文件创建了一个表,名为NEWS_VISIT_CNT,列蔟为C1。并使用incr创建了若干个计数器,每个rowkey为:新闻的编号_时间段。CNT为count的缩写,表示访问的次数
wangting@ops01:/home/wangting/hbase >pwd
/home/wangting/hbase
wangting@ops01:/home/wangting/hbase >ll
total 60
-rw-r--r-- 1 wangting wangting 6595 Aug 5 16:34 NEWS_VISIT_CNT.txt
-rw-r--r-- 1 wangting wangting 49366 Aug 5 16:34 ORDER_INFO.txt
wangting@ops01:/home/wangting/hbase >hbase shell /home/wangting/hbase/NEWS_VISIT_CNT.txt
hbase(main):084:0> scan 'NEWS_VISIT_CNT', {LIMIT => 3, FORMATTER => 'toString'}
ROW COLUMN+CELL0000000001_00:00-01:00 column=C1:CNT, timestamp=1660639040544, value=0000000001_00:00-01:00 column=C1:TIME_RANGE, timestamp=1660639040815, value=00:00-01:000000000002_01:00-02:00 column=C1:CNT, timestamp=1660639040575, value=0000000002_01:00-02:00 column=C1:TIME_RANGE, timestamp=1660639040820, value=01:00-02:000000000003_02:00-03:00 column=C1:CNT, timestamp=1660639040581, value={0000000003_02:00-03:00 column=C1:TIME_RANGE, timestamp=1660639040825, value=02:00-03:00
3 row(s)
Took 0.0215 seconds
hbase(main):085:0>
需求1:对0000000020新闻01:00 - 02:00访问计数+1
- 获取0000000020这条新闻在01:00-02:00当前的访问次数
hbase(main):085:0> get_counter 'NEWS_VISIT_CNT','0000000020_01:00-02:00','C1:CNT'
COUNTER VALUE = 6
Took 0.0101 seconds
- 使用incr进行累加
hbase(main):087:0> incr 'NEWS_VISIT_CNT','0000000020_01:00-02:00','C1:CNT'
COUNTER VALUE = 7
Took 0.0099 seconds
hbase(main):088:0>
- 再次查案新闻当前的访问次数
hbase(main):088:0> get_counter 'NEWS_VISIT_CNT','0000000020_01:00-02:00','C1:CNT'
COUNTER VALUE = 7
Took 0.0041 seconds
hbase(main):089:0>
shell管理操作
status
显示服务器状态信息
hbase(main):090:0> status
1 active master, 0 backup masters, 3 servers, 0 dead, 13.6667 average load
Took 0.1194 seconds
whoami
显示HBase当前用户
hbase(main):091:0> whoami
wangting (auth:SIMPLE)groups: wangting
Took 0.0093 seconds
list
显示当前所有的表
hbase(main):092:0> list
TABLE
NEWS_VISIT_CNT
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
8 row(s)
Took 0.0065 seconds
=> ["NEWS_VISIT_CNT", "ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS"]
count
统计指定表的记录数
hbase(main):093:0> count 'ORDER_INFO'
66 row(s)
Took 0.0241 seconds
=> 66
describe
展示表结构信息
hbase(main):094:0> describe 'ORDER_INFO'
Table ORDER_INFO is ENABLED
ORDER_INFO
COLUMN FAMILIES DESCRIPTION
{NAME => 'C1', 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'}
1 row(s)
Took 0.0260 seconds
hbase(main):095:0>
exists
检查表是否存在,适用于表量特别多的情况
hbase(main):095:0> exists 'ORDER_INFO'
Table ORDER_INFO does exist
Took 0.0084 seconds
=> true
is_endbled、is_disabled
检查表是否启用或禁用
hbase(main):096:0> is_enabled 'ORDER_INFO'
true
Took 0.0080 seconds
=> true
hbase(main):097:0> is_disabled 'ORDER_INFO'
false
Took 0.0040 seconds
=> 1
alter
该命令可以改变表和列蔟的模式
# 创建测试USER_INFO表,列簇C1,C2
hbase(main):098:0> create 'USER_INFO', 'C1', 'C2'
Created table USER_INFO
Took 0.7598 seconds
=> Hbase::Table - USER_INFO
# 新增列簇C3
hbase(main):099:0> alter 'USER_INFO', 'C3'
Updating all regions with the new schema...
1/1 regions updated.
Done.
Took 2.2088 seconds
# 删除列簇C3
hbase(main):100:0> alter 'USER_INFO', 'delete' => 'C3'
Updating all regions with the new schema...
1/1 regions updated.
Done.
Took 2.1770 seconds
disable、enable
禁用一张表/启用一张表
hbase(main):101:0> disable 'USER_INFO'
Took 0.7453 seconds
hbase(main):102:0> enable 'USER_INFO'
Took 0.7531 seconds
drop
删除一张表,记得在删除表之前必须先禁用
truncate
清空表的数据,相当于禁用表-删除表-创建表
HBase_java_api编程
需求与数据集
某某自来水公司,需要存储大量的缴费明细数据。以下截取了缴费明细的一部分内容
用户id | 姓名 | 用户地址 | 性别 | 缴费时间 | 表示数(本次) | 表示数(上次) | 用量(立方) | 合计金额 | 查表日期 | 最迟缴费日期 |
---|---|---|---|---|---|---|---|---|---|---|
4944191 | 登卫红 | 贵州省铜仁市德江县7单元267室 | 男 | 2020-05-10 | 308.1 | 283.1 | 25 | 150 | 2020-04-25 | 2020-06-09 |
因为缴费明细的数据记录非常庞大,该公司的信息部门决定使用HBase来存储这些数据。并且,他们希望能够通过Java程序来访问这些数据
实验环境准备
创建maven工程
通过idea工具创建maven项目(idea开发工具自行准备)
groupId | cn.wangt |
---|---|
artifactId | hbase_op |
导入pom依赖
pom文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>cn.wangt</groupId><artifactId>hbase_op</artifactId><version>1.0-SNAPSHOT</version><repositories><!-- 代码库 --><repository><id>aliyun</id><url>http://maven.aliyun.com/nexus/content/groups/public/</url><releases><enabled>true</enabled></releases><snapshots><enabled>false</enabled><updatePolicy>never</updatePolicy></snapshots></repository></repositories><dependencies><dependency><groupId>org.apache.hbase</groupId><artifactId>hbase-client</artifactId><version>2.1.0</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>org.testng</groupId><artifactId>testng</artifactId><version>6.14.3</version><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.1</version><configuration><target>1.8</target><source>1.8</source></configuration></plugin></plugins></build>
</project>
右边打开maven工具栏,对项目进行test测试,会去拉取依赖包
复制HBase和Hadoop配置文件
将以下三个配置文件复制到创建的maven项目中resource目录中
- hbase-site.xml (hbase/conf)
- core-site.xml (hadoop-3.1.3/etc/hadoop)
- log4j.properties (hbase/conf)
项目创建包结构和类
在项目test你也可以创建cn.wangt.hbase.admin.api_test 包结构
创建TableAmdinTest类
创建Hbase连接以及admin管理对象
要操作Hbase也需要建立Hbase的连接。此处我们仍然使用TestNG来编写测试。使用@BeforeTest初始化HBase连接,创建admin对象、@AfterTest关闭连接
实现步骤:
- 使用HbaseConfiguration.create()创建Hbase配置
- 使用ConnectionFactory.createConnection()创建Hbase连接
- 要创建表,需要基于Hbase连接获取admin管理对象
- 使用admin.close、connection.close关闭连接
package cn.wangt.hbase.admin.api_test;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.client.*;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;import java.io.IOException;public class TableAmdinTest {private Connection connection;private Admin admin;@BeforeTestpublic void beforeTest() throws IOException {// 1.使用HbaseConfiguration.create()创建Hbase配置Configuration configuration = HBaseConfiguration.create();// 2. 使用ConnectionFactory.createConnection()创建Hbase连接connection = ConnectionFactory.createConnection(configuration);// 3. 要创建表,需要基于Hbase连接获取admin管理对象// 要创建表、删除表需要和HMaster连接,所以需要有一个admin对象admin = connection.getAdmin();}@AfterTestpublic void afterTest() throws IOException {// 4. 使用admin.close、connection.close关闭连接admin.close();connection.close();}}
需求1:使用java代码创建表
创建一个名为WATER_BILL的表,包含一个列蔟C1
实现步骤:
- 判断表是否存在
a) 存在,则退出 - 使用TableDescriptorBuilder.newBuilder构建表描述构建器
- 使用ColumnFamilyDescriptorBuilder.newBuilder构建列蔟描述构建器
- 构建列蔟描述,构建表描述
- 创建表
当前的hbase表list信息:
hbase(main):103:0> list
TABLE
NEWS_VISIT_CNT
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
USER_INFO
9 row(s)
Took 0.0079 seconds
=> ["NEWS_VISIT_CNT", "ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS", "USER_INFO"]
建表代码
package cn.wangt.hbase.admin.api_test;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.io.IOException;public class TableAmdinTest {private Connection connection;private Admin admin;@BeforeTestpublic void beforeTest() throws IOException {// 1.使用HbaseConfiguration.create()创建Hbase配置Configuration configuration = HBaseConfiguration.create();// 2. 使用ConnectionFactory.createConnection()创建Hbase连接connection = ConnectionFactory.createConnection(configuration);// 3. 要创建表,需要基于Hbase连接获取admin管理对象// 要创建表、删除表需要和HMaster连接,所以需要有一个admin对象admin = connection.getAdmin();}@Testpublic void createTableTest() throws IOException {// 表名String TABLE_NAME = "WATER_BILL";// 列簇名String COLUMN_FAMILY = "C1";// 1.判断表是否存在if(admin.tableExists(TableName.valueOf(TABLE_NAME))){return;}// 2.构建表描述构建器TableDescriptorBuilder tableDescriptorBuilder = TableDescriptorBuilder.newBuilder(TableName.valueOf(TABLE_NAME));// 3.构建列簇描述构建器ColumnFamilyDescriptorBuilder columnFamilyDescriptorBuilder = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes(COLUMN_FAMILY));// 4.构建列簇描述ColumnFamilyDescriptor columnFamilyDescriptor = columnFamilyDescriptorBuilder.build();// 5.构建表描述 添加列簇tableDescriptorBuilder.setColumnFamily(columnFamilyDescriptor);TableDescriptor tableDescriptor = tableDescriptorBuilder.build();// 6.创建表admin.createTable(tableDescriptor);}@AfterTestpublic void afterTest() throws IOException {// 4. 使用admin.close、connection.close关闭连接admin.close();connection.close();}}
再次查看hbase表list信息:
hbase(main):105:0> list
TABLE
NEWS_VISIT_CNT
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
USER_INFO
WATER_BILL
10 row(s)
Took 0.0038 seconds
=> ["NEWS_VISIT_CNT", "ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS", "USER_INFO", "WATER_BILL"]
可以看到WATER_BILL表创建成功
需求2:向表中插入数据
在 test 目录中创建 cn.wangt.hbase.data.api_test 包
创建DataOpTest类
在表中插入一个行,该行只包含一个列
ROWKEY | 姓名(列名:NAME) |
---|---|
4944191 | 登卫红 |
实现步骤:
1.使用Hbase连接获取Htable
2.构建ROWKEY、列蔟名、列名
3.构建Put对象(对应put命令)
4.添加姓名列
5.使用Htable表对象执行put操作
6.关闭Htable表对象
代码:
package cn.wangt.hbase.data.api_test;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.io.IOException;public class DataOpTest {private Configuration configuration;private Connection connection;@BeforeTestpublic void beforeTest() throws IOException {configuration = HBaseConfiguration.create();connection = ConnectionFactory.createConnection(configuration);}@Testpublic void addTest() throws IOException {// 1.使用Hbase连接获取HtableTableName waterBillTableName = TableName.valueOf("WATER_BILL");Table waterBillTable = connection.getTable(waterBillTableName);// 2.构建ROWKEY、列蔟名、列名String rowkey = "4944191";String cfName = "C1";String colName = "NAME";// 3.构建Put对象(对应put命令)Put put = new Put(Bytes.toBytes(rowkey));// 4.添加姓名列put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colName), Bytes.toBytes("登卫红"));// 5.使用Htable表对象执行put操作waterBillTable.put(put);// 6. 关闭表waterBillTable.close();}@AfterTestpublic void afterTest() throws IOException {connection.close();}}
查询验证:
hbase(main):106:0> get 'WATER_BILL','4944191',{FORMATTER => 'toString'}
COLUMN CELLC1:NAME timestamp=1660704584510, value=登卫红
1 row(s)
Took 0.0118 seconds
hbase(main):107:0>
继续尝试插入其他列数据
列名 | 说明 | 值 |
---|---|---|
ADDRESS | 用户地址 | 贵州省铜仁市德江县7单元267室 |
SEX | 性别 | 男 |
PAY_DATE | 缴费时间 | 2020-05-10 |
NUM_CURRENT | 表示数(本次) | 308.1 |
NUM_PREVIOUS | 表示数(上次) | 283.1 |
NUM_USAGE | 用量(立方) | 25 |
TOTAL_MONEY | 合计金额 | 150 |
RECORD_DATE | 查表日期 | 2020-04-25 |
LATEST_DATE | 最迟缴费日期 | 2020-06-09 |
代码:
package cn.wangt.hbase.data.api_test;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.io.IOException;public class DataOpTest {private Configuration configuration;private Connection connection;@BeforeTestpublic void beforeTest() throws IOException {configuration = HBaseConfiguration.create();connection = ConnectionFactory.createConnection(configuration);}@Testpublic void addTest() throws IOException {// 1.使用Hbase连接获取HtableTableName waterBillTableName = TableName.valueOf("WATER_BILL");Table waterBillTable = connection.getTable(waterBillTableName);// 2.构建ROWKEY、列蔟名、列名String rowkey = "4944191";String cfName = "C1";String colName = "NAME";String colADDRESS = "ADDRESS";String colSEX = "SEX";String colPAY_DATE = "PAY_DATE";String colNUM_CURRENT = "NUM_CURRENT";String colNUM_PREVIOUS = "NUM_PREVIOUS";String colNUM_USAGE = "NUM_USAGE";String colTOTAL_MONEY = "TOTAL_MONEY";String colRECORD_DATE = "RECORD_DATE";String colLATEST_DATE = "LATEST_DATE";// 3.构建Put对象(对应put命令)Put put = new Put(Bytes.toBytes(rowkey));// 4.添加姓名列put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colName), Bytes.toBytes("登卫红"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colADDRESS), Bytes.toBytes("贵州省铜仁市德江县7单元267室"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colSEX), Bytes.toBytes("男"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colPAY_DATE), Bytes.toBytes("2020-05-10"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colNUM_CURRENT), Bytes.toBytes("308.1"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colNUM_PREVIOUS), Bytes.toBytes("283.1"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colNUM_USAGE), Bytes.toBytes("25"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colTOTAL_MONEY), Bytes.toBytes("150"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colRECORD_DATE), Bytes.toBytes("2020-04-25"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colLATEST_DATE), Bytes.toBytes("2020-06-09"));// 5.使用Htable表对象执行put操作waterBillTable.put(put);// 6. 关闭表waterBillTable.close();}@AfterTestpublic void afterTest() throws IOException {connection.close();}}
查询验证:
hbase(main):108:0> get 'WATER_BILL','4944191',{FORMATTER => 'toString'}
COLUMN CELLC1:ADDRESS timestamp=1660705352480, value=贵州省铜仁市德江县7单元267室C1:LATEST_DATE timestamp=1660705352480, value=2020-06-09C1:NAME timestamp=1660705352480, value=登卫红C1:NUM_CURRENT timestamp=1660705352480, value=308.1C1:NUM_PREVIOUS timestamp=1660705352480, value=283.1C1:NUM_USAGE timestamp=1660705352480, value=25C1:PAY_DATE timestamp=1660705352480, value=2020-05-10C1:RECORD_DATE timestamp=1660705352480, value=2020-04-25C1:SEX timestamp=1660705352480, value=男C1:TOTAL_MONEY timestamp=1660705352480, value=150
1 row(s)
Took 0.0122 seconds
需求3:表中查看1条数据
查询rowkey为4944191的所有列的数据,并打印出来
实现步骤:
- 获取HTable
- 使用rowkey构建Get对象
- 执行get请求
- 获取所有单元格
- 打印rowkey
- 迭代单元格列表
- 关闭表
代码:
注意:截至目前代码均是类的完整代码,实际只是在原有代码中增加了get方法,运行时只需要单独运行get即可
后续代码至附上相关代码段
package cn.wangt.hbase.data.api_test;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.io.IOException;
import java.util.List;public class DataOpTest {private Configuration configuration;private Connection connection;@BeforeTestpublic void beforeTest() throws IOException {configuration = HBaseConfiguration.create();connection = ConnectionFactory.createConnection(configuration);}@Testpublic void addTest() throws IOException {// 1.使用Hbase连接获取HtableTableName waterBillTableName = TableName.valueOf("WATER_BILL");Table waterBillTable = connection.getTable(waterBillTableName);// 2.构建ROWKEY、列蔟名、列名String rowkey = "4944191";String cfName = "C1";String colName = "NAME";String colADDRESS = "ADDRESS";String colSEX = "SEX";String colPAY_DATE = "PAY_DATE";String colNUM_CURRENT = "NUM_CURRENT";String colNUM_PREVIOUS = "NUM_PREVIOUS";String colNUM_USAGE = "NUM_USAGE";String colTOTAL_MONEY = "TOTAL_MONEY";String colRECORD_DATE = "RECORD_DATE";String colLATEST_DATE = "LATEST_DATE";// 3.构建Put对象(对应put命令)Put put = new Put(Bytes.toBytes(rowkey));// 4.添加姓名列put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colName), Bytes.toBytes("登卫红"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colADDRESS), Bytes.toBytes("贵州省铜仁市德江县7单元267室"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colSEX), Bytes.toBytes("男"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colPAY_DATE), Bytes.toBytes("2020-05-10"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colNUM_CURRENT), Bytes.toBytes("308.1"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colNUM_PREVIOUS), Bytes.toBytes("283.1"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colNUM_USAGE), Bytes.toBytes("25"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colTOTAL_MONEY), Bytes.toBytes("150"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colRECORD_DATE), Bytes.toBytes("2020-04-25"));put.addColumn(Bytes.toBytes(cfName), Bytes.toBytes(colLATEST_DATE), Bytes.toBytes("2020-06-09"));// 5.使用Htable表对象执行put操作waterBillTable.put(put);// 6. 关闭表waterBillTable.close();}@Testpublic void getOneTest() throws IOException {// 1. 获取HTableTableName waterBillTableName = TableName.valueOf("WATER_BILL");Table waterBilltable = connection.getTable(waterBillTableName);// 2. 使用rowkey构建Get对象Get get = new Get(Bytes.toBytes("4944191"));// 3. 执行get请求Result result = waterBilltable.get(get);// 4. 获取所有单元格List<Cell> cellList = result.listCells();// 打印rowkeySystem.out.println("rowkey => " + Bytes.toString(result.getRow()));// 5. 迭代单元格列表for (Cell cell : cellList) {// 打印列蔟名System.out.print(Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()));System.out.println(" => " + Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));}// 6.关闭表waterBillTable.close();}@AfterTestpublic void afterTest() throws IOException {connection.close();}}
run运行getOneTest查看 idea控制台输出:
2022-08-17 11:13:15,559 INFO [ReadOnlyZKClient-ops01:2181,ops02:2181,ops03:2181@0x15eb5ee5-SendThread(ops03:2181)] zookeeper.ClientCnxn: Socket connection established to ops03/11.8.36.76:2181, initiating session
2022-08-17 11:13:15,590 INFO [ReadOnlyZKClient-ops01:2181,ops02:2181,ops03:2181@0x15eb5ee5-SendThread(ops03:2181)] zookeeper.ClientCnxn: Session establishment complete on server ops03/11.8.36.76:2181, sessionid = 0x30a8f7122b00027, negotiated timeout = 40000rowkey => 4944191
ADDRESS => 贵州省铜仁市德江县7单元267室
LATEST_DATE => 2020-06-09
NAME => 登卫红
NUM_CURRENT => 308.1
NUM_PREVIOUS => 283.1
NUM_USAGE => 25
PAY_DATE => 2020-05-10
RECORD_DATE => 2020-04-25
SEX => 男
TOTAL_MONEY => 1502022-08-17 11:13:17,012 INFO [main] zookeeper.ReadOnlyZKClient: Close zookeeper connection 0x15eb5ee5 to ops01:2181,ops02:2181,ops03:2181===============================================
Default Suite
Total tests run: 1, Failures: 0, Skips: 0
===============================================
2022-08-17 11:13:17,028 INFO [ReadOnlyZKClient-ops01:2181,ops02:2181,ops03:2181@0x15eb5ee5] zookeeper.ZooKeeper: Session: 0x30a8f7122b00027 closed
Process finished with exit code 0
需求4:删除一条数据
删除rowkey为4944191的整条数据
实现步骤:
1.获取HTable对象
2.根据rowkey构建delete对象
3.执行delete请求
4.关闭表
相关代码块:
@Testpublic void deleteOneTest() throws IOException {// 1. 获取HTable对象Table waterBillTable = connection.getTable(TableName.valueOf("WATER_BILL"));// 2. 根据rowkey构建delete对象Delete delete = new Delete(Bytes.toBytes("4944191"));// 3. 执行delete请求waterBillTable.delete(delete);// 4. 关闭表waterBillTable.close();}
复核验证
hbase(main):110:0> get 'WATER_BILL','4944191',{FORMATTER => 'toString'}
COLUMN CELL
0 row(s)
Took 0.0053 seconds
hbase(main):111:0>
需求5:导入数据&导出数据
数据导入
需求将一份数据文件导入HBase中
实验数据文件:http://osswangting.oss-cn-shanghai.aliyuncs.com/hbase/part-m-00000_10w
在HBase中,有一个Import的MapReduce作业,可以专门用来将数据文件导入到HBase中
使用方式:hbase org.apache.hadoop.hbase.mapreduce.Import 表名 HDFS数据文件路径
- 将资料中数据文件上传到Linux中或wget下载
- 将文件上传到hdfs中
# 下载数据文件
wangting@ops01:/home/wangting/hbase >wget http://osswangting.oss-cn-shanghai.aliyuncs.com/hbase/part-m-00000_10w
# 查看
wangting@ops01:/home/wangting/hbase >ll
total 50340
-rw-r--r-- 1 wangting wangting 6595 Aug 5 16:34 NEWS_VISIT_CNT.txt
-rw-r--r-- 1 wangting wangting 49366 Aug 5 16:34 ORDER_INFO.txt
-rw-r--r-- 1 wangting wangting 51483241 Aug 5 16:32 part-m-00000_10w
# 创建hdfs目录
wangting@ops01:/home/wangting/hbase >hadoop fs -mkdir -p /water_bill/output_ept_10W
2022-08-17 11:39:05,123 INFO [main] Configuration.deprecation (Configuration.java:logDeprecation(1395)) - No unit for dfs.client.datanode-restart.timeout(30) assuming SECONDS
# 文件上传至hdfs
wangting@ops01:/home/wangting/hbase >hadoop fs -put part-m-00000_10w /water_bill/output_ept_10W
2022-08-17 11:39:17,432 INFO [main] Configuration.deprecation (Configuration.java:logDeprecation(1395)) - No unit for dfs.client.datanode-restart.timeout(30) assuming SECONDS
2022-08-17 11:39:18,194 INFO [Thread-7] sasl.SaslDataTransferClient (SaslDataTransferClient.java:checkTrustAndSend(239)) - SASL encryption trust check: localHostTrusted = false, remoteHostTrusted = false
# 查看上传结果
wangting@ops01:/home/wangting/hbase >hadoop dfs -ls /water_bill/output_ept_10W
WARNING: Use of this script to execute dfs is deprecated.
WARNING: Attempting to execute replacement "hdfs dfs" instead.
2022-08-17 11:39:44,182 INFO [main] Configuration.deprecation (Configuration.java:logDeprecation(1395)) - No unit for dfs.client.datanode-restart.timeout(30) assuming SECONDS
Found 1 items
-rw-r--r-- 3 wangting supergroup 51483241 2022-08-17 11:39 /water_bill/output_ept_10W/part-m-00000_10w
wangting@ops01:/home/wangting/hbase >
当前相当于HBase有一张表water_bill,但暂时未和hdfs上数据关联,还需要导入hbase
wangting@ops01:/home/wangting/hbase >hbase org.apache.hadoop.hbase.mapreduce.Import WATER_BILL /water_bill/output_ept_10W
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
2022-08-17 11:44:48,894 INFO [main] mapreduce.Import: writing directly to table from Mapper.
2022-08-17 11:44:49,100 INFO [main] client.RMProxy: Connecting to ResourceManager at ops02/11.8.36.63:8032
...
...
2022-08-17 11:45:00,711 INFO [main] mapreduce.Job: map 0% reduce 0%
2022-08-17 11:45:13,801 INFO [main] mapreduce.Job: map 100% reduce 0%
2022-08-17 11:45:14,814 INFO [main] mapreduce.Job: Job job_1615531413182_9573 completed successfully
2022-08-17 11:45:14,901 WARN [main] counters.FrameworkCounterGroup: MAP_PHYSICAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:45:14,902 WARN [main] counters.FrameworkCounterGroup: MAP_VIRTUAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:45:14,911 INFO [main] mapreduce.Job: operations=0HDFS: Number of bytes read=51483366HDFS: Number of bytes written=0HDFS: Number of read operations=3HDFS: Number of large read operations=0HDFS: Number of write operations=0Job Counters Launched map tasks=1Data-local map tasks=1Total time spent by all maps in occupied slots (ms)=11095Total time spent by all reduces in occupied slots (ms)=0Total time spent by all map tasks (ms)=11095Total vcore-milliseconds taken by all map tasks=11095Total megabyte-milliseconds taken by all map tasks=11361280Map-Reduce FrameworkMap input records=99505Map output records=99505Input split bytes=125Spilled Records=0Failed Shuffles=0Merged Map outputs=0GC time elapsed (ms)=172CPU time spent (ms)=12840Physical memory (bytes) snapshot=272273408Virtual memory (bytes) snapshot=2012495872Total committed heap usage (bytes)=170393600File Input Format Counters Bytes Read=51483241File Output Format Counters Bytes Written=0
2022-08-17 11:45:14,917 WARN [main] counters.FrameworkCounterGroup: MAP_PHYSICAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:45:14,917 WARN [main] counters.FrameworkCounterGroup: MAP_VIRTUAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:45:14,921 WARN [main] counters.FrameworkCounterGroup: MAP_PHYSICAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:45:14,921 WARN [main] counters.FrameworkCounterGroup: MAP_VIRTUAL_MEMORY_BYTES_MAX is not a recognized counter.
wangting@ops01:/home/wangting/hbase >
数据导出
# 查看hdfs的/water_bill/目录
wangting@ops01:/home/wangting/hbase >hadoop dfs -ls /water_bill/
WARNING: Use of this script to execute dfs is deprecated.
WARNING: Attempting to execute replacement "hdfs dfs" instead.
2022-08-17 11:54:49,211 INFO [main] Configuration.deprecation (Configuration.java:logDeprecation(1395)) - No unit for dfs.client.datanode-restart.timeout(30) assuming SECONDS
Found 1 items
drwxr-xr-x - wangting supergroup 0 2022-08-17 11:39 /water_bill/output_ept_10W
wangting@ops01:/home/wangting/hbase >
# 数据导出
wangting@ops01:/home/wangting/hbase >hbase org.apache.hadoop.hbase.mapreduce.Export WATER_BILL /water_bill/output_ept_10W_export_0817
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hbase/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.htmlmultiple_bindings for an explanation.
...
...
2022-08-17 11:55:09,033 INFO [main] mapreduce.Job: map 0% reduce 0%
2022-08-17 11:55:19,115 INFO [main] mapreduce.Job: map 100% reduce 0%
2022-08-17 11:55:20,128 INFO [main] mapreduce.Job: Job job_1615531413182_9574 completed successfully
2022-08-17 11:55:20,214 WARN [main] counters.FrameworkCounterGroup: MAP_PHYSICAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:55:20,214 WARN [main] counters.FrameworkCounterGroup: MAP_VIRTUAL_MEMORY_BYTES_MAX is not a recognized counter.
2022-08-17 11:55:20,224 INFO [main] mapreduce.Job: Total time spent by all reduces in occupied slots (ms)=0Total time spent by all map tasks (ms)=8586Total vcore-milliseconds taken by all map tasks=8586Total megabyte-milliseconds taken by all map tasks=8792064Map-Reduce FrameworkMap input records=99505Map output records=99505Input split bytes=136Spilled Records=0Failed Shuffles=0Merged Map outputs=0GC time elapsed (ms)=149CPU time spent (ms)=10410Physical memory (bytes) snapshot=287027200Virtual memory (bytes) snapshot=2046070784Total committed heap usage (bytes)=186122240HBase CountersBYTES_IN_REMOTE_RESULTS=53960732BYTES_IN_RESULTS=53960732MILLIS_BETWEEN_NEXTS=3697NOT_SERVING_REGION_EXCEPTION=0NUM_SCANNER_RESTARTS=0NUM_SCAN_RESULTS_STALE=0REGIONS_SCANNED=1REMOTE_RPC_CALLS=996REMOTE_RPC_RETRIES=0ROWS_FILTERED=1ROWS_SCANNED=99506RPC_CALLS=996RPC_RETRIES=0File Input Format Counters Bytes Read=0File Output Format Counters Bytes Written=51483241
# 查看验证hdfs已经成功导出一份output_ept_10W_export_0817数据
wangting@ops01:/home/wangting/hbase >hadoop dfs -ls /water_bill/
WARNING: Use of this script to execute dfs is deprecated.
WARNING: Attempting to execute replacement "hdfs dfs" instead.
2022-08-17 11:56:30,957 INFO [main] Configuration.deprecation (Configuration.java:logDeprecation(1395)) - No unit for dfs.client.datanode-restart.timeout(30) assuming SECONDS
Found 2 items
drwxr-xr-x - wangting supergroup 0 2022-08-17 11:39 /water_bill/output_ept_10W
drwxr-xr-x - wangting supergroup 0 2022-08-17 11:55 /water_bill/output_ept_10W_export_0817
wangting@ops01:/home/wangting/hbase >
需求6:代码实现条件查询
查询2020年6月份所有用户的用水量
在Java API中,我们也是使用scan + filter来实现过滤查询。2020年6月份其实就是从2020年6月1日到2020年6月30日的所有抄表数据
在cn.wangt.hbase.data.api_test包下创建ScanFilterTest类
使用@BeforeTest、@AfterTest构建HBase连接、以及关闭HBase连接
实现步骤:
- 获取表
- 构建scan请求对象
- 构建两个过滤器
a) 构建两个日期范围过滤器(注意此处请使用RECORD_DATE——抄表日期比较
b) 构建过滤器列表 - 执行scan扫描请求
- 迭代打印result
- 迭代单元格列表
- 关闭ResultScanner(这玩意把转换成一个个的类似get的操作,注意要关闭释放资源)
- 关闭表
代码实现
package cn.wangt.hbase.data.api_test;import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CompareOperator;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.BinaryComparator;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
import org.apache.hadoop.hbase.util.Bytes;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;import java.io.IOException;
import java.util.Iterator;
import java.util.List;public class ScanFilterTest {private Connection connection;private TableName TABLE_NAME = TableName.valueOf("WATER_BILL");@BeforeTestpublic void beforeTest() throws IOException {Configuration configuration = HBaseConfiguration.create();connection = ConnectionFactory.createConnection(configuration);}// 查询2020年6月份所有用户的用水量数据@Testpublic void scanFilterTest() throws IOException {// 1. 获取表Table table = connection.getTable(TABLE_NAME);// 2. 构建scan请求对象Scan scan = new Scan();// 3. 构建两个过滤器// a) 构建两个日期范围过滤器(注意此处请使用RECORD_DATE——抄表日期比较SingleColumnValueFilter startFilter = new SingleColumnValueFilter(Bytes.toBytes("C1"), Bytes.toBytes("RECORD_DATE"), CompareOperator.GREATER_OR_EQUAL, new BinaryComparator(Bytes.toBytes("2020-06-01")));SingleColumnValueFilter endFilter = new SingleColumnValueFilter(Bytes.toBytes("C1"), Bytes.toBytes("RECORD_DATE"), CompareOperator.LESS_OR_EQUAL, new BinaryComparator(Bytes.toBytes("2020-06-30")));// b) 构建过滤器列表FilterList filterList = new FilterList(FilterList.Operator.MUST_PASS_ALL, startFilter, endFilter);// 4. 执行scan扫描请求scan.setFilter(filterList);ResultScanner resultScanner = table.getScanner(scan);Iterator<Result> iterator = resultScanner.iterator();// 5. 迭代打印resultwhile (iterator.hasNext()) {Result result = iterator.next();// 列出所有的单元格List<Cell> cellList = result.listCells();// 5. 打印rowkeybyte[] rowkey = result.getRow();System.out.println(Bytes.toString(rowkey));// 6. 迭代单元格列表for (Cell cell : cellList) {// 将字节数组转换为字符串// 获取列蔟的名称String cf = Bytes.toString(cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength());// 获取列的名称String columnName = Bytes.toString(cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength());String value = "";// 解决乱码问题:// 思路:// 如果某个列是以下列中的其中一个,调用toDouble将它认为是一个数值来转换//1. NUM_CURRENT//2. NUM_PREVIOUS//3. NUM_USAGE//4. TOTAL_MONEYif (columnName.equals("NUM_CURRENT")|| columnName.equals("NUM_PREVIOUS")|| columnName.equals("NUM_USAGE")|| columnName.equals("TOTAL_MONEY")) {value = Bytes.toDouble(cell.getValueArray()) + "";} else {// 获取值value = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());}System.out.println(cf + ":" + columnName + " -> " + value);}}// 7. 关闭ResultScanner(这玩意把转换成一个个的类似get的操作,注意要关闭释放资源)resultScanner.close();// 8. 关闭表table.close();}@AfterTestpublic void afterTest() throws IOException {connection.close();}}
需求7:使用Java代码删除表
创建一个测试表
hbase(main):114:0> create 'TEST_FOR_DROP','C1'
Created table TEST_FOR_DROP
Took 0.7583 seconds
=> Hbase::Table - TEST_FOR_DROP
hbase(main):115:0> list
TABLE
NEWS_VISIT_CNT
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
TEST_FOR_DROP
USER_INFO
WATER_BILL
11 row(s)
Took 0.0078 seconds
=> ["NEWS_VISIT_CNT", "ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS", "TEST_FOR_DROP", "USER_INFO", "WATER_BILL"]
hbase(main):116:0>
实现步骤:
- 判断表是否存在
- 如果存在,则禁用表
- 再删除表
在admin.api_test包下的,TableAmdinTest类中增加一个dropTable方法
相关代码块
@Testpublic void dropTable() throws IOException {// 表名TableName tableName = TableName.valueOf("TEST_FOR_DROP");// 1. 判断表是否存在if(admin.tableExists(tableName)) {// 2. 禁用表admin.disableTable(tableName);// 3. 删除表admin.deleteTable(tableName);}}
命令行查看验证发现TEST_FOR_DROP表已经不存在
hbase(main):117:0> list
TABLE
NEWS_VISIT_CNT
ORDER_INFO
SYSTEM.CATALOG
SYSTEM.FUNCTION
SYSTEM.LOG
SYSTEM.MUTEX
SYSTEM.SEQUENCE
SYSTEM.STATS
USER_INFO
WATER_BILL
10 row(s)
Took 0.0070 seconds
=> ["NEWS_VISIT_CNT", "ORDER_INFO", "SYSTEM.CATALOG", "SYSTEM.FUNCTION", "SYSTEM.LOG", "SYSTEM.MUTEX", "SYSTEM.SEQUENCE", "SYSTEM.STATS", "USER_INFO", "WATER_BILL"]
hbase(main):118:0>
HBase高可用
考虑关于HBase集群的一个问题,在当前的HBase集群中,只有一个Master,一旦Master出现故障,将会导致HBase不再可用。所以,在实际的生产环境中,是非常有必要搭建一个高可用的HBase集群的
HBase高可用介绍
HBase的高可用配置其实就是HMaster的高可用。要搭建HBase的高可用,只需要再选择一个节点作为HMaster,在HBase的conf目录下创建文件backup-masters,然后再backup-masters添加备份Master的记录。一条记录代表一个backup master,可以在文件配置多个记录
当前环境查看
wangting@ops01:/home/wangting >for i in ops01 ops02 ops03;do echo "=== $i ===" && ssh $i jps -l | grep hbase;done
=== ops01 ===
16029 org.apache.hadoop.hbase.regionserver.HRegionServer
15806 org.apache.hadoop.hbase.master.HMaster
=== ops02 ===
60896 org.apache.hadoop.hbase.regionserver.HRegionServer
=== ops03 ===
80496 org.apache.hadoop.hbase.regionserver.HRegionServer
wangting@ops01:/home/wangting >
11.8.37.50 ops01 # HMaster
11.8.36.63 ops02
11.8.36.76 ops03
hbase(main):118:0> status
1 active master, 0 backup masters, 3 servers, 0 dead, 14.3333 average load
Took 0.0117 seconds
hbase(main):119:0>
搭建部署HBase高可用落地
# 在hbase的conf文件夹中创建 backup-masters 文件
wangting@ops01:/home/wangting >cd /opt/module/hbase/conf/
# 将另2台服务器hosts添加至文件
wangting@ops01:/opt/module/hbase/conf >vim backup-masters
ops02
ops03#分发同步文件
wangting@ops01:/opt/module/hbase/conf >scp backup-masters ops02:$PWD
wangting@ops01:/opt/module/hbase/conf >scp backup-masters ops03:$PWD
# 重启服务
wangting@ops01:/opt/module/hbase/bin >stop-hbase.sh
wangting@ops01:/opt/module/hbase/bin >start-hbase.sh# 再次查看应用相关信息
wangting@ops01:/opt/module/hbase/bin >for i in ops01 ops02 ops03;do echo "=== $i ===" && ssh $i jps -l | grep hbase;done
=== ops01 ===
112651 org.apache.hadoop.hbase.master.HMaster
112874 org.apache.hadoop.hbase.regionserver.HRegionServer
=== ops02 ===
117251 org.apache.hadoop.hbase.regionserver.HRegionServer
117339 org.apache.hadoop.hbase.master.HMaster
=== ops03 ===
64828 org.apache.hadoop.hbase.master.HMaster
64733 org.apache.hadoop.hbase.regionserver.HRegionServer
状态查看
hbase(main):119:0> status
1 active master, 2 backup masters, 3 servers, 0 dead, 14.3333 average load
Took 0.0641 seconds
hbase(main):120:0>
通过zk去查看hbase集群信息
wangting@ops01:/home/wangting >zkCli.sh
Connecting to localhost:2181
2022-08-17 16:44:34,063 [myid:] - INFO [main:Environment@109] - Client environment:zookeeper.version=3.5.7-f0fdd52973d373ffd9c86b81d99842dc2c7f660e, built on 02/10/2020 11:30 GMT
2022-08-17 16:44:34,067 [myid:] - INFO [main:Environment@109] - Client environment:host.name=ops01
[zk: localhost:2181(CONNECTED) 0]
[zk: localhost:2181(CONNECTED) 1]
# 通过get /hbase/master看到hbase的master是ops01
[zk: localhost:2181(CONNECTED) 2] get /hbase/master
�master:16000D��PBUFops01�}���ת0�}# 通过ls /hbase/backup-masters看到backup-masters为ops02和ops03
[zk: localhost:2181(CONNECTED) 3] ls /hbase/backup-masters
[ops02,16000,1660725472902, ops03,16000,1660725472775]
[zk: localhost:2181(CONNECTED) 4]
验证HBase高可用
# 查看当前ops01上hbase进程
wangting@ops01:/home/wangting >jps -l | grep hbase
112651 org.apache.hadoop.hbase.master.HMaster
112874 org.apache.hadoop.hbase.regionserver.HRegionServer
# 杀掉master的进程
wangting@ops01:/home/wangting >kill -9 112651
wangting@ops01:/home/wangting >
# 查看hbase集群服务信息
wangting@ops01:/home/wangting >for i in ops01 ops02 ops03;do echo "=== $i ===" && ssh $i jps -l | grep hbase;done
=== ops01 ===
112874 org.apache.hadoop.hbase.regionserver.HRegionServer
=== ops02 ===
117251 org.apache.hadoop.hbase.regionserver.HRegionServer
117339 org.apache.hadoop.hbase.master.HMaster
=== ops03 ===
64828 org.apache.hadoop.hbase.master.HMaster
64733 org.apache.hadoop.hbase.regionserver.HRegionServer
wangting@ops01:/home/wangting >
# 通过zookeeper查看集群角色信息
wangting@ops01:/home/wangting >zkCli.sh
Connecting to localhost:2181
2022-08-17 16:58:25,940 [myid:] - INFO [main:Environment@109] - Client environment:zookeeper.version=3.5.7-f0fdd52973d373ffd9c86b81d99842dc2c7f660e, built on 02/10/2020 11:30 GMT
2022-08-17 16:58:25,944 [myid:] - INFO [main:Environment@109] - Client environment:host.name=ops01
WatchedEvent state:SyncConnected type:None path:null
# 可以看到杀掉ops01的master进程后,ops03成为master
[zk: localhost:2181(CONNECTED) 0] get /hbase/master
�master:16000��� L\�BPBUFops03�}���ת0�}
# backup-masters为ops02
[zk: localhost:2181(CONNECTED) 1] ls /hbase/backup-masters
[ops02,16000,1660725472902]
[zk: localhost:2181(CONNECTED) 2]
至此,hbase自动实现了master的角色切换
HBase架构
系统架构图
角色-Client
客户端,指发出HBase操作的请求方。例如:之前编写的Java API代码发起求情、以及HBase shell交互命令行,都是CLient
角色-Master Server
- 监控RegionServer
- 处理RegionServer故障转移
- 处理元数据的变更
- 处理region的分配或移除
- 在空闲时间进行数据的负载均衡
- 通过Zookeeper发布自己的位置给客户端
角色-Region Server
- 处理分配给它的Region
- 负责存储HBase的实际数据
- 刷新缓存到HDFS
- 维护HLog
- 执行压缩
- 负责处理Region分片
其中RegionServer中包含了大量丰富的组件
Write-Ahead logs
HFile(StoreFile)
Store
MemStore
Region
逻辑结构模型
Region
- 在HBASE中,表被划分为很多「Region」,并由Region Server提供服务
Store
- Region按列蔟垂直划分为「Store」,存储在HDFS在文件中
MemStore
- MemStore与缓存内存类似
- 当往HBase中写入数据时,首先是写入到MemStore
- 每个列蔟将有一个MemStore
- 当MemStore存储快满的时候,整个数据将写入到HDFS中的HFile中
StoreFile
- 每当任何数据被写入HBASE时,首先要写入MemStore
- 当MemStore快满时,整个排序的key-value数据将被写入HDFS中的一个新的HFile中
- 写入HFile的操作是连续的,速度非常快
- 物理上存储的是HFile
WAL
- WAL全称为Write Ahead Log,它最大的作用就是故障恢复
- WAL是HBase中提供的一种高并发、持久化的日志保存与回放机制
- 每个业务数据的写入操作(PUT/DELETE/INCR),都会保存在WAL中
- 一旦服务器崩溃,通过回放WAL,就可以实现恢复崩溃之前的数据
- 物理上存储是Hadoop的Sequence File
Apache Phoenix
phoenix简介
Apache Phoenix让Hadoop中支持低延迟OLTP和业务操作分析。
提供标准的SQL以及完备的ACID事务支持
通过利用HBase作为存储,让NoSQL数据库具备通过有模式的方式读取数据,我们可以使用SQL语句来操作HBase,例如:创建表、以及插入数据、修改数据、删除数据等。
Phoenix通过协处理器在服务器端执行操作,最小化客户机/服务器数据传输
Apache Phoenix可以很好地与其他的Hadoop组件整合在一起,例如:Spark、Hive、Flume以及MapReduce。
hoenix特点
- Phoenix不会影响HBase性能,反而会提升HBase性能
- Phoenix将SQL查询编译为本机HBase扫描
- 确定scan的key的最佳startKey和endKey
- 编排scan的并行执行
- 将WHERE子句中的谓词推送到服务器端
- 通过协处理器执行聚合查询
- 用于提高非行键列查询性能的二级索引
- 统计数据收集,以改进并行化,并指导优化之间的选择
- 跳过扫描筛选器以优化IN、LIKE和OR查询
- 行键加盐保证分配均匀,负载均衡
Phoenix只是在HBase之上构建了SQL查询引擎,并不是像MapReduce、Spark这种大规模数据计算引擎。HBase的定位是在高性能随机读写,Phoenix可以使用SQL快查询HBase中的数据,但数据操作底层是必须符合HBase的存储结构,例如:必须要有ROWKEY、必须要有列蔟。因为有这样的一些限制,绝大多数公司不会选择HBase+Phoenix来作为数据仓库的开发。而是用来快速进行海量数据的随机读写。这方面HBase +Phoenix有很大的优势。
安装部署phoenix
官网下载地址:http://archive.apache.org/dist/phoenix/
# 安装包
wangting@ops01:/home/wangting >cd /opt/software/
wangting@ops01:/opt/software >ll | grep phoenix
-rw-r--r-- 1 wangting wangting 436868323 May 9 17:11 apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz
# 解压
wangting@ops01:/opt/software >tar -zxvf apache-phoenix-5.0.0-HBase-2.0-bin.tar.gz -C /opt/module/
# 改名
wangting@ops01:/opt/module >mv apache-phoenix-5.0.0-HBase-2.0-bin phoenix
wangting@ops01:/opt/module >cd phoenix/
wangting@ops01:/opt/module/phoenix >
# 复制server包和client包到hbase集群各节点的lib目录下
wangting@ops01:/opt/module/phoenix >cp phoenix-*.jar /opt/module/hbase/lib/
wangting@ops01:/opt/module/phoenix >scp phoenix-*.jar ops02:/opt/module/hbase/lib/
wangting@ops01:/opt/module/phoenix >scp phoenix-*.jar ops03:/opt/module/hbase/lib/# 添加环境变量
wangting@ops01:/opt/module/phoenix >sudo vim /etc/profile
#phoenix
export PHOENIX_HOME=/opt/module/phoenix
export PATH=$PATH:$PHOENIX_HOME/bin
wangting@ops01:/opt/module/phoenix >source /etc/profile
wangting@ops01:/opt/module/phoenix >for i in ops01 ops02 ops03 ;do ssh $i sudo netstat -tnlpu|grep 2181;done
tcp6 0 0 :::2181 :::* LISTEN 56644/java
tcp6 0 0 :::2181 :::* LISTEN 105183/java
tcp6 0 0 :::2181 :::* LISTEN 53772/java wangting@ops01:/opt/module/phoenix >cd /opt/module/hbase/conf/
# hbase配置文件增加命名空间映射和索引相关配置
wangting@ops01:/opt/module/hbase/conf >vim hbase-site.xml<!-- 支持HBase命名空间映射 --><property><name>phoenix.schema.isNamespaceMappingEnabled</name><value>true</value></property><!-- 支持索引预写日志编码 --><property><name>hbase.regionserver.wal.codec</name><value>org.apache.hadoop.hbase.regionserver.wal.IndexedWALEditCodec</value></property>wangting@ops01:/opt/module/hbase/conf >scp hbase-site.xml ops02:$PWD
wangting@ops01:/opt/module/hbase/conf >scp hbase-site.xml ops03:$PWD# 4. 将配置后的hbase-site.xml拷贝到phoenix的bin目录
wangting@ops01:/opt/module/hbase/conf >cp hbase-site.xml /opt/module/phoenix/bin/# 重启hbase集群
wangting@ops01:/opt/module/phoenix >stop-hbase.sh
wangting@ops01:/opt/module/phoenix >start-hbase.sh# 启动Phoenix 指定zookeeper集群
wangting@ops01:/home/wangting >sqlline.py ops01,ops02,ops03:2181
Setting property: [incremental, false]
Setting property: [isolation, TRANSACTION_READ_COMMITTED]
issuing: !connect jdbc:phoenix:ops01,ops02,ops03:2181 none none org.apache.phoenix.jdbc.PhoenixDriver
Connecting to jdbc:phoenix:ops01,ops02,ops03:2181
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/phoenix/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
22/08/17 18:01:30 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
22/08/17 18:01:34 WARN query.ConnectionQueryServicesImpl: Expected 5 system tables but found 6:[SYSTEM.CATALOG, SYSTEM.FUNCTION, SYSTEM.LOG, SYSTEM.MUTEX, SYSTEM.SEQUENCE, SYSTEM.STATS]
Connected to: Phoenix (version 5.0)
Driver: PhoenixEmbeddedDriver (version 5.0)
Autocommit status: true
Transaction isolation: TRANSACTION_READ_COMMITTED
Building list of tables and columns for tab-completion (set fastconnect to true to skip)...
133/133 (100%) Done
Done
sqlline version 1.2.0
0: jdbc:phoenix:ops01,ops02,ops03:2181>
0: jdbc:phoenix:ops01,ops02,ops03:2181> !tables
+------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------+
| TABLE_CAT | TABLE_SCHEM | TABLE_NAME | TABLE_TYPE | REMARKS | TYPE_NAME | SELF_REFERENCING_COL_NAME | REF_GENERATION | INDEX_STATE | IMMUTABLE_ROWS | SALT_BUCKETS | MULTI_TENANT | VIEW_STATEMENT | VIEW_TYPE | INDEX_TYPE | TRANSACTIONAL | IS_NAMES |
+------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------+
| | SYSTEM | CATALOG | SYSTEM TABLE | | | | | | false | null | false | | | | false | true |
| | SYSTEM | FUNCTION | SYSTEM TABLE | | | | | | false | null | false | | | | false | true |
| | SYSTEM | LOG | SYSTEM TABLE | | | | | | true | 32 | false | | | | false | true |
| | SYSTEM | SEQUENCE | SYSTEM TABLE | | | | | | false | null | false | | | | false | true |
| | SYSTEM | STATS | SYSTEM TABLE | | | | | | false | null | false | | | | false | true |
+------------+--------------+-------------+---------------+----------+------------+----------------------------+-----------------+--------------+-----------------+---------------+---------------+-----------------+------------+-------------+----------------+----------+
0: jdbc:phoenix:ops01,ops02,ops03:2181>
phoenix使用
phoenix创建表
在Phoenix中,我们可以使用类似于MySQL DDL的方式快速创建表
建表语法:
CREATE TABLE IF NOT EXISTS 表名 (
ROWKEY名称 数据类型 PRIMARY KEY
列蔟名.列名1 数据类型 NOT NULL,
列蔟名.列名2 数据类型 NOT NULL,
列蔟名.列名3 数据类型);
create table if not exists ORDER_DTL(ID varchar primary key,C1.STATUS varchar,C1.MONEY float,C1.PAY_WAY integer,C1.USER_ID varchar,C1.OPERATION_TIME varchar,C1.CATEGORY varchar
);
查看表结构信息
0: jdbc:phoenix:ops01,ops02,ops03:2181> !desc ORDER_DTL
+------------+--------------+-------------+-----------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------------+-------------------+--------------------+-----------------+
| TABLE_CAT | TABLE_SCHEM | TABLE_NAME | COLUMN_NAME | DATA_TYPE | TYPE_NAME | COLUMN_SIZE | BUFFER_LENGTH | DECIMAL_DIGITS | NUM_PREC_RADIX | NULLABLE | REMARKS | COLUMN_DEF | SQL_DATA_TYPE | SQL_DATETIME_SUB | CHAR_OCTET_LENGTH | ORDINAL_POSITIO |
+------------+--------------+-------------+-----------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------------+-------------------+--------------------+-----------------+
| | | ORDER_DTL | ID | 12 | VARCHAR | null | null | null | null | 0 | | | null | null | null | 1 |
| | | ORDER_DTL | STATUS | 12 | VARCHAR | null | null | null | null | 1 | | | null | null | null | 2 |
| | | ORDER_DTL | MONEY | 6 | FLOAT | null | null | null | null | 1 | | | null | null | null | 3 |
| | | ORDER_DTL | PAY_WAY | 4 | INTEGER | null | null | null | null | 1 | | | null | null | null | 4 |
| | | ORDER_DTL | USER_ID | 12 | VARCHAR | null | null | null | null | 1 | | | null | null | null | 5 |
| | | ORDER_DTL | OPERATION_TIME | 12 | VARCHAR | null | null | null | null | 1 | | | null | null | null | 6 |
| | | ORDER_DTL | CATEGORY | 12 | VARCHAR | null | null | null | null | 1 | | | null | null | null | 7 |
+------------+--------------+-------------+-----------------+------------+------------+--------------+----------------+-----------------+-----------------+-----------+----------+-------------+----------------+-------------------+--------------------+-----------------+
0: jdbc:phoenix:ops01,ops02,ops03:2181>
删除表
0: jdbc:phoenix:ops01,ops02,ops03:2181> drop table if exists ORDER_DTL;
No rows affected (1.601 seconds)
phoenix大小写
在HBase中,如果在列蔟、列名没有添加双引号。Phoenix会自动转换为大写
create table if not exists ORDER_DTL(id varchar primary key,C1.status varchar,C1.money double,C1.pay_way integer,C1.user_id varchar,C1.operation_time varchar,C1.category varchar
);0: jdbc:phoenix:ops01,ops02,ops03:2181> !desc ORDER_DTL
|COLUMN_NAME
|ID
|STATUS
|MONEY
|PAY_WAY
|USER_ID
|OPERATION_TIME
|CATEGORY
插入数据
在Phoenix中,插入并不是使用insert来实现的。而是 「upsert 」命令。它的功能为insert + update,与HBase中的put相对应。如果不存在则插入,否则更新。列表是可选的,如果不存在,值将按模式中声明的顺序映射到列。这些值必须计算为常量
语法:upsert into 表名(列蔟列名, xxxx, ) VALUES(XXX, XXX, XXX)
0: jdbc:phoenix:ops01,ops02,ops03:2181> UPSERT INTO ORDER_DTL VALUES('000001', '已提交', 4070, 1, '4944191', '2020-04-25 12:09:16', '手机');
1 row affected (0.249 seconds)
数据查询
与标准SQL一样,Phoenix也是使用select语句来实现数据的查询
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL;
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000001 | 已提交 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
+---------+---------+---------+----------+----------+----------------------+-----------+
1 row selected (0.162 seconds)
数据更新
在Phoenix中,更新数据也是使用UPSERT。语法格式如下:
UPSERT INTO 表名(列名, …) VALUES(对应的值, …);
将ORDER_DTL表ID为’000001’的订单状态修改为已付款
0: jdbc:phoenix:ops01,ops02,ops03:2181> UPSERT INTO ORDER_DTL(ID, STATUS) VALUES ('000001', '已付款');
1 row affected (0.014 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL;
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000001 | 已付款 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
+---------+---------+---------+----------+----------+----------------------+-----------+
1 row selected (0.083 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181>
条件查询
0: jdbc:phoenix:ops01,ops02,ops03:2181> UPSERT INTO ORDER_DTL VALUES('000002', '已提交', 8888, 1, '9988776', '2222-02-22 12:09:16', '激光枪');
1 row affected (0.015 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL;
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000001 | 已付款 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
| 000002 | 已提交 | 8888.0 | 1 | 9988776 | 2222-02-22 12:09:16 | 激光枪 |
+---------+---------+---------+----------+----------+----------------------+-----------+
2 rows selected (0.08 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> SELECT * FROM ORDER_DTL WHERE "ID" = '000002';
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000002 | 已提交 | 8888.0 | 1 | 9988776 | 2222-02-22 12:09:16 | 激光枪 |
+---------+---------+---------+----------+----------+----------------------+-----------+
1 row selected (0.054 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> SELECT * FROM ORDER_DTL WHERE "OPERATION_TIME" > '2022-08-18 00:00:00';
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000002 | 已提交 | 8888.0 | 1 | 9988776 | 2222-02-22 12:09:16 | 激光枪 |
+---------+---------+---------+----------+----------+----------------------+-----------+
1 row selected (0.069 seconds)
条件删除
0: jdbc:phoenix:ops01,ops02,ops03:2181> DELETE FROM ORDER_DTL WHERE "ID" = '000002';
1 row affected (0.01 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL;
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000001 | 已付款 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
+---------+---------+---------+----------+----------+----------------------+-----------+
1 row selected (0.057 seconds)
导入测试数据(执行sql脚本)
使用phoenix自带的psql.py工具
# 编写sql测试数据
wangting@ops01:/home/wangting >cd /home/wangting/hbase/
wangting@ops01:/home/wangting/hbase >vim load_phoenix.sql
UPSERT INTO "ORDER_DTL" VALUES('000002','已提交',4070,1,'4944191','2020-04-25 12:09:16','手机;');
UPSERT INTO "ORDER_DTL" VALUES('000003','已完成',4350,1,'1625615','2020-04-25 12:09:37','家用电器;;电脑;');
UPSERT INTO "ORDER_DTL" VALUES('000004','已提交',6370,3,'3919700','2020-04-25 12:09:39','男装;男鞋;');
UPSERT INTO "ORDER_DTL" VALUES('000005','已付款',6370,3,'3919700','2020-04-25 12:09:44','男装;男鞋;');
UPSERT INTO "ORDER_DTL" VALUES('000006','已提交',9380,1,'2993700','2020-04-25 12:09:41','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000007','已付款',9380,1,'2993700','2020-04-25 12:09:46','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000008','已完成',6400,2,'5037058','2020-04-25 12:10:13','数码;女装;');
UPSERT INTO "ORDER_DTL" VALUES('000009','已付款',280,1,'3018827','2020-04-25 12:09:53','男鞋;汽车;');
UPSERT INTO "ORDER_DTL" VALUES('000010','已完成',5600,1,'6489579','2020-04-25 12:08:55','食品;家用电器;');
UPSERT INTO "ORDER_DTL" VALUES('000011','已付款',5600,1,'6489579','2020-04-25 12:09:00','食品;家用电器;');
UPSERT INTO "ORDER_DTL" VALUES('000012','已提交',8340,2,'2948003','2020-04-25 12:09:26','男装;男鞋;');
UPSERT INTO "ORDER_DTL" VALUES('000013','已付款',8340,2,'2948003','2020-04-25 12:09:30','男装;男鞋;');
UPSERT INTO "ORDER_DTL" VALUES('000014','已提交',7060,2,'2092774','2020-04-25 12:09:38','酒店;旅游;');
UPSERT INTO "ORDER_DTL" VALUES('000015','已提交',640,3,'7152356','2020-04-25 12:09:49','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000016','已付款',9410,3,'7152356','2020-04-25 12:10:01','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000017','已提交',9390,3,'8237476','2020-04-25 12:10:08','男鞋;汽车;');
UPSERT INTO "ORDER_DTL" VALUES('000018','已提交',7490,2,'7813118','2020-04-25 12:09:05','机票;文娱;');
UPSERT INTO "ORDER_DTL" VALUES('000019','已付款',7490,2,'7813118','2020-04-25 12:09:06','机票;文娱;');
UPSERT INTO "ORDER_DTL" VALUES('000020','已付款',5360,2,'5301038','2020-04-25 12:08:50','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000021','已提交',5360,2,'5301038','2020-04-25 12:08:53','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000022','已取消',5360,2,'5301038','2020-04-25 12:08:58','维修;手机;');
UPSERT INTO "ORDER_DTL" VALUES('000023','已付款',6490,0,'3141181','2020-04-25 12:09:22','食品;家用电器;');
UPSERT INTO "ORDER_DTL" VALUES('000024','已付款',3820,1,'9054826','2020-04-25 12:10:04','家用电器;;电脑;');
UPSERT INTO "ORDER_DTL" VALUES('000025','已提交',4650,2,'5837271','2020-04-25 12:08:52','机票;文娱;');
UPSERT INTO "ORDER_DTL" VALUES('000026','已付款',4650,2,'5837271','2020-04-25 12:08:57','机票;文娱;');wangting@ops01:/home/wangting/hbase >pwd
/home/wangting/hbase
wangting@ops01:/home/wangting/hbase >ls -l /home/wangting/hbase/load_phoenix.sql
-rw-rw-r-- 1 wangting wangting 2773 Aug 18 09:19 /home/wangting/hbase/load_phoenix.sql
# 如果配置了环境变量,执行时也可以不带全路径
wangting@ops01:/home/wangting >ls -l /opt/module/phoenix/bin/psql.py
-rwxr-xr-x 1 wangting wangting 2739 Jun 27 2018 /opt/module/phoenix/bin/psql.py
wangting@ops01:/home/wangting >/opt/module/phoenix/bin/psql.py /home/wangting/hbase/load_phoenix.sql
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/opt/module/phoenix/phoenix-5.0.0-HBase-2.0-client.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/opt/module/hadoop-3.1.3/share/hadoop/common/lib/slf4j-log4j12-1.7.30.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
22/08/18 09:24:01 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable
1 row upserted
Time: 0.061 sec(s)1 row upserted
Time: 0.009 sec(s)
...
...
...
1 row upserted
Time: 0.005 sec(s)
# 进入phoenix命令行验证
wangting@ops01:/home/wangting >
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL;
+---------+---------+---------+----------+----------+----------------------+------------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+------------+
| 000001 | 已付款 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
| 000002 | 已提交 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机; |
| 000003 | 已完成 | 4350.0 | 1 | 1625615 | 2020-04-25 12:09:37 | 家用电器;;电脑; |
| 000004 | 已提交 | 6370.0 | 3 | 3919700 | 2020-04-25 12:09:39 | 男装;男鞋; |
| 000005 | 已付款 | 6370.0 | 3 | 3919700 | 2020-04-25 12:09:44 | 男装;男鞋; |
| 000006 | 已提交 | 9380.0 | 1 | 2993700 | 2020-04-25 12:09:41 | 维修;手机; |
| 000007 | 已付款 | 9380.0 | 1 | 2993700 | 2020-04-25 12:09:46 | 维修;手机; |
| 000008 | 已完成 | 6400.0 | 2 | 5037058 | 2020-04-25 12:10:13 | 数码;女装; |
| 000009 | 已付款 | 280.0 | 1 | 3018827 | 2020-04-25 12:09:53 | 男鞋;汽车; |
| 000010 | 已完成 | 5600.0 | 1 | 6489579 | 2020-04-25 12:08:55 | 食品;家用电器; |
| 000011 | 已付款 | 5600.0 | 1 | 6489579 | 2020-04-25 12:09:00 | 食品;家用电器; |
| 000012 | 已提交 | 8340.0 | 2 | 2948003 | 2020-04-25 12:09:26 | 男装;男鞋; |
| 000013 | 已付款 | 8340.0 | 2 | 2948003 | 2020-04-25 12:09:30 | 男装;男鞋; |
| 000014 | 已提交 | 7060.0 | 2 | 2092774 | 2020-04-25 12:09:38 | 酒店;旅游; |
| 000015 | 已提交 | 640.0 | 3 | 7152356 | 2020-04-25 12:09:49 | 维修;手机; |
| 000016 | 已付款 | 9410.0 | 3 | 7152356 | 2020-04-25 12:10:01 | 维修;手机; |
| 000017 | 已提交 | 9390.0 | 3 | 8237476 | 2020-04-25 12:10:08 | 男鞋;汽车; |
| 000018 | 已提交 | 7490.0 | 2 | 7813118 | 2020-04-25 12:09:05 | 机票;文娱; |
| 000019 | 已付款 | 7490.0 | 2 | 7813118 | 2020-04-25 12:09:06 | 机票;文娱; |
| 000020 | 已付款 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:50 | 维修;手机; |
| 000021 | 已提交 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:53 | 维修;手机; |
| 000022 | 已取消 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:58 | 维修;手机; |
| 000023 | 已付款 | 6490.0 | 0 | 3141181 | 2020-04-25 12:09:22 | 食品;家用电器; |
| 000024 | 已付款 | 3820.0 | 1 | 9054826 | 2020-04-25 12:10:04 | 家用电器;;电脑; |
| 000025 | 已提交 | 4650.0 | 2 | 5837271 | 2020-04-25 12:08:52 | 机票;文娱; |
| 000026 | 已付款 | 4650.0 | 2 | 5837271 | 2020-04-25 12:08:57 | 机票;文娱; |
+---------+---------+---------+----------+----------+----------------------+------------+
26 rows selected (0.17 seconds)
分页数据查询
使用limit和offset可以快速进行分页
limit表示每页多少条记录,offset表示从第几条记录开始查起
-- 第一页
select * from ORDER_DTL limit 10 offset 0;
-- 第二页
-- offset从10开始
select * from ORDER_DTL limit 10 offset 10;
-- 第三页
select * from ORDER_DTL limit 10 offset 20;# select * from ORDER_DTL limit 10 offset 0;当offset为不定义,默认为0开始
# 第一页数据
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL limit 10 offset 0;
+---------+---------+---------+----------+----------+----------------------+------------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+------------+
| 000001 | 已付款 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机 |
| 000002 | 已提交 | 4070.0 | 1 | 4944191 | 2020-04-25 12:09:16 | 手机; |
| 000003 | 已完成 | 4350.0 | 1 | 1625615 | 2020-04-25 12:09:37 | 家用电器;;电脑; |
| 000004 | 已提交 | 6370.0 | 3 | 3919700 | 2020-04-25 12:09:39 | 男装;男鞋; |
| 000005 | 已付款 | 6370.0 | 3 | 3919700 | 2020-04-25 12:09:44 | 男装;男鞋; |
| 000006 | 已提交 | 9380.0 | 1 | 2993700 | 2020-04-25 12:09:41 | 维修;手机; |
| 000007 | 已付款 | 9380.0 | 1 | 2993700 | 2020-04-25 12:09:46 | 维修;手机; |
| 000008 | 已完成 | 6400.0 | 2 | 5037058 | 2020-04-25 12:10:13 | 数码;女装; |
| 000009 | 已付款 | 280.0 | 1 | 3018827 | 2020-04-25 12:09:53 | 男鞋;汽车; |
| 000010 | 已完成 | 5600.0 | 1 | 6489579 | 2020-04-25 12:08:55 | 食品;家用电器; |
+---------+---------+---------+----------+----------+----------------------+------------+
10 rows selected (0.079 seconds)
# 第二页数据
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL limit 10 offset 10;
+---------+---------+---------+----------+----------+----------------------+-----------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+-----------+
| 000011 | 已付款 | 5600.0 | 1 | 6489579 | 2020-04-25 12:09:00 | 食品;家用电器; |
| 000012 | 已提交 | 8340.0 | 2 | 2948003 | 2020-04-25 12:09:26 | 男装;男鞋; |
| 000013 | 已付款 | 8340.0 | 2 | 2948003 | 2020-04-25 12:09:30 | 男装;男鞋; |
| 000014 | 已提交 | 7060.0 | 2 | 2092774 | 2020-04-25 12:09:38 | 酒店;旅游; |
| 000015 | 已提交 | 640.0 | 3 | 7152356 | 2020-04-25 12:09:49 | 维修;手机; |
| 000016 | 已付款 | 9410.0 | 3 | 7152356 | 2020-04-25 12:10:01 | 维修;手机; |
| 000017 | 已提交 | 9390.0 | 3 | 8237476 | 2020-04-25 12:10:08 | 男鞋;汽车; |
| 000018 | 已提交 | 7490.0 | 2 | 7813118 | 2020-04-25 12:09:05 | 机票;文娱; |
| 000019 | 已付款 | 7490.0 | 2 | 7813118 | 2020-04-25 12:09:06 | 机票;文娱; |
| 000020 | 已付款 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:50 | 维修;手机; |
+---------+---------+---------+----------+----------+----------------------+-----------+
10 rows selected (0.063 seconds)
# 第三页数据
0: jdbc:phoenix:ops01,ops02,ops03:2181> select * from ORDER_DTL limit 10 offset 20;
+---------+---------+---------+----------+----------+----------------------+------------+
| ID | STATUS | MONEY | PAY_WAY | USER_ID | OPERATION_TIME | CATEGORY |
+---------+---------+---------+----------+----------+----------------------+------------+
| 000021 | 已提交 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:53 | 维修;手机; |
| 000022 | 已取消 | 5360.0 | 2 | 5301038 | 2020-04-25 12:08:58 | 维修;手机; |
| 000023 | 已付款 | 6490.0 | 0 | 3141181 | 2020-04-25 12:09:22 | 食品;家用电器; |
| 000024 | 已付款 | 3820.0 | 1 | 9054826 | 2020-04-25 12:10:04 | 家用电器;;电脑; |
| 000025 | 已提交 | 4650.0 | 2 | 5837271 | 2020-04-25 12:08:52 | 机票;文娱; |
| 000026 | 已付款 | 4650.0 | 2 | 5837271 | 2020-04-25 12:08:57 | 机票;文娱; |
+---------+---------+---------+----------+----------+----------------------+------------+
6 rows selected (0.061 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181>
创建视图
0: jdbc:phoenix:ops01,ops02,ops03:2181> CREATE VIEW VIEW_ORDER_DTL AS SELECT * FROM ORDER_DTL;
No rows affected (0.063 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> select count(*) from VIEW_ORDER_DTL;
+-----------+
| COUNT(1) |
+-----------+
| 26 |
+-----------+
1 row selected (0.047 seconds)
0: jdbc:phoenix:ops01,ops02,ops03:2181> select count(*) from ORDER_DTL;
+-----------+
| COUNT(1) |
+-----------+
| 26 |
+-----------+
1 row selected (0.015 seconds)
指定分区压缩格式建表
# ===按条件建表===
# 按照用户ID来分区,一共4个分区。并指定数据的压缩格式为GZ
create table if not exists ORDER_DTL_NEW("id" varchar primary key,C1."status" varchar,C1."money" float,C1."pay_way" integer,C1."user_id" varchar,C1."operation_time" varchar,C1."category" varchar
)
CONPRESSION='GZ'
SPLIT ON ('3','5','7');# 加盐指定数量分区并指定数据的压缩格式
create table if not exists ORDER_DTL_NEWWW("id" varchar primary key,C1."status" varchar,C1."money" float,C1."pay_way" integer,C1."user_id" varchar,C1."operation_time" varchar,C1."category" varchar
)
CONPRESSION='GZ', SALT_BUCKETS=10;
phoenix索引介绍
索引-全局索引
- 全局索引适用于读多写少业务
- 全局索引绝大多数负载都发生在写入时,当构建了全局索引时,Phoenix会拦截写入(DELETE、UPSERT值和UPSERT SELECT)上的数据表更新,构建索引更新,同时更新所有相关的索引表,开销较大
- 读取时,Phoenix将选择最快能够查询出数据的索引表。默认情况下,除非使用Hint,如果SELECT查询中引用了其他非索引列,该索引是不会生效的
- 全局索引一般和覆盖索引搭配使用,读的效率很高,但写入效率会受影响
创建语法:
CREATE INDEX 索引名称 ON 表名 (列名1, 列名2, 列名3...)
索引-本地索引
- 本地索引适合写操作频繁,读相对少的业务
- 当使用SQL查询数据时,Phoenix会自动选择是否使用本地索引查询数据
- 在本地索引中,索引数据和业务表数据存储在同一个服务器上,避免写入期间的其他网络开销
- 在Phoenix 4.8.0之前,本地索引保存在一个单独的表中,在Phoenix 4.8.1中,本地索引的数据是保存在一个影子列蔟中
- 本地索引查询即使SELECT引用了非索引中的字段,也会自动应用索引的
- [注意]-创建表的时候指定了SALT_BUCKETS,是不支持本地索引的
创建语法:
CREATE local INDEX 索引名称 ON 表名 (列名1, 列名2, 列名3...)
索引-覆盖索引
Phoenix提供了覆盖的索引,可以不需要在找到索引条目后返回到主表。Phoenix可以将关心的数据捆绑在索引行中,从而节省了读取时间的开销
例如,以下语法将在v1和v2列上创建索引,并在索引中包括v3列,也就是通过v1、v2就可以直接把数据查询出来。
创建语法:
CREATE INDEX my_index ON my_table (v1,v2) INCLUDE(v3)
索引-函数索引
函数索引(4.3和更高版本)可以支持在列上创建索引,还可以基于任意表达式上创建索引。然后,当查询使用该表达式时,可以使用索引来检索结果,而不是数据表。例如,可以在UPPER(FIRST_NAME||‘ ’||LAST_NAME)上创建一个索引,这样将来搜索两个名字拼接在一起时,索引依然可以生效
创建语法:
-- 创建索引
CREATE INDEX UPPER_NAME_IDX ON EMP (UPPER(FIRST_NAME||' '||LAST_NAME))
-- 以下查询会走索引
SELECT EMP_ID FROM EMP WHERE UPPER(FIRST_NAME||' '||LAST_NAME)='JOHN DOE'
HBase工作机制
hbase读取数据流程
- 客户端拿到一个rowkey(首先得要知道这个rowkey存在哪个region中)
- 根据zk获取hbase:meta表,这个表中存放了region的信息,根据namespace、表名,就可以根据rowkey查看是否匹配某个region的startkey、endkey,返回region的信息
- 还需要查询region是在哪个HRegionServer(因为我们是不知道region会在存在什么地方的)
- 读取Store
- 优先读取写缓存(MemStore)
- 读取BlockCache(LRUBlockCache、BucketBlockCache)
- 再读取HFile
hbase数据存储流程
HBase的数据存储过程是分为几个阶段的。写入的过程与HBase的LSM结构对应。
为了提高HBase的写入速度,数据都是先写入到MemStore(内存)结构中,V2.0 MemStore也会进行Compaction
MemStore写到一定程度(默认128M),由后台程序将MemStore的内容flush刷写到HDFS中的StoreFile
数据量较大时,会产生很多的StoreFile。这样对高效读取不利,HBase会将这些小的StoreFile合并,一般3-10个文件合并成一个更大的StoreFile
写入MemStore
- Client访问zookeeper,从ZK中找到meta表的region位置
- 读取meta表中的数据,根据namespace、表名、rowkey获取对应的Region信息
- 通过刚刚获取的地址访问对应的RegionServer,拿到对应的表存储的RegionServer
- 去表所在的RegionServer进行数据的添加
- 查找对应的region,在region中寻找列族,先向MemStore中写入数据
MemStore溢写合并
- 当MemStore写入的值变多,触发溢写操作(flush),进行文件的溢写,成为一个StoreFile
- 当溢写的文件过多时,会触发文件的合并(Compact)操作,合并有两种方式(major,minor)
- 一旦MemStore达到128M时,则触发Flush溢出(Region级别)
- MemStore的存活时间超过1小时(默认),触发Flush溢写(RegionServer级别)
In-memory合并
In-memory合并是HBase 2.0之后添加的。它与默认的MemStore的区别:实现了在内存中进行compaction(合并)。
在CompactingMemStore中,数据是以段(Segment)为单位存储数据的。MemStore包含了多个segment
- 当数据写入时,首先写入到的是Active segment中(也就是当前可以写入的segment段)
- 在2.0之前,如果MemStore中的数据量达到指定的阈值时,就会将数据flush到磁盘中的一个StoreFile
- 2.0的In-memory compaction,active segment满了后,将数据移动到pipeline中。这个过程跟以前不一样,以前是flush到磁盘,而这次是将Active segment的数据,移到称为pipeline的内存当中。一个pipeline中可以有多个segment。而In-memory compaction会将pipeline的多个segment合并为更大的、更紧凑的segment,这就是compaction
- HBase会尽量延长CompactingMemStore的生命周期,以达到减少总的IO开销。当需要把CompactingMemStore flush到磁盘时,pipeline中所有的segment会被移动到一个snapshot中,然后进行合并后写入到HFile
StoreFile合并
- 当MemStore超过阀值的时候,就要flush到HDFS上生成一个StoreFile。因此随着不断写入,HFile的数量将会越来越多,根据前面所述,StoreFile数量过多会降低读性能
- 为了避免对读性能的影响,需要对这些StoreFile进行compact操作,把多个HFile合并成一个HFile
- compact操作需要对HBase的数据进行多次的重新读写,因此这个过程会产生大量的IO。可以看到compact操作的本质就是以IO操作换取后续的读性能的提高
Region管理
region分配
- 任何时刻,一个region只能分配给一个region server
- Master记录了当前有哪些可用的region server,以及当前哪些region分配给了哪些region server,哪些region还没有分配。当需要分配的新的region,并且有一个region server上有可用空间时,master就给这个region server发送一个装载请求,把region分配给这个region server。region server得到请求后,就开始对此region提供服务。
region server上线
- Master使用ZooKeeper来跟踪region server状态
- 当某个region server启动时
- 首先在zookeeper上的server目录下建立代表自己的znode
- 由于Master订阅了server目录上的变更消息,当server目录下的文件出现新增或删除操作时,master可以得到来自zookeeper的实时通知
- 一旦region server上线,master能马上得到消息
region server下线
- 当region server下线时,它和zookeeper的会话断开,ZooKeeper而自动释放代表这台server的文件上的独占锁
- Master即可以确定
- region server和zookeeper之间的网络断开
- region server挂掉
- 无论哪种情况,region server都无法继续为它的region提供服务了,此时master会删除server目录下代表这台region server的znode数据,并将这台region server的region分配给其它还活着的节点
region分裂
- 当region中的数据逐渐变大之后,达到某一个阈值,会进行裂变
- 一个region等分为两个region,并分配到不同的RegionServer
- 原本的Region会下线,新Split出来的两个Region会被HMaster分配到相应的HRegionServer上,使得原先1个Region的压力得以分流到2个Region上
- HBase只是增加数据,所有的更新和删除操作,都是在Compact阶段做的
- 用户写操作只需要进入到内存即可立即返回,从而保证I/O高性能读写
自动分区
- 如果初始时R=1,那么Min(128MB,10GB)=128MB,也就是说在第一个flush的时候就会触发分裂操作
- 当R=2的时候Min(22128MB,10GB)=512MB ,当某个store file大小达到512MB的时候,就会触发分裂
- 如此类推,当R=9的时候,store file 达到10GB的时候就会分裂,也就是说当R>=9的时候,store file 达到10GB的时候就会分裂
- split 点都位于region中row key的中间点
手动分区
在创建表的时候,就可以指定表分为多少个Region。默认一开始的时候系统会只向一个RegionServer写数据,系统不指定startRow和endRow,可以在运行的时候提前Split,提高并发写入
Master工作机器
master上线
Master启动进行以下步骤:
- 从zookeeper上获取唯一一个代表active master的锁,用来阻止其它master成为master
- 一般hbase集群中总是有一个master在提供服务,还有一个以上的‘master’在等待时机抢占它的位置。
- 扫描zookeeper上的server父节点,获得当前可用的region server列表
- 和每个region server通信,获得当前已分配的region和region server的对应关系
- 扫描.META.region的集合,计算得到当前还未分配的region,将他们放入待分配region列表
master下线
- 由于master只维护表和region的元数据,而不参与表数据IO的过程,master下线仅导致所有元数据的修改被冻结
- 无法创建删除表
- 无法修改表的schema
- 无法进行region的负载均衡
- 无法处理region 上下线
- 无法进行region的合并
- 唯一例外的是region的split可以正常进行,因为只有region server参与
- 表的数据读写还可以正常进行
- 因此master下线短时间内对整个hbase集群没有影响
- 从上线过程可以看到,master保存的信息全是可以冗余信息(都可以从系统其它地方收集到或者计算出来)
HBase常规优化
每个集群会有系统配置,社区一定会把一些通用的、适应性强的作为默认配置,有很多都是折中的配置。很多时候,出现问题的时候,我们要考虑优化。
- 通用优化
- 跟硬件有一定的关系,SSD、RAID(给NameNode使用RAID1架构,可以有一定容错能力)
- 操作系统优化
- 最大的开启文件数量(集群规模大之后,写入的速度很快,经常要Flush,会在操作系统上同时打开很多的文件读取)
- 最大允许开启的进程
- HDFS优化
- 副本数
- RPC的最大数量
- 开启的线程数
- HBase优化
- 配置StoreFile大小
- 预分区
- 数据压缩
- 设计ROWKEY
- 开启BLOOMFILER
- 2.X开启In-memory Compaction
- …
- JVM
- 调整堆内存大小
- 调整GC,并行GC,缩短GC的时间
HBase入门至进阶以及开发等知识梳理相关推荐
- linux运维基础进阶和shell入门,Linux运维工程师从基础到进阶:Shell变量知识梳理...
原标题:Linux运维工程师从基础到进阶:Shell变量知识梳理 Shell是一个用C语言编写的程序,它是用户使用Linux的桥梁.Shell既是一种命令语言,又是一种程序设计语言,涵盖的知识点多且杂 ...
- shell 输出数据到文本_Linux运维工程师从基础到进阶:Shell变量知识梳理
Shell是一个用C语言编写的程序,它是用户使用Linux的桥梁.Shell既是一种命令语言,又是一种程序设计语言,涵盖的知识点多且杂,却是云计算开发人员必须要掌握的技术之一.接下来的广州云计算培训小 ...
- @value 静态变量_Linux运维工程师从基础到进阶:Shell变量知识梳理
Shell是一个用C语言编写的程序,它是用户使用Linux的桥梁.Shell既是一种命令语言,又是一种程序设计语言,涵盖的知识点多且杂,却是云计算开发人员必须要掌握的技术之一.接下来的广州云计算培训小 ...
- 【软件开发底层知识修炼】十九 GDB调试从入门到熟练掌握超级详细实战教程学习目录
本文记录之前写过的5篇关于GDB快速学习的文章,从第一篇开始学习到最后一篇,保证可以从入门GDB调试到熟练掌握GDB调试的技巧. 学习交流加 个人qq: 1126137994 个人微信: liu112 ...
- 【算法入门到进阶】【学习目录】
2022-1-10 文章目录 2022-1-10 文章说明 第三章 STL和基本数据结构 3.1 容器 3.1.1 vector 3.1.2 栈 和 stack 3.1.3 队列 和 queue 3. ...
- flask web开发是前端还是后端_Flask Web开发实战:入门、进阶与原理解析 PDF 全格式版...
给大家带来的一篇关于Flask相关的电子书资源,介绍了关于Flask.Web.开发实战方面的内容,本书是由机械工业出版社出版,格式为PDF,资源大小12.2M,李辉编写,目前豆瓣.亚马逊.当当.京东等 ...
- 前端开发从入门到进阶完全指南,不用再迷茫前端要怎么学啦!
我经常会看到很多同学在学习前端的时候比较迷茫,不知道到底应该以怎样的学习路线来入门和进阶前端领域.每次遇到这种问题我也会分享一下自己的学习经验,但是发现这是一个问得非常多的一个共性问题. 作为程序员, ...
- 前端开发核心知识进阶
课程内容 开篇词:如何突破前端开发技术瓶颈 日本后现代主义作家村上春树写过一本富有哲理的书--<当我谈跑步时我谈些什么>. 书中,他谈到,跑步跟写作一样:都需要坚毅隐忍,追逐超越:都需要心 ...
- 《Flask Web开发实战:入门、进阶与原理解析》读书笔记
写在前面 学docker编排,有一个用Flask框架的Demo,感觉挺方便,所以学习下 基于<Flask Web开发实战:入门.进阶与原理解析>做的读书笔记 个人还是比较喜欢看书,看书的话 ...
最新文章
- 指定python 版本安装相关插件
- python表白代码照片墙-python入会生成照片墙 利用python生成照片墙代码
- Berkeley DB——Database
- RocketMQ-初体验RocketMQ(07)-使用API操作RocketMQ_顺序消息 ordermessage
- Could not connect to SMTP host: smtp.qq.com, port: 465, response: -1 clojure邮箱发送
- 初始化对于类与接口的异同点深入解析
- 史上最全的Maven Pom文件标签详解
- ReentrantReadWriteLock源码解读
- php执行js加密解密
- ESP32编译速度慢问题
- 高端游戏计算机配置单,2021年高端游戏电脑配置清单推荐
- pycharm永久激活
- 基于树莓派+STM32+OneNET云平台打造智能家居系统(一)硬件设计篇
- FWT(快速沃尔什变换)零基础详解qaq(ACM/OI)
- Dbeaver_ee使用mysql和mongodb
- JavaScript中如何严格的判断NaN
- VScode解决Backend TkAgg is interactive backend. Turning interactive mode on.
- 威名赫赫的“国防七子”,究竟有多牛!
- HUAWEI华为笔记本电脑MateBook 13(WRTB-WFE9L)原装出厂系统恢复原厂系统
- Unity 5 物理
热门文章
- iOS NSProxy 的简单介绍和使用
- 今天看了《海马记忆法》这本书
- 电脑版贝爷生存java在哪_我的世界:贝爷生存!史上最“难”生存模组,开局只有三滴血?...
- 跨境电商商家如何借助匿名浏览器进行网络爬虫?
- 哔哩哔哩 PC web player 快捷键
- 豆瓣电台接口中频道(channel)参数的对应表
- “知行-技术人的管理之路”书籍推荐
- 关于JAVA中request.getParameterMap与request.getReader()获取不到请求值的解决方案
- MASM官网下载地址
- java stream sum_Java中的DoubleStream sum()方法