大数据开发工程师-快速上手NoSQL数据库HBase-1


快速上手NoSQL数据库HBase-1

1 快速了解HBase

HBase简介

1
2
3
在前面我们学习过了Hive,一句话概括Hive就是一个数据仓库,更像一个传统意义上的SQL数据库,主要用作数据仓库数据分析,更加强调离线的数据分析,为公司各个业务部门提供数据支撑。

但是在我们大数据的处理过程中,不光需要进行OLAP的操作,在有些情况下,也经常需要对数据进行记录级别的更新、删除等操作,也就是OLTP(联机事务处理)的操作,这是Hive所办不到的,那么这个时候就不能用Hive来为我们处理OLTP的业务了,因为Hive不支持事务,那我们使用Oracle总可以吧,当然对于一定范围内的数据是OK,没有问题的,但是当数据量达到亿级别,甚至更高的时候,Oracle的性能也会下降的非常厉害,几乎就瘫掉了。
1
2
3
4
比如有这么一个例子:公司原来的业务系统使用的是Oracle,目前数据量大约10亿条,处理响应非常慢。为了平缓的迁移到大数据平台,那么决定使用hive替换oracle,这种方案怎么样?
这个方案不怎么样。
我们都知道Oracle是关系型数据库,强调的是事务,更适合OLTP的应用场景。而Hive是数据仓库,更强调的是在大数据基础上的强大的查询和分析能力,更适合于OLAP的应用场景,显然是无法来帮我们完成业务需求的。
既然传统的SQL无法解决我们的这种需求,那么就需要另辟蹊径了——NoSQL(Not only SQL),使用NoSQL数据库来满足我们的业务需求,在Hadoop生态圈中一个比较典型的NoSQL数据库就是HBase,从字面意思上来看就是Hadoop Database,Hadoop生态圈的数据库,可以提供这种OLTP的操作,解决我们刚才提出的问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
那么,HBase到底是一个怎么样的NoSQL数据库呢?我们来看一下。

1.HBase – Hadoop Database:是一个高可靠 、高性能、面向列、可伸缩的NoSQL数据库(Key-Value类型)
高可靠:指的是HBase集群支持多个主节点,多个从节点,集群可靠性比较高,并且HBase的数据是存储在HDFS上的,数据可靠性也比较高。
高性能:指的是HBase可以存储上亿或者十亿级别的数据,实现毫秒级别查询。
面向列:指的是HBase数据的存储方式,是按照列存储的。
可伸缩:指的是HBase集群可以方便的添加或者删除一个节点。

2.HBase使用HDFS作为其文件存储系统。
HBase数据库的数据是存储在HDFS上面的。

3.HBase支持对海量数据的增删改查。
这里大家可能会有一个疑问,HDFS是不支持修改删除的,为什么HBase也是基于HDFS存储数据的,却可以支持修改删除呢?
这是因为HBase自己封装了一个中间层,在中间层针对删除的数据会做一个特殊标记,这样在查询的时候就会过滤掉已经标记为删除状态的数据,这样间接支持了删除功能。

列式存储简介

1
2
3
4
5
6
传统的关系型数据库,如MySQL、Oracle等采用行存储的方式来存储数据
先来解释一下行存储的概念
咱们在MySQL中使用的select… where…语句,select后面的字段表示从行中查询哪些字段,where是过滤查询哪些行的。
MySQL存储的时候都是按行存储的,对于关系型数据库而言,按行存储查询起来效率是比较高的。

绝大多数传统意义上的RDBMS都采用行存储的方式来存储数据,基于此一般都适合SQL查询,并且适合一次性查询出所有列、适合范围查询的情况。
1
缺点是不适合查询少量字段的情况。就算是你只需要查询某一个列的数据,底层也需要读取这一行中所有列的数据,最后只过滤出来你需要的某个列,这是由它的底层数据结构而定的。
1
2
在基于行式存储的数据库中,数据是按照行数据为基础逻辑存储单元进行存储的,一行中的数据在存储介质中以连续存储形式存在。
列式存储是相对于行式存储来说的,在基于列式存储 的数据库中,数据是按照列为基础逻辑存储单元进行存储的,一列中的数据在存储介质中以连续存储形式存在。

列式存储的优点

1
2
行式数据库在做一些列分析时,必须将所有列的信息全部读取出来,而列式数据库由于其是按列存取,因此只需在特定列做I/O即可完成查询与分析,效率节省90%。
列式数据库在每列上还有专门的列压缩算法进一步提高数据库性能,这是行式数据库不具备的。

image-20230621162349232

1
2
Row-based(Row-based store):行式存储。
Column-based (Cloumn-based store):列式存储。
1
2
3
从这个图里面可以看出来:
行式存储数据库中的数据在底层存储的时候,每一行的数据作为一个基础逻辑存储单元进行存储。
列式存储数据库中的数据在底层存储的时候,每一列的数据作为一个基础逻辑存储单元进行存储。

HBase典型应用场景

image-20230621162607346

1
2
3
4
5
6
7
1.半结构化或非结构化数据
对于数据结构字段不够确定或杂乱无章,很难按一个概念去进行抽取的数据适合用HBase进行存储。
比如文章的tag(标签)信息,会不断的增加、删除。

解释:每个文章的标签信息是不一样的,并且标签也会经常变化,用户可能随时对文章的标签信息进行修改。
如果使用MySQL存储,可以选择把文章的所有标签信息通过逗号分割拼接成一个字符串存储到一个字段里面,这样存储的时候方便了,但是使用的时候比较麻烦,每次都需要解析这个字符串进行处理。
当然也可以选择在MySQL中将每一个标签都存储到一个独立的字段中,这样使用起来比较方便,但是这样表结构就需要经常变化了,可能会存在某一篇文章有10个标签信息(需要10个标签字段),另一篇文章只有1个标签信息的情况(只需要1个标签字段),这样表结构中至少需要增加10个标签字段,这样就会存在字段冗余的情况,额外浪费存储空间。
1
2
3
2.记录非常稀疏
RDBMS(关系型数据库)的每一行有多少列是固定的,值为null的列浪费了存储空间。
HBase中值为null的列不会被存储,这样既节省了空间又提高了读性能。
1
2
3
3.多版本数据
HBase中某一个列的值可以有任意数量的版本值,因此对于需要存储历史记录的数据,用HBase就非常方便了。
解释:Mysql上如果想要保存一个数据的多个历史版本数据的话只能存储多行记录,而HBase只需要针对某个列存储多个历史版本即可,通过时间戳可以定位历史版本的数据。
1
2
3
4
5
4.超大数据量
当数据量越来越大,RDBMS数据库撑不住了,就出现了读写分离策略,通过一个Master专门负责写操作,多个Slave负责读操作,服务器成本倍增。
随着压力增加,Master撑不住了,这时就要分库了,把关联不大的数据分开存储,一些join查询不能用了,需要借助中间层。
随着数据量的进一步增加,一个表的记录越来越大,查询就变得很慢,于是又得搞分表,比如按ID取模分成多个表以减少单个表的记录数。
经历过这些事的人都知道过程是多么的折腾。采用HBase就简单了,只需要加机器即可,HBase会自动水平切分扩展,跟Hadoop的无缝集成保障了其数据可靠性(HDFS)和海量数据分析的高性能(MapReduce)。

HBase应用案例

image-20230621163320045

1
2
3
4
5
6
7
8
9
淘宝在2011年之前所有的后端持久化存储基本上都是在MySQL上进行的,MySQL由于开源,并且生态系统良好,本身拥有分库分表等多种解决方案,因此很长一段时间内都可以满足淘宝大量业务的需求。
但是由于业务的多样化发展,有越来越多的业务系统的需求开始发生了变化。一般来说有以下几类变化:
1.数据量变得越来越多,事实上现在淘宝几乎任何一个与用户相关的在线业务的数据量都在亿级别,每日系统调用次数从亿到百亿都有,且历史数据不能轻易删除。这需要有一个海量分布式文件系统,能对TB级甚至PB级别的数据提供在线服务
2.数据量的增长很快且不一定能准确预计,大多数应用系统从上线起在一段时间内数据量都呈很快的上升趋势,因此从成本的角度考虑对系统水平扩展能力有比较强烈的需求,且不希望存在单点制约
3.只需要简单的k-v读取,没有复杂的join等需求。但对系统的并发能力以及吞吐量、响应延时有非常高的需求,并且希望系统能够保持强一致性
4.通常系统的写入非常频繁,尤其是大量系统依赖于实时的日志分析
5.希望能够快速读取批量数据
6.Schema(表结构信息)灵活多变,可能经常更新列属性或新增列
7.希望能够方便使用,有良好且语义清晰的java接口
1
2
3
4
5
6
7
8
9
10
腾讯产品与技术的发展迅速,几乎任何一个与用户相关的在线业务的数据量都在亿级别,每日系统调用次数从亿到百亿,对海量数据的高效插入和快速读取变得越来越重要。
而传统关系型数据库模式固定、强调参照完整性、数据的逻辑与物理形式相对独立等,比较适用于中小规模的数据,但对于数据的规模和并发读写方面进行大规模扩展时,RDBMS性能会大大降低,分布式更为困难。
腾讯为什么会选择HBase?

1.高可靠性。HBase是运行在Hadoop上的NoSQL数据库,它的数据由HDFS做了数据冗余,具有高可靠性。
2.同时TDW(腾讯分布式数据仓库)五年的稳定运行,8800台的集群规模,证明了其服务于海量数据的能力。
3.高并发读写。使用日志文件(HLOG)和内存存储来将随机写转换成顺序写,保证稳定的数据插入速率;读写分离,这两种操作没有冲突。
4.优雅的伸缩性。HBase服务能力可以随服务器的增长而线性增长;HBase中表的数据表按Key 值范围自动分片,散布于不同的机器上,实现自动负载均衡;支持百亿行×百万列×上万个版本。
5.低延迟。数据按列存储,数据即索引。
6.低成本。历史数据不能轻易删除,数据量变得越来越多,尤其是对于日志类存储,写多读少。而HBase可构建在廉价的PC上,此外,HBase支持较多的压缩算法。

HBase的优缺点总结

HBase的优点

image-20230621183955700

1
2
3
解释:

支持动态列:支持随时动态增加或者减少列。例如:第一条数据有10列,第二条数据可以有100列,互相不影响,也不需要修改表结构。

HBase的缺点

image-20230621184122750

1
2
3
4
5
解释:

不支持SQL数据分析:HBase属于NoSQL数据库,所以不支持常见的SQL语法。
不擅长多条件组合查询:HBase中根据多个列进行组合条件查询相当于全表扫描,所以效率不高。
不适合大范围扫描查询:大范围扫描效率不高,数据量过大时容易导致扫描超时或者失败。

HBase逻辑存储模型之名词解释

image-20230621184225370

1
2
3
4
5
6
7
8
9
10
解释:

-命名空间(Namespace):命名空间类似于MySQL中的Database。
-表(Table):表类似于MySQL中的Table。
-行(Row):行类似于MySQL中的行。
-行键(Rowkey):行键类似于MySQL中的主键。在MySQL中,主键不是必须的。在HBase中,行键在每一行中是必须存在的。HBase号称上亿条数据可以实现毫秒级查询,其实是需要根据Rowkey查询的,如果涉及其它列的组合查询,查询效率也一般。
-列族(ColumnFamily):列族在MySQL中没有对应的概念。在HBase中,列族是一批列的集合,在创建表的时候,列族必须定义。
-列(Column):列类似于在MySQL中的列。在HBase中创建表的时候,列不需要提前定义,也不能提前定义,后期在向表中添加数据的时候,动态指定列。
-时间戳(Timestamp ):默认插入值的时候会带有时间戳,是HBase自带的,不需要在表定义的时候指定。时间戳和值是一一对应的,时间戳的类型是64位整型。时间戳可以由 HBase (在数据写入时)自动赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由用户显式赋值,针对某个列,不同版本的数据按照时间戳倒序排序,即最新的数据排在最前面。HBase在查询的时候,默认返回列中最新版本的数据。
-数据类型(DataType):MySQL中,数据类型多种多样,常见的有int、varchar、date、datetime等等。在HBase中,数据类型只有一种,就是byte[](字节数组),不管是什么数据类型,在HBase中存储的时候统一都会转化为byte[]。
1
什么是逻辑存储模型呢?简单来说就是我们操作HBase的时候能够直观看到的一些东西

image-20230621184735696

1
2
3
4
5
6
这个图的意思表示在HBase中有一张表,表里面有2个列族,c1和c2
目前这个表里面有1条数据,这条数据的RowKey是 隔壁老王
其中列族c1里面只有1列,是age,列族c2里面有2列,car和house
针对列族c1中的age列的值而言,有4个历史版本:T1,T2,T3,T5。

想要定位某一列的值,流程是这样的:HBase Table --> RowKey --> ColumnFamily --> Column --> TimeStamp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
想要达到图中这个数据效果,需要对表执行以下这些步骤:

第一次向HBase表中添加数据,指定RowKey 为隔壁老王,指定列族c1中的age列,值为27,此时这一列的值会对应的产生一个时间戳 T1,表示数据的产生时间

注意:这里面的时间戳定义为T1只是为了看起来清晰,实际上时间戳的值就是我们平时接触到的毫秒级时间戳:1628582473928。

第二次向HBase表中添加数据,指定RowKey 为隔壁老王,指定列族c1中的age列,值为28,此时这一列的值会对应的产生一个时间戳 T2

第三次向HBase表中添加数据,指定RowKey 为隔壁老王,指定列族c1中的age列,值为29,此时这一列的值会对应的产生一个时间戳 T3

第四次向HBase表中添加数据,指定RowKey 为隔壁老王,指定列族c2中的car列,值为有车,此时这一列的值会对应的产生一个时间戳 T4

第五次向HBase表中添加数据,指定RowKey 为隔壁老王,指定列族c1中的age列,值为30,此时这一列的值会对应的产生一个时间戳 T5

第六次向HBase表中添加数据,指定RowKey 为隔壁老王,指定列族c2中的house列,值为有房,此时这一列的值会对应的产生一个时间戳 T6

第七次向HBase表中添加数据,指定RowKey 为隔壁老王,指定列族c2中的car列,值为没车(老王把车卖了),此时这一列的值会对应的产生一个时间戳 T7
经过这7步之后,数据就可以达到和图中一样的效果了。
1
2
3
4
5
HBase是 三维(三个维度) 有序存储的,是指Rowkey,Column key(Column Family和Column)和 TimeStamp 这三个维度是依照ASCII码表排序的。

先按Rowkey升序排序。Rowkey相同则按Column key升序排序。Rowkey、Column key相同则按Timestamp降序排序

如下图所示:

image-20230621185309314

1
2
3
4
5
6
这个图里面表示对HBase的表t1进行全表扫描,在扫描的时候会返回同一个列的最多2个历史版本数据(VERSIONS=>2)。
图中执行scan 't1',{VERSION=>2}(全表扫描) 命令之后,最终返回了3条数据,Rowkey是:stu001,stu002,stu003。
针对第1条数据stu001,它里面一共有3列:age,name、math。列族有2个:info和level。age和name属于info列族,math属于level列族。
这里的info:age显示了两次是因为age字段存储了2个历史版本的数据。
最终从这个图里面是可以看出来HBase的三维有序存储特性的。
首先按照Rowkey(stu001,stu002,stu003)升序排序,相同Rowkey(stu001)内按照Column key(info:age,info:name,level:math)升序排序,针对相同的Column key(info:age),则按照数据对应的Timestamp(1800690940131,1800690940039)降序排序。

2 快速上手使用HBase

HBase安装部署

1
2
3
4
5
HBase需要依赖于Hadoop(HDFS)和Zookeeper服务
Hadoop我们前面已经学习过了,Zookeeper的安装部署步骤在Kafka课程中已经讲过,所以在这就直接使用了。
HBase支持伪分布集群和分布式集群
在具体安装HBase集群之前,我们需要先确认JDK的版本和Hadoop的版本
HBase与JDK版本对应关系如下:

image-20230621185906640

1
HBase与Hadoop版本对应关系如下:

image-20230621185935961

1
2
3
4
5
6
7
8
9
10
11
咱们本套课程中JDK使用的是1.8版本,HBase的所有版本都是支持的。
Hadoop版本我们使用的是3.2.0,由图中内容可知,我们只能选择HBase2.2.x或者HBase-2.3.x
一般不建议选最新的那个版本,所以在这里我们选择HBase2.2.7版本。

HBase所有版本的下载地址:
https://archive.apache.org/dist/hbase/

HBase2.2.7版本的下载地址:
https://archive.apache.org/dist/hbase/2.2.7/hbase-2.2.7-bin.tar.gz

最终下载好的HBase安装包是这个:hbase-2.2.7-bin.tar.gz

分布式集群

1
2
3
4
5
6
7
8
在这里就直接演示分布式集群的安装步骤了,伪分布集群和分布式集群的安装基本上没有多少区别。

HBase集群也是支持主从架构的,在这计划使用bigdata01、02、03这三台机器。
建议把HBase的从节点和Hadoop集群的从节点部署在相同的机器上面,这样可以最大化利用数据本地化的特性。
所以最终的节点规划如下:
bigdata01 HMaster(HBase的主节点,主节点可以支持多个,实现HA)
bigdata02 HRegionserver(HBase的从节点)
bigdata03 HRegionserver(HBase的从节点)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
下面开始安装部署:
bigdata01、02、03这三台机器的基础环境是OK的,并且上面已经运行了一套Hadoop的一主两从的集群。

1:首先在bigdata01上进行操作,将HBase安装包上传到bigdata01的/data/soft目录中
[root@bigdata01 soft]# ll
-rw-r--r--. 1 root root 220469021 Oct 31 2020 hbase-2.2.7-bin.tar.gz

2:解压
[root@bigdata01 soft]# tar -zxvf hbase-2.2.7-bin.tar.gz

3:修改配置文件
首先修改hbase-env.sh,在文件末尾直接添加以下配置即可
[root@bigdata01 soft]# cd hbase-2.2.7/conf
[root@bigdata01 conf]# vi hbase-env.sh
....
export JAVA_HOME=/data/soft/jdk1.8
export HADOOP_HOME=/data/soft/hadoop-3.2.0
export HBASE_MANAGES_ZK=false
export HBASE_LOG_DIR=/data/hbase/logs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
接下来修改hbase-site.xml
hbase-site.xml中默认有3个配置参数,主要修改里面hbase.cluster.distributed和hbase.tmp.dir的值。
[root@bigdata01 conf]# vi hbase-site.xml
<!--是否为分布式模式部署,true表示分布式部署-->
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<!-- 本地文件系统tmp目录-->
<property>
<name>hbase.tmp.dir</name>
<value>/data/hbase/tmp</value>
</property>
<!-- 这个参数的值默认不变即可,默认就是false。在分布式情况下, 一定设置为false -->
<property>
<name>hbase.unsafe.stream.capability.enforce</name>
<value>false</value>
</property>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
前面这几个已有的参数修改完毕以后,还需要向hbase-site.xml中添加下面这些参数
[root@bigdata01 conf]# vi hbase-site.xml
.......
<!--设置HBase表数据,也就是HBase数据在hdfs上的存储根目录-->
<property>
<name>hbase.rootdir</name>
<value>hdfs://bigdata01:9000/hbase</value>
</property>
<!--zookeeper集群的URL配置,多个host中间用逗号隔开-->
<property>
<name>hbase.zookeeper.quorum</name>
<value>bigdata01,bigdata02,bigdata03</value>
</property>
<!--HBase在zookeeper上数据的根目录znode节点-->
<property>
<name>zookeeper.znode.parent</name>
<value>/hbase</value>
</property>
<!--设置zookeeper通信端口,不配置也可以,zookeeper默认就是2181-->
<property>
<name>hbase.zookeeper.property.clientPort</name>
<value>2181</value>
</property>

最后修改regionservers文件,在里面添加HBase从节点的主机名或者ip
[root@bigdata01 conf]# vi regionservers
bigdata02
bigdata03

注意:如果是使用一台机器安装HBase伪分布集群,只需要在regionservers文件中配置那一台机器的主机名即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
4:将bigdata01中修改完配置的HBase目录远程拷贝到bigdata02和bigdata03上
[root@bigdata01 soft]# scp -rq hbase-2.2.7 bigdata02:/data/soft/
[root@bigdata01 soft]# scp -rq hbase-2.2.7 bigdata03:/data/soft/

5:启动HBase集群
注意:在启动HBase集群之前一定要确保Hadoop集群和Zookeeper集群已经正常启动了。

先启动Hadoop集群
[root@bigdata01 hadoop-3.2.0]# sbin/start-all.sh

再启动Zookeeper集群
注意:Zookeeper伪分布和集群的安装部署步骤在Kafka课程中。
地址为:https://class.imooc.com/course/1695
[root@bigdata01 apache-zookeeper-3.5.8-bin]# bin/zkServer.sh start
[root@bigdata02 apache-zookeeper-3.5.8-bin]# bin/zkServer.sh start
[root@bigdata03 apache-zookeeper-3.5.8-bin]# bin/zkServer.sh start

最后在bigdata01上启动HBase集群
[root@bigdata01 hbase-2.2.7]# bin/start-hbase.sh

6:验证集群
在bigdata01上执行jps命令,会发现多了一个HMaster进程,这个就是HBase集群主节点中的进程
[root@bigdata01 hbase-2.2.7]# jps
3826 NameNode
5528 QuorumPeerMain
5736 HMaster
4093 SecondaryNameNode
4334 ResourceManager

然后在bigdata02上执行jps命令,会发现多了一个HRegionServer进程,这个就是HBase集群从节点中的进程
[root@bigdata02 ~]# jps
2631 QuorumPeerMain
2249 NodeManager
2139 DataNode
2715 HRegionServer

然后在bigdata03上执行jps命令,会发现多了一个HRegionServer进程,这个就是HBase集群从节点中的进程
[root@bigdata03 ~]# jps
2625 QuorumPeerMain
2250 NodeManager
2140 DataNode
2702 HRegionServer

如果发现HMaster进程和HRegionServer进程都在,说明HBase集群正常启动了

image-20230621190830287

1
2
3
4
5
6
7
HBase提供的有web界面,可以通过浏览器确认集群是否正常启动,端口默认是16010
http://bigdata01:16010/

7:停止HBase集群
[root@bigdata01 hbase-2.2.7]# bin/stop-hbase.sh

注意:在停止集群进程的时候,要先停HBase集群进程,再停止Zookeeper集群和Hadoop集群,否则HBase停止程序会一直卡住不动,这种情况就需要使用kill命令强制杀进程了。

HBase常用Shell命令

1
2
3
4
5
HBase集群启动之后,下面我们来操作一下HBase
HBase的使用在工作中主要分为两种形式

-在开发和调试阶段,我们会通过HBase自带的shell命令行去操作,可以执行创建表,添加数据,修改数据之类的操作,比较方便
-在程序上线运行阶段,需要通过代码来操作HBase,HBase提供的有JavaAPI可以使用
1
2
3
4
5
6
7
在这里我们先看一下HBase中常用的一些shell命令

在这里可以把HBase中的命令总结为三种

-基础命令
-DDL命令
-增删改查命令
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如何进入HBase的shell命令行?
执行hbase shell命令即可

[root@bigdata01 hbase-2.2.7]# bin/hbase shell
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/data/soft/hadoop-3.2.0/share/hadoop/common/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/data/soft/hbase-2.2.7/lib/client-facing-thirdparty/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory]
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.2.7, r0fc18a9056e5eb3a80fdbde916865607946c5195, 2021年 04月 11日 星期日 19:24:57 CST
Took 0.0021 seconds
hbase(main):001:0>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
为了后期方便操作HBase,建议在PATH中配置一下HBASE_HOME

[root@bigdata01 hbase-2.2.7]# vi /etc/profile
export JAVA_HOME=/data/soft/jdk1.8
export HADOOP_HOME=/data/soft/hadoop-3.2.0
export HBASE_HOME=/data/soft/hbase-2.2.7
export PATH=.:$JAVA_HOME/bin:$HADOOP_HOME/bin:$HADOOP_HOME/sbin:$HBASE
_HOME/bin:$PATH
[root@bigdata01 hbase-2.2.7]# source /etc/profile

此时就可以直接在任何目录下执行hbase shell命令了。

注意:如果命令输入错误,使用键盘上的退格键(backspace)可能会无法删除命令,此时需要确认SecureCRT中的这2个配置。

1.终端类型选择Linux

2.选中Backspace发送delete

image-20230621224114770

基础命令

1
2
3
4
命令	解释
status 查看集群状态
version 查看当前版本
whoami 查看当前用户
1
2
3
4
5
6
7
8
9
10
hbase(main):001:0> status
1 active master, 0 backup masters, 2 servers, 0 dead, 1.0000 average load
Took 0.9864 seconds
hbase(main):002:0> version
2.2.7, r0fc18a9056e5eb3a80fdbde916865607946c5195, 2021年 04月 11日 星期日 19:24:57 CST
Took 0.0002 seconds
hbase(main):003:0> whoami
root (auth:SIMPLE)
groups: root
Took 0.0085 seconds

DDL命令

1
2
3
4
5
6
7
8
9
命令	解释
create 创建表
list 列出所有表
disable / is_disabled 禁用表/验证表是否被禁用
enable / is_enabled 启用表/验证表是否已启用
desc 查看表的详细信息
alter 修改表结构
exists 验证表是否存在
drop / truncate 删除表/清空表(删除重建)
创建表
1
2
3
4
5
6
7
8
命令:create ‘表名’,‘列族1’,‘列族2’,'列族N’

注意:单引号不能少,只能使用单引号,不能使用双引号。

例子:create 'student','info','level'
解释:创建了一张名为student的表,表中有两个列族,分别是info和level。create后面的第一个参数是表名,后面的都是列族的名称

注意:创建表时不能指定列,只能指定列族。
列出所有表
1
2
3
4
5
6
hbase(main):005:0> list
TABLE
student
1 row(s)
Took 0.0300 seconds
=> ["student"]
禁用表/验证表是否被禁用
1
2
3
4
5
6
7
8
9
10
11
hbase(main):006:0> disable 'student'
Took 1.1489 seconds

验证表是否被禁用
命令:is_disabled '表名’
解释:返回true表示此表被禁用,此时这个表不能被操作,因为表中的数据是存在Region中的,当Region中的数据达到一定量级的时候会进行分裂,产生多个Region,多个Region会分到其它节点上面。这个时候,数据是不能被操作的,所以会有disabled这个状态。

hbase(main):008:0> is_disabled 'student'
true
Took 0.0145 seconds
=> 1
启用表/验证表是否已启用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
启用表
命令:enable '表名’
解释:当表被手工禁用之后,想要恢复使用,需要启动表

hbase(main):009:0> enable 'student'
Took 0.8177 seconds

验证表是否已启用
命令:is_enabled '表名’
解释:返回true表示表已启用,默认情况下,创建的新表都是启动状态。

hbase(main):010:0> is_enabled 'student'
true
Took 0.0212 seconds
=> true
查看表的详细信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
命令:desc ‘表名’ 或者 desc ‘表名’

hbase(main):013:0> desc 'student'
Table student is ENABLED
student
COLUMN FAMILIES DESCRIPTION
{NAME => 'info', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NE
W_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DA
TA_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 => 'level', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', N
EW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_D
ATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVE
R', 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'}

2 row(s)

QUOTAS
0 row(s)
Took 0.0815 seconds
修改表结构
修改列族(版本数)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
使用alter命令可以完成更改列族参数信息、增加列族、删除列族以及更改表等操作,在这里主要演示一下增加列族、修改列族和删除列族
1:修改列族
修改列族的参数信息,例如:修改列族的版本
通过desc命令可以看到student表中列族的版本个数:
NAME => 'info', VERSIONS => '1'
NAME => 'level', VERSIONS => '1'
这里显示的这两个列族版本个数都是1,说明这两个列族中的所有列的值只能保存最新的1份数据,如果想要保存level列族中所有列的最近3个数据历史版本,可以这样操作:

hbase(main):014:0> alter 'student',{NAME=>'level',VERSIONS=>'3'}
Updating all regions with the new schema...
1/1 regions updated.
Done.
Took 2.5768 seconds

此时查看一下student表中列族的信息,发现level这个列族的VERSIONS参数的值变成了3

hbase(main):015:0> desc 'student'
Table student is ENABLED
student
COLUMN FAMILIES DESCRIPTION
{NAME => 'info', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NE
W_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DA
TA_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 => 'level', VERSIONS => '3', EVICT_BLOCKS_ON_CLOSE => 'false', N
EW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_D
ATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVE
R', 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'}

2 row(s)

QUOTAS
0 row(s)
Took 0.0590 seconds
1
2
3
注意:
-修改已存在数据的列族的属性时,HBase需要对列族里所有的数据进行修改,如果数据量很大,则修改可能要消耗很长时间。
-这里的版本个数是和列族绑定的,不是和某一个列绑定的。
增加列族
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
2:增加列族
在已存在的表的基础之上增加列族,在这我们向student表中增加一个列族:about
hbase(main):016:0> alter 'student','about'
Updating all regions with the new schema...
1/1 regions updated.
Done.
Took 2.3725 seconds

此时查看student表中的列族信息,发现有3个列族了
hbase(main):018:0> desc 'student'
Table student is ENABLED
student
COLUMN FAMILIES DESCRIPTION
{NAME => 'about', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', N
EW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_D
ATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVE
R', 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 => 'info', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NE
W_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DA
TA_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 => 'level', VERSIONS => '3', EVICT_BLOCKS_ON_CLOSE => 'false', N
EW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_D
ATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVE
R', 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'}

3 row(s)

QUOTAS
0 row(s)
Took 0.0712 seconds
删除列族
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
3:删除列族
删除表中已有的列族,在这我们删除student中的about这个列族
hbase(main):020:0> alter 'student',{NAME=>'about',METHOD=>'delete'}
Updating all regions with the new schema...
1/1 regions updated.
Done.
Took 2.3361 seconds

此时再查看student中的列族信息,会发现就只有2个了
hbase(main):021:0> desc 'student'
Table student is ENABLED
student
COLUMN FAMILIES DESCRIPTION
{NAME => 'info', VERSIONS => '1', EVICT_BLOCKS_ON_CLOSE => 'false', NE
W_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_DA
TA_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 => 'level', VERSIONS => '3', EVICT_BLOCKS_ON_CLOSE => 'false', N
EW_VERSION_BEHAVIOR => 'false', KEEP_DELETED_CELLS => 'FALSE', CACHE_D
ATA_ON_WRITE => 'false', DATA_BLOCK_ENCODING => 'NONE', TTL => 'FOREVE
R', 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'}

2 row(s)

QUOTAS
0 row(s)
Took 0.1049 seconds
1
注意:HBase中的表至少要包含一个列族,因此当表中只有一个列族时,无法将其删除。
验证表是否存在
1
2
3
4
5
6
7
8
9
10
11
命令:exists '表名’
解释:表存在返回true,否则返回false

hbase(main):030:0> exists 'student'
Table student does exist
Took 0.0066 seconds
=> true
hbase(main):031:0> exists 'aaaa'
Table aaaa does not exist
Took 0.0112 seconds
=> false
删除表/清空表(删除重建)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
先创建一个表t1,包含1个列族info
hbase(main):032:0> create 't1','info'
Created table t1
Took 1.2597 seconds
=> Hbase::Table - t1

1:删除表
命令:drop ‘表名’
hbase(main):033:0> drop 't1'

ERROR: Table t1 is enabled. Disable it first.

For usage try 'help "drop"'

Took 0.0142 seconds

注意:这里提示删除表失败了,提示的是想要删除表,需要先禁用表。
所以在这先禁用此表
hbase(main):034:0> disable 't1'
Took 0.7576 seconds

重新删除表,此时删除成功了。
hbase(main):035:0> drop 't1'
清空表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
清空表其实包含了两步,删除+重建
命令:truncate ‘表名’

先创建表t2,包含1个列族info
hbase(main):037:0> create 't2','info'
Created table t2
Took 1.2576 seconds
=> Hbase::Table - t2

清空此表
hbase(main):038:0> truncate 't2'
Truncating 't2' table (it may take a while):
Disabling table...
Truncating table...
Took 3.0259 seconds

注意:在清空表的时候,会自动先禁用表。

增删改查命令(DML)

1
2
3
4
5
6
命令	解释
put 添加数据/修改数据
get 查看数据
count 查看表中数据总条数
scan 扫描表中的数据
delete / deleteall 删除数据
添加数据/修改数据
1
2
3
HBase中没有insert方法,它也属于key-value类型的NoSQL数据库,类似于HashMap这种数据结构,所以它提供了put方法添加数据
命令:put ‘表名’,‘Rowkey’,‘列族:列’,'value’
添加2条数据,rowkey分别为:jack和tom
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
hbase(main):001:0> put 'student','jack','info:sex','man'
Took 0.8415 seconds
hbase(main):002:0> put 'student','jack','info:age','22'
Took 0.0295 seconds
hbase(main):003:0> put 'student','jack','level:class','A'
Took 0.0300 seconds
hbase(main):004:0> put 'student','tom','info:sex','woman'
Took 0.0286 seconds
hbase(main):005:0> put 'student','tom','info:age','20'
Took 0.0275 seconds
hbase(main):006:0> put 'student','tom','level:class','B'
Took 0.0228 seconds

HBase中没有修改方法,所以重复执行put就是修改操作了。
所以put操作在执行的时候,如果指定的数据已经存在,则更新,否则就新增。
查看数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
在查看数据的时候有这么几种用法
命令:get ‘表名’,'Rowkey’
命令:get ‘表名’,‘Rowkey’,'列族’
命令:get ‘表名’,‘Rowkey’,‘列族:列’

查询student中rowkey等于jack的所有列族中的数据

hbase(main):007:0> get 'student','jack'
COLUMN CELL
info:age timestamp=1776767046323, value=22
info:sex timestamp=1776767042049, value=man
level:class timestamp=1776767049721, value=A
1 row(s)
Took 0.0800 seconds
1
2
3
4
5
6
7
查询student中rowkey等于jack的info列族中的age列的数据

hbase(main):009:0> get 'student','jack','info:age'
COLUMN CELL
info:age timestamp=1776767046323, value=22
1 row(s)
Took 0.0332 seconds
查看表中数据总条数
1
2
3
4
5
6
7
8
命令:count ‘表名’

统计指定表中的数据总数

hbase(main):010:0> count 'student'
2 row(s)
Took 0.1515 seconds
=> 2
扫描表中的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
命令:scan '表名’
扫描表中的所有数据

hbase(main):011:0> scan 'student'
ROW COLUMN+CELL
jack column=info:age, timestamp=1776767046323, value=22
jack column=info:sex, timestamp=1776767042049, value=man
jack column=level:class, timestamp=1776767049721, value=
A
tom column=info:age, timestamp=1776767056816, value=20
tom column=info:sex, timestamp=1776767053359, value=wom
an
tom column=level:class, timestamp=1776767060140, value=
B
2 row(s)
Took 0.0791 seconds
1
scan后面可以添加过滤条件,扫描满足条件的数据,后面再分析
删除数据
1
2
3
4
5
delete有这么几种用法:
命令:delete ‘表名’,‘Rowkey’,'列族:列’
命令:delete ‘表名’,‘Rowkey’,‘列族:列’,时间戳

注意:delete 操作并不会马上删除数据,只会将对应的数据打上删除标记,只有在HBase底层合并数据时,数据才会被真正删除。
1
2
3
删除指定Rowkey中指定列族中指定列的数据
hbase(main):017:0> delete 'student','jack','info:age'
Took 0.0161 seconds
1
2
3
删除指定Rowkey中指定列族中指定列中时间戳小于2的数据
hbase(main):018:0> delete 'student','jack','info:age',2
Took 0.0180 seconds
1
2
3
4
注意:delete 命令不能跨列族操作,如果需要删除表中的某一行数据,则需要使用 deleteall 命令,此时不需要指定列族和列的名称

hbase(main):019:0> deleteall 'student','jack'
Took 0.0182 seconds

HBase的namespace(命名空间)

1
2
3
HBase的命名空间相当于MySQL中的Database
HBase默认有2个命名空间:分别是hbase和default
其中hbase存放系统表,default是存放用户表
1
2
3
4
5
6
7
8
使用list_namespace命令可以查看所有的命名空间,我们创建的表默认在default这个命名空间里面

hbase(main):023:0> list_namespace
NAMESPACE
default
hbase
2 row(s)
Took 0.4732 seconds
1
2
3
4
5
6
7
8
9
10
11
可以选择创建一个新的namespace

hbase(main):029:0> create_namespace 'n1'
Took 0.3233 seconds
hbase(main):030:0> list_namespace
NAMESPACE
default
hbase
n1
3 row(s)
Took 0.0163 seconds
1
2
3
4
5
6
7
8
在创建表的时候可以选择创建到n1这个namespace中,如何实现呢?
使用这种格式即可:'命名空间名称:表名'
针对default这个命名空间,在使用的时候可以省略不写

hbase(main):031:0> create 'n1:t1','info','level'
Created table n1:t1
Took 2.3100 seconds
=> Hbase::Table - n1:t1
1
2
3
4
5
6
7
8
9
10
此时使用list查看所有的表

hbase(main):032:0> list
TABLE
student
t2
n1:t1
3 row(s)
Took 0.0083 seconds
=> ["student", "t2", "n1:t1"]
1
2
3
4
5
6
7
8
9
如果只想查看n1这个命名空间中的表,如何实现呢?
可以使用命令list_namespace_tables

hbase(main):033:0> list_namespace_tables 'n1'
TABLE
t1
1 row(s)
Took 0.0099 seconds
=> ["t1"]

HBase JavaAPI的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
创建maven项目:db_hbase
由于需要操作HBase,所以需要在pom.xml中添加hbase-client依赖

<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>2.2.7</version>
</dependency>

以及日志相关配置

<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.10</version>
</dependency>

在resources目录中添加log4j.properties文件,文件内容如下:
log4j.rootLogger=info,stdout

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c] [%p] - %m%n
1
2
在src/main/java目录中创建package:com.imooc.hbase
创建类:HBaseOp

对表中的数据进行增删改查(DML)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
首先需要获取HBase数据库连接
代码如下:

//获取配置
Configuration conf = HBaseConfiguration.create();
//指定HBase使用的zk的地址,多个用逗号隔开
conf.set("hbase.zookeeper.quorum", "bigdata01:2181,bigdata02:2181,bigdata03:2181");
//指定HBase在hdfs上的根目录
conf.set("hbase.rootdir","hdfs://bigdata01:9000/hbase");
//创建HBase连接,负责对HBase中数据的一些增删改查(DML操作)
Connection conn = ConnectionFactory.createConnection(conf);


注意:想要在哪台机器上执行代码操作HBase,一定要在这台机器上配置HBase集群所有节点的主机名和IP的映射关系
1
2
因为我们在连接HBase集群的时候在代码中指定的是zookeeper集群的地址信息,通过Zookeeper去查找HBase,HBase会在zookeeper里面存储自己的地址信息,存储的是主机名。
所以一定要在执行HBase代码的机器上配置HBase集群所有节点的主机名和IP的映射关系。
1
2
3
4
5
注意:代码在windows中执行的时候会提示缺少winutils.exe

[WARN] - Did not find winutils.exe: java.io.FileNotFoundException: java.io.FileNotFoundException: HADOOP_HOME and hadoop.home.dir are unset.

这个警告信息不影响代码执行,在windows中执行会有这个提示,属于正常,把代码打包提交到集群上执行的时候就不会提示这个了,所以不用处理即可。
添加数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//获取Table对象,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回put对象
Put put = new Put(Bytes.toBytes("laowang"));
//向put对象中指定列族、列、值
//put 'student','laowang','info:age','18'
put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("age"),Bytes.toBytes("18"));
//put 'student','laowang','info:sex','man'
put.addColumn(Bytes.toBytes("info"),Bytes.toBytes("sex"),Bytes.toBytes("man"));
//put 'student','laowang','level:class','A'
put.addColumn(Bytes.toBytes("level"),Bytes.toBytes("class"),Bytes.toBytes("A"));
//向表中添加数据
table.put(put);
//关闭table连接
table.close();
查询数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//获取Table对象,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回Get对象
Get get = new Get(Bytes.toBytes("laowang"));
//【可选】可以在这里指定要查询指定Rowkey数据哪些列族中的列
// 如果不指定,默认查询指定Rowkey所有列的内容
//get.addColumn(Bytes.toBytes("info"),Bytes.toBytes("age"));
//get.addColumn(Bytes.toBytes("info"),Bytes.toBytes("sex"));

Result result = table.get(get);
//如果不清楚HBase中到底有哪些列族和列,可以使用listCells()获取所有cell(单元格),cell对应的是某一个列的数据
List<Cell> cells = result.listCells();
for (Cell cell: cells) {
//注意:下面获取的信息都是字节类型的,可以通过new String(bytes)转为字符串
//列族
byte[] famaily_bytes = CellUtil.cloneFamily(cell);
//列
byte[] column_bytes = CellUtil.cloneQualifier(cell);
//值
byte[] value_bytes = CellUtil.cloneValue(cell);
System.out.println("列族:"+new String(famaily_bytes)+",列:"+new String(column_bytes)+",值:"+new String(value_bytes));
}
System.out.println("========================================================");
//如果明确知道HBase中有哪些列族和列,可以使用getValue(family, qualifier)直接获取指定列族中指定列的数据
byte[] age_bytes = result.getValue(Bytes.toBytes("info"),Bytes.toBytes("age"));
System.out.println("age列的值:"+new String(age_bytes));
//关闭table连接
table.close();
查询多版本数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//获取Table对象,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回Get对象
Get get = new Get(Bytes.toBytes("laowang"));
//读取cell中的所有历史版本数据,不设置此配置的时候默认只读取最新版本的数据
//可以通过get.readVersions(2)来指定获取多少个历史版本的数据
get.readAllVersions();

Result result = table.get(get);

//获取指定列族中指定列的所有历史版本数据,前提是要设置get.readAllVersions()或者get.readVersions(2),否则只会获取最新数据
List<Cell> columnCells = result.getColumnCells(Bytes.toBytes("info"), Bytes.toBytes("age"));
for (Cell cell :columnCells) {
byte[] value_bytes = CellUtil.cloneValue(cell);
long timestamp = cell.getTimestamp();
System.out.println("值为:"+new String(value_bytes)+",时间戳:"+timestamp);
}
//关闭table连接
table.close();
删除数据
1
2
3
4
5
6
7
8
9
10
//获取Table对象,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回Delete对象
Delete delete = new Delete(Bytes.toBytes("laowang"));
//【可选】可以在这里指定要删除指定Rowkey数据哪些列族中的列
//delete.addColumn(Bytes.toBytes("info"),Bytes.toBytes("age"));

table.delete(delete);
//关闭table连接
table.close();

创建和删除表(DDL)

1
2
3
4
想要操作表,也就是执行DDL操作,需要获取管理员权限,通过HBase获取

//获取管理权限,负责对HBase中的表进行操作(DDL操作)
Admin admin = conn.getAdmin();
创建表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//指定列族信息
ColumnFamilyDescriptor familyDesc1 = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("info"))
//在这里可以给列族设置一些属性
.setMaxVersions(3)//指定最多存储多少个历史版本数据
.build();
ColumnFamilyDescriptor familyDesc2 = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("level"))
//在这里可以给列族设置一些属性
.setMaxVersions(2)//指定最多存储多少个历史版本数据
.build();
ArrayList<ColumnFamilyDescriptor> familyList = new ArrayList<ColumnFamilyDescriptor>();
familyList.add(familyDesc1);
familyList.add(familyDesc2);

//获取TableDescriptor对象
TableDescriptor desc = TableDescriptorBuilder.newBuilder(TableName.valueOf("test"))//指定表名
.setColumnFamilies(familyList)//指定列族
.build();
//创建表
admin.createTable(desc);
删除表
1
2
3
//删除表,先禁用表
admin.disableTable(TableName.valueOf("test"));
admin.deleteTable(TableName.valueOf("test"));

完整代码

1
最后对代码进行抽取,便于查看,完整代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
package com.imooc.hbase;

import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
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 java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
* 操作HBase
* 表:创建、删除
* 数据:增、删、改、查
* Created by xuwei
*/
public class HBaseOp {
public static void main(String[] args) throws Exception {
//获取HBase数据库连接
Connection conn = getConn();
//添加数据
//put(conn);

//查询数据
//get(conn);

/**
* 查询多版本的数据
* 当列的值有多个历史版本的时候
*
* 修改列族info的最大历史版本存储数量
* alter 'student',{NAME=>'info',VERSIONS=>3}
*
* 然后再执行下面命令,向列族info中的age列中添加几次数据,实现多历史版本数据存储
* put 'student','laowang','info:age','19'
* put 'student','laowang','info:age','20'
*
*/
//getMoreVersion(conn);

//修改数据--同添加数据

//删除数据
//delete(conn);

//==============================分割线======================

//获取管理权限,负责对HBase中的表进行操作(DDL操作)
Admin admin = conn.getAdmin();
//创建表
//createTable(admin);

//删除表
//deleteTable(admin);

//关闭admin连接
admin.close();
//关闭连接
conn.close();

}

/**
* 删除表
* @param admin
* @throws IOException
*/
private static void deleteTable(Admin admin) throws IOException {
//删除表,先禁用表
admin.disableTable(TableName.valueOf("test"));
admin.deleteTable(TableName.valueOf("test"));
}

/**
* 创建表
* @param admin
* @throws IOException
*/
private static void createTable(Admin admin) throws IOException {
//指定列族信息
ColumnFamilyDescriptor familyDesc1 = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("info"))
//在这里可以给列族设置一些属性
.setMaxVersions(3)//指定最多存储多少个历史版本数据
.build();
ColumnFamilyDescriptor familyDesc2 = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("level"))
//在这里可以给列族设置一些属性
.setMaxVersions(2)//指定最多存储多少个历史版本数据
.build();
ArrayList<ColumnFamilyDescriptor> familyList = new ArrayList<ColumnFamilyDescriptor>();
familyList.add(familyDesc1);
familyList.add(familyDesc2);

//获取TableDescriptor对象
TableDescriptor desc = TableDescriptorBuilder.newBuilder(TableName.valueOf("test"))//指定表名
.setColumnFamilies(familyList)//指定列族
.build();
//创建表
admin.createTable(desc);
}

/**
* 删除数据
* @param conn
* @throws IOException
*/
private static void delete(Connection conn) throws IOException {
//获取Table,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回Delete对象
Delete delete = new Delete(Bytes.toBytes("laowang"));
//【可选】可以在这里指定要删除指定Rowkey数据哪些列族中的列
//delete.addColumn(Bytes.toBytes("info"),Bytes.toBytes("age"));

table.delete(delete);
//关闭table连接
table.close();
}

/**
* 查询多版本的数据
* @param conn
* @throws IOException
*/
private static void getMoreVersion(Connection conn) throws IOException {
//获取Table,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回Get对象
Get get = new Get(Bytes.toBytes("laowang"));
//读取cell中的所有历史版本数据,不设置此配置的时候默认只读取最新版本的数据
//可以通过get.readVersions(2)来指定获取多少个历史版本的数据
get.readAllVersions();

Result result = table.get(get);

//获取指定列族中指定列的所有历史版本数据,前提是要设置get.readAllVersions()或者get.readVersions(2),否则只会获取最新数据
List<Cell> columnCells = result.getColumnCells(Bytes.toBytes("info"), Bytes.toBytes("age"));
for (Cell cell: columnCells) {
byte[] value_bytes = CellUtil.cloneValue(cell);
long timestamp = cell.getTimestamp();
System.out.println("值为:"+new String(value_bytes)+",时间戳:"+timestamp);
}
//关闭table连接
table.close();
}

/**
* 查询数据
* @param conn
* @throws IOException
*/
private static void get(Connection conn) throws IOException {
//获取Table,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回Get对象
Get get = new Get(Bytes.toBytes("laowang"));
//【可选】可以在这里指定要查询指定Rowkey数据哪些列族中的列
//如果不指定,默认查询指定Rowkey所有列的内容
get.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"));
get.addColumn(Bytes.toBytes("info"), Bytes.toBytes("sex"));

Result result = table.get(get);
//如果不清楚HBase中到底有哪些列族和列,可以使用listCells()获取所有cell(单元格),cell对应的是某一列的数据
List<Cell> cells = result.listCells();
for (Cell cell : cells) {
//注意:下面获取的信息都是字节类型的,可以通过new String(bytes)转为字符串
//列族
byte[] famaily_bytes = CellUtil.cloneFamily(cell);
//列
byte[] column_bytes = CellUtil.cloneQualifier(cell);
//值
byte[] value_bytes = CellUtil.cloneValue(cell);
System.out.println("列族:"+new String(famaily_bytes)+",列:"+new String(column_bytes)+",值:"+new String(value_bytes));
}
System.out.println("==============================================");
//如果明确知道HBase中有哪些列族和列,可以使用getValue(family,qualifier)直接获取指定列族中指定列的数据
byte[] age_bytes = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age"));
System.out.println("age列的值:"+new String(age_bytes));
//关闭table连接
table.close();
}

/**
* 添加数据
*
* @param conn
* @throws IOException
*/
private static void put(Connection conn) throws IOException {
//获取Table,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回put对象
Put put = new Put(Bytes.toBytes("laowang"));
//向put对象中指定列族、列、值
//put 'student','laowang','info:age','18'
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("18"));
//put 'student','laowang','info:sex','man'
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("sex"), Bytes.toBytes("man"));
//put 'student','laowang','level:class','A'
put.addColumn(Bytes.toBytes("level"), Bytes.toBytes("class"), Bytes.toBytes("A"));
//向表中添加数据
table.put(put);
//关闭table连接
table.close();
}

/**
* 获取连接
*
* @return
* @throws IOException
*/
private static Connection getConn() throws IOException {
//获取配置
Configuration conf = HBaseConfiguration.create();
//指定HBase使用的zk的地址,多个用逗号隔开
conf.set("hbase.zookeeper.quorum", "bigdata01:2181,bigdata02:2181,bigdata03:2181");
//指定HBase在hdfs上的根目录
conf.set("hbase.rootdir", "hdfs://bigdata01:9000/hbase");
//创建HBase连接,负责对HBase中的数据的一些增删改查(DML操作)
return ConnectionFactory.createConnection(conf);
}
}

本文标题:大数据开发工程师-快速上手NoSQL数据库HBase-1

文章作者:TTYONG

发布时间:2023年06月13日 - 09:06

最后更新:2023年07月04日 - 18:07

原始链接:http://tianyong.fun/%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%BC%80%E5%8F%91%E5%B7%A5%E7%A8%8B%E5%B8%88-%E5%BF%AB%E9%80%9F%E4%B8%8A%E6%89%8BNoSQL%E6%95%B0%E6%8D%AE%E5%BA%93HBase-1.html

许可协议: 转载请保留原文链接及作者。

多少都是爱
0%