Hadoop NoSQL数据库HBase

一、简介

NoSQLNot Only SQL的缩写,泛指用来解决大数据相关问题而创建的数据库技术,NoSQL技术不会完全替代关系型数据库,而是关系型数据的一种补充。HBase是建立在Hadoop文件系统之上的分布式面向列的NoSQL数据库。它是一个开源项目,可横向扩展。HBase的数据模型,类似于谷歌的BigTable设计,可以快速随机访问海量半结构化数据,并利用了Hadoop的文件系统HDFS提供的容错能力。

二、HBase 原理

HBase 是NoSQL数据库的一种,与传统关系型数据库有很多区别,既可以存储结构化数据,也可以存储非结构化数据或半结构化数据。

2.1 HBase 概述

如果需要实时随机地访问超大规模数据集,就可以使用HBase这一Hadoop应用。HBase是一个在HDFS上开发的面向列的分布式数据库。
虽然数据库存储和检索的实现可以选择很多不同的策略,但是绝大多数解决办法,特别是关系型数据库技术的变种,不是为大规模可伸缩的分布式处理设计的。很多厂商提供了复制(replication)和分区(partitioning)解决方案,让数据库能够从单个节点上扩展出去,但是这些附加的技术大都属于事后的解决办法,而且非常难以安装和维护,并且这些解决办法常常要牺牲一些重要的关系型数据库管理系统(RDBMS)特性。
在一个扩展的RDBMS上,连接、复杂查询、触发器、视图以及外键约束这些功能或运行开销大,或根本无法用。HBase从另一个方向来解决可伸缩性的问题,它自底向上地进行构建,能够简单地通过增加节点来达到线性扩展的目的。
HBase并不是关系型数据库,它不支持SQL。但在特定的问题空间里,它能够做RDBMS不能做的事:在廉价硬件构成的集群上管理超大规模的稀疏表。
HBase是Apache的顶级开源项目,本质上是谷歌BigTable的开源山寨版本。建立的HDFS之上,提供高可靠性、高性能、列存储、可伸缩、实时读写的数据库系统,它介于NoSQL和RDBMS之间,仅能通过主键(row key)和主键的范围(range)来检索数据,仅支持单行事务(可通过Hive支持来实现多表join等复杂操作)。主要用来存储非结构化和半结构化的松散数据。

2.2 HBase 核心概念

HBase的数据存放在带标签的表中。表由行和列组成。表格的单元格(cell)由行和列的坐标交叉决定,是有版本的。默认情况下,版本号是自动分配的,为HBase插入单元格时的时间戳。单元格的内容是未解释的字节数组。HBase是一个稀疏、长期存储、多维度、排序的映射表。这张表的索引是行关键字、列关键字和时间戳。每个值是一个二进制的字节数组。

  1. 行关键字
    row key保存为字节数组,是用来检索记录的主键。可以是任意字符串(最大长度是64KB)。存储时,数据按照row key的字典序(byte order)排序存储。设计row key时,要充分利用排序存储这个特性,将经常一起读取的行存储放到一起。
  2. 列关键字
    列关键字由列族column family和列qualifier两部分组成。列族是表的schema元数据的一部分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如courses:history、courses:math都属于courses这个列族。有关联的数据应都放在一个列族里,否则将降低读写效率。目前HBase并不能很好地处理多个列族,建议最多使用两个列族。
  3. 时间戳
    HBase中通过row和columns确定的一个存储单元称为cell。每个cell都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是64位整型。时间戳可以由HBase在数据写入时自动赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。为了避免数据存在过多版本造成管理(包括存储和索引)负担,HBase提供了两种数据版本回收方式。一是保存数据的最后N个版本(比如3个),二是保存最近一段时间内的版本(比如最近7天)。用户可以针对每个列族进行设置。
  4. cell
    由{row key, column(=<family> + <label>), version}唯一确定的单元,cell中的数据是没有类型的,全部以字节码形式存储。

与NoSQL数据库一样,row key是用来检索记录的主键。访问HBase表中的行,只有3种方式。
(1)通过单个row key访问单条记录。
(2)通过row key的range指定检索范围。
(3)全表扫描。
物理上,所有的列族成员都一起存放在文件系统中。所以,虽然把HBase描述为一个面向列的存储器,但实际上更准确的说法是,HBase是个面向列族的存储器。由于调优和存储都是在列族这个层次进行的,所以最好使所有列族成员都有相同的访问模式(access pattern)和大小特征。简而言之,HBase表和RDBMS中的表类似,单元格有版本,行是排序的,而只要列族预先存在,客户端随时可以把列添加到列族中去。

HBase存储格式如下图所示
图片1

一个典型的RDBMS二维表格式如下所示
图片2

对应的数据在HBase中的存储格式如下所示
图片3

HBase自动把表水平划分成区域(region)。每个区域由表中行的子集构成。每个区域由它所属的表、它所包含的第一行及最后一行(不包括这行)来表示。
一开始,一个表只有一个区域。但是随着区域变大,等到它的大小超出设定的阈值时,便会在某行的边界上把表分成两个大小基本相同的新分区。在第一次划分之前,所有加载的数据都放在原始区域所在的服务器上。
随着表变大,区域的个数也会增加。区域是在HBase集群上分布数据的最小单位。用这种方式,一个因为太大而无法放在单台服务器上的表会被放到服务器集群上,其中每个节点都负责管理表所在区域的一个子集。表的加载也是使用这种方法把数据分布到各个节点。在线的所有区域按次序排列就构成了表的所有内容。

常规的HBase服务器部署结构如下图所示
图片4

各个角色的功能如下:
(1)Region:表中一部分数据组成的子集,当Region内的数据过多时能够自动分裂,过少时会合并。
(2)RegionServer:维护Master分配给它的Region,处理对这些Region的IO请求,负责切分在运行过程中变得过大的Region。
(3)Master:为RegionServer分配Region,负责RegionServer的负载均衡,发现失效的RegionServer并重新分配其上的Region,执行HDFS上的垃圾文件回收。
(4)ZooKeeper:保证任何时候集群中只有一个Master存储所有Region的寻址入口。实时监控RegionServer的状态,将RegionServer的上线和下线信息实时通知给Master。存储HBase的Schema,包括有哪些table、每个table有哪些column family,处理Region和Master的失效。

以下是HBase与RMDBS的主要差异
图片5

2.3 HBase 的关键流程

HBase客户端会将查询过的HRegion的位置信息进行缓存,如果客户端没有缓存一个HRegion的位置或者位置信息是不正确的,客户端会重新获取位置信息。如果客户端的缓存全部失效,则需要进行多次网络访问才能定位到正确的位置。

2.3.1 Region的分配

任何时刻,一个Region只能分配给一个RegionServer。Master跟踪当前有哪些可用的RegionServer,以及当前哪些Region分配给了哪些RegionServer,哪些Region还没有分配。当存在未分配的Region且有一个RegionServer上有可用空间时,Master就给这个RegionServer发送一个装载请求,把Region分配给这个RegionServer。RegionServer得到请求后,就开始对此Region提供服务。

2.3.2 RegionServer上线

Master使用ZooKeeper来跟踪RegionServer状态。当某个RegionServer启动时,会首先在ZooKeeper上的rs目录下建立代表自己的文件,并获得该文件的独占锁。由于Master订阅了rs目录上的变更消息,当rs目录下的文件出现新增或删除操作时,Master可以得到来自ZooKeeper的实时通知。因此一旦RegionServer上线,Master能马上得到消息。

2.3.3 RegionServer下线

当RegionServer下线时,它和ZooKeeper的会话断开,ZooKeeper会自动释放代表这台Server的文件上的独占锁,而Master不断轮询rs目录下文件的锁状态。如果Master发现某个RegionServer丢失了它自己的独占锁,Master就会尝试去获取代表这个RegionServer的读写锁,一旦获取成功,就可以确定:
(1)RegionServer和ZooKeeper之间的网络断开了;
(2)RegionServer失效了。
只要这两种情况中的一种情况发生了,无论哪种情况,RegionServer都无法继续为它的Region提供服务,此时Master会删除Server目录下代表这台RegionServer的文件,并将这台RegionServer的Region分配给其他还活着的机器。
如果网络短暂出现问题导致RegionServer丢失了它的锁,那么RegionServer重新连接到ZooKeeper之后,只要代表它的文件还在,它就会不断尝试获取这个文件上的锁,一旦获取到了,就可以继续提供服务。

2.3.4 Master上线

Master启动上线包括以下步骤。
(1)从ZooKeeper上获取唯一代表Master的锁,用来阻止其他节点成为Master。
(2)扫描ZooKeeper上的Server目录,获得当前可用的RegionServer列表。
(3)与每个RegionServer通信,获得当前已分配的Region和RegionServer的对应关系。
(4)扫描.META.region的集合,计算得到当前还未分配的Region,将它们放入待分配Region列表。

2.3.5 Master下线

(1)由于Master只维护表和Region的元数据,而不参与表数据IO的过程,所以Master下线仅导致所有元数据的修改被冻结。此时无法创建、删除表,无法修改表的schema,无法进行Region的负载均衡,无法处理Region上下线,无法进行Region的合并,唯一例外的是Region的split可以正常进行,因为只有RegionServer参与,表的数据读写还可以正常进行。因此Master下线短时间内对整个HBase集群没有影响。
(2)Master下线,启用Zookeeper的选举机制,确定新的Master,新的Master执行上线流程

从上线过程可以看到,Master保存的信息全是冗余信息,都可以从系统其他地方收集或者计算出来。因此,一般HBase集群中总是有一个Master在提供服务,还有一个以上的Master在等待时机抢占它的位置。
当客户端要修改HBase的数据时,首先创建一个action(比如put、delete、incr等操作),这些action都会被包装成Key-Value对象,然后通过RPC将其传输到HRegionServer上。HRegionServer将其分配给相应的HRegion,HRegion先将数据写入Hlog中,然后将其写入MemStore。MemStore中的数据是排序的,当MemStore累计到一定阈值时,就会创建一个新的MemStore,并且将老的MemStore添加到flush队列,由单独的线程flush到磁盘上,成为一个StoreFile。与此同时,系统会在ZooKeeper中记录一个redo point,表示这个时刻之前的变更已经持久化了。当系统出现意外时,可能导致内存(MemStore)中的数据丢失,此时使用Log(WAL log)来恢复redo point之后的数据。
StoreFile是只读的,一旦创建后就不可以再修改,因此HBase的更新其实是不断追加的操作。当一个Store中的StoreFile达到一定的阈值后,就会进行一次合并,将对同一个key的修改合并到一起,形成一个大的StoreFile。
由于对表的更新是不断追加的,处理读请求时,需要访问Store中全部的StoreFile和MemStore,将它们的数据按照row key进行合并,由于StoreFile和MemStore都是经过排序的,并且StoreFile带有内存中索引,所以合并的过程还是比较快的。

2.3.6 读请求处理过程

(1)客户端通过 ZooKeeper 以及-ROOT-表和.META.表找到目标数据所在的 RegionServer(就是数据所在的 Region 的主机地址);
(2)zk返回结果给客户端;
(3)联系 RegionServer 查询目标数据;
(4)RegionServer 定位到目标数据所在的 Region,发出查询请求;
(5)Region 先在 Memstore 中查找,命中则返回;
(6)如果在 Memstore 中找不到,则在 Storefile 中扫描,为了能快速的判断要查询的数据在不在这个 StoreFile 中,应用了 BloomFilter;
(BloomFilter,布隆过滤器:迅速判断一个元素是不是在一个庞大的集合内,但是他有一个 弱点:它有一定的误判率);
(误判率:原本不存在与该集合的元素,布隆过滤器有可能会判断说它存在,但是,如果布隆过滤器,判断说某一个元素不存在该集合,那么该元素就一定不在该集合内)。

2.3.7 写请求处理过程

写请求处理过程如下:
(1)Client向RegionServer提交写请求;
(2)RegionServer找到目标Region;
(3)Region检查数据是否与schema一致;
(4)如果客户端没有指定版本,则获取当前系统时间作为数据版本;
(5)将更新写入WAL log;
(6)将更新写入Memstore;
(7)判断Memstore的数据是否需要flush为StoreFile。

三、HBase 集群部署

官网下载HBase:https://hbase.apache.org/downloads.html

服务规则

主机名运行HBase服务
hadoop1Master、RegionServer
hadoop2Backup_Master、RegionServer
hadoop3Backup_Master、RegionServer

3.1 修改 HBase配置文件

下载 hbase

1
2
[hadoop@hadoop1 ~/downloads]$ wget https://archive.apache.org/dist/hbase/2.3.6/hbase-2.3.6-bin.tar.gz
[hadoop@hadoop1 ~]$ tar xf downloads/hbase-2.3.6-bin.tar.gz

复制 hdfs-site.xml 配置文件

1
[hadoop@hadoop1 ~]$ cp hadoop-2.7.2/etc/hadoop/hdfs-site.xml hbase-2.3.6/conf/

复制$HADOOP_HOME/etc/hadoop/hdfs-site.xml$HBASE_HOME/conf目录下,这样以保证hdfshbase两边一致,这也是官网所推荐的方式。在官网中提到一个例子,例如hdfs中配置的副本数量为5,而默认为3,如果没有将最新的hdfs-site.xml复制到$HBASE_HOME/conf目录下,则hbase将会按3份备份,从而两边不一致,导致会出现异常

修改 hbase-site.xml

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
[hadoop@hadoop1 ~]$ vim hbase-2.3.6/conf/hbase-site.xml
<configuration>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
<description>此项用于配置HBase的部署模式,false表示单机或者伪分布式模式,true表示完全分布式模式。
</description>
</property>
<property>
<name> hbase.rootdir </name>
<value>hdfs://mycluster:8020/hbase</value>
<description> hbase.rootdir是RegionServer的共享目录,用于持久化存储HBase数据,默认写入/tmp中。如果不修改此配置,在HBase重启时,数>据会丢失。此处一般设置的是hdfs的文件目录。hdfs端口可以查看hdfs-site.xml里的dfs.namenode.rpc-address.hdfscluster.nn1配置。
</description>
</property>
<property>
<name>hbase.zookeeper.quorum</name>
<value>hadoop1:2181,hadoop2:2181,hadoop3:2181</value>
<description>此项用于配置ZooKeeper集群所在的主机地址,我这里zk跟hadoop运行在一起。
</description>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/home/hadoop/zookeeper-3.4.8/data</value>
<description>此项用于设置存储ZooKeeper的元数据,如果不设置默认存在/tmp下,重启时数据会丢失。这里的zookeeper数据目录与hadoop ha的共
用,也即要与 zoo.cfg 中配置的一致。
</description>
</property>
</configuration>

修改 hbase-env.sh 添加 Java 运行环境

1
2
[hadoop@hadoop1 ~]$ vim hbase-2.3.6/conf/hbase-env.sh
export JAVA_HOME=/home/hadoop/jdk1.8.0_301

修改 regionserver

1
2
3
4
[hadoop@hadoop1 ~]$ vim hbase-2.3.6/conf/regionservers
hadoop1
hadoop2
hadoop3

创建 backup-masters 备用master配置文件

1
2
3
[hadoop@hadoop1 ~]$ vim hbase-2.3.6/conf/backup-masters
hadoop2
hadoop3

同步hbase节点配置

1
2
[hadoop@hadoop1 ~]$ rsync -av hbase-2.3.6 hadoop@hadoop2:~/
[hadoop@hadoop1 ~]$ rsync -av hbase-2.3.6 hadoop@hadoop3:~/

3.2 启动 HBase

启动所有服务

1
2
[hadoop@hadoop1 ~]$ cd hbase-2.3.6/bin/
[hadoop@hadoop1 ~/hbase-2.3.6/bin]$ ./start-hbase.sh

也可以只启动单个服务

#启动hbase master
hbase-daemon.sh start master

#启动hbase regionserver
hbase-daemon.sh start regionserver

查看 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
[hadoop@hadoop1 ~]$ jps
16883 HRegionServer
18197 Jps
1656 QuorumPeerMain
23208 NameNode
23560 JournalNode
24234 NodeManager
24109 ResourceManager
16685 HMaster
23342 DataNode
23886 DFSZKFailoverController

[hadoop@hadoop2 ~]$ jps
577 DFSZKFailoverController
17537 HMaster
1138 ResourceManager
17315 HRegionServer
18378 Jps
13403 QuorumPeerMain
32765 DataNode
429 JournalNode
32622 NameNode
878 NodeManager

[hadoop@hadoop3 ~]$ jps
26432 JournalNode
11332 QuorumPeerMain
26699 NodeManager
3259 Jps
2348 HMaster
2125 HRegionServer
26286 DataNode

3.3 访问 HBase Web UI

HBase Web UI使用的HTTP端口Master的16010和RegionServer的16030。
如果一切设置正确,能够使用 Web 浏览器连接到 Master,或辅助Master 的 UI

Master Web UI
图片6

RegionServer Web UI
图片7

四、HBase Shell

4.1 HBase Shell 常用命令

命名描述语法
通用命令
help ‘命令名’查看命令的使用描述help '命令名'
whoami我是谁whoami
version返回hbase版本信息version
status返回hbase集群的状态信息status
table_help查看如何操作表table_help
tools列出hbase所支持的工具tools
shutdown关闭hbase集群(与exit不同)shutdown
exit退出hbase shellexit
数据定义语言 DDL
create创建表create '表名','列族名1','列族名2','列族名N'
alter修改列族添加一个列族:alter '表名','列族名'
删除列族:alter '表名',{NAME=> '列族名',METHOD=> 'delete'}
describe显示表相关的详细信息describe '表名'
list列出hbase中存在的所有表list
exists测试表是否存在exists '表名'
enable使表有效enable '表名'
is_enabled是否启用is_enabled '表名'
disable使表无效disable '表名'
is_disabled是否无效is_disabled '表名'
drop删除表drop的表必须是disable的 'disable' 表名,'drop' 表名
drop_all删除满足正则表达式的所有表drop_all '正则表达式'
truncate重新创建指定表truncate '表名'
数据操纵语言 DML
delete删除指定对象的值(可以为表,行,列对应的值,另外也可以指定时间戳的值)删除列族的某个列: delete '表名','行键','列族名:列名'
deleteall删除指定行的所有元素值deleteall '表名','行键'
get_counter获取计数器get_counter '表名','行键','列族:列名'
put添加或修改的表的值put '表名','行键','列族名','列值'
put '表名','行键','列族名:列名','列值'
scan通过对表的扫描来获取对用的值scan '表名'
扫描某个列族: scan '表名', {COLUMN=>'列族名'}
扫描列族的某个列: scan '表名', {COLUMN=>'列族名:列名'}
查询同一个列族的多个列: scan '表名', {COLUMNS => ['列族名1:列名1','列族名1:列名2', …]}
get获取行或单元(cell)的值get '表名', '行键'
get '表名', '行键', '列族名'
count统计表中行的数量count '表名'
incr增加指定表行或列的值incr '表名', '行键','列族:列名',步长值

4.2 HBase Shell 常用操作

使用shell登录

1
$ hbase shell

create 创建一个表

1
hbase(main):006:0> create 'member','member_id','address','info'

describe 查看表的描述

1
hbase(main):008:0> describe 'member'

对表添加一个列族

1
hbase(main):009:0> alter 'member','id'

删除一个列族

1
hbase(main):010:0> alter 'member', {NAME => 'member_id', METHOD => 'delete'}

删除列

1
2
3
4
5
6
#通过delete命令,我们可以删除id为某个值的‘info:age’字段
hbase(main):011:0> delete 'member','debugo','info:age'
hbase(main):012:0> get 'member','debugo','info:age'

#删除整行的值:deleteall
hbase(main):014:0> deleteall 'member','debugo'

启动/禁用表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#通过enable和disable来启用/禁用这个表,相应的可以通过is_enabled和is_disabled来检查表是否被禁用
hbase(main):016:0> is_enabled 'member'
true
Took 0.0427 seconds
=> true

hbase(main):017:0> disable 'member'
hbase(main):018:0> is_enabled 'member'
false
Took 0.0155 seconds
=> false

hbase(main):019:0> enable 'member'
hbase(main):020:0> is_enabled 'member'
true
Took 0.0150 seconds
=> true

exists 检查表是否存在

1
2
3
4
hbase(main):021:0> exists 'member'
Table member does exist
Took 0.0117 seconds
=> true

drop 删除表

1
2
3
#删除表需要先将表disable
hbase(main):025:0> disable 'test'
hbase(main):026:0> drop 'test'

put 推送数据到HBase表

1
2
3
4
5
6
7
hbase(main):030:0> create 'table1','id','address','info'
hbase(main):032:0> put 'table1', 'zhangsan','id','11'
hbase(main):033:0> put 'table1', 'zhangsan','info:age','20'
hbase(main):034:0> put 'table1', 'zhangsan','info:birthday','2001-01-11'
hbase(main):035:0> put 'table1', 'zhangsan','info:industry', 'it'
hbase(main):036:0> put 'table1', 'zhangsan','address:city','beijing'
hbase(main):037:0> put 'table1', 'zhangsan','address:country','china'

count
查询表中有多少行

1
hbase(main):043:0> count 'table1'

get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#获取一个id的所有数据
hbase(main):046:0> get 'table1','zhangsan'
COLUMN CELL
address:city timestamp=2021-10-21T14:48:51.493, value=beijing
address:country timestamp=2021-10-21T14:49:07.554, value=china
id: timestamp=2021-10-21T14:47:23.091, value=11
info:age timestamp=2021-10-21T14:47:31.087, value=20
info:birthday timestamp=2021-10-21T14:48:09.703, value=2001-01-11
info:industry timestamp=2021-10-21T14:48:33.678, value=it
1 row(s)

#获得一个id,在一个列簇(一个列)中的所有数据
hbase(main):047:0> get 'table1','zhangsan','info'
COLUMN CELL
info:age timestamp=2021-10-21T14:47:31.087, value=20
info:birthday timestamp=2021-10-21T14:48:09.703, value=2001-01-11
info:industry timestamp=2021-10-21T14:48:33.678, value=it
1 row(s)

scan

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#查询整表数据
hbase(main):048:0> scan 'table1'
ROW COLUMN+CELL
zhangsan column=address:city, timestamp=2021-10-21T14:48:51.493, value=beijing
zhangsan column=address:country, timestamp=2021-10-21T14:49:07.554, value=china
zhangsan column=id:, timestamp=2021-10-21T14:47:23.091, value=11
zhangsan column=info:age, timestamp=2021-10-21T14:47:31.087, value=20
zhangsan column=info:birthday, timestamp=2021-10-21T14:48:09.703, value=2001-01-11
zhangsan column=info:industry, timestamp=2021-10-21T14:48:33.678, value=it
1 row(s)

#扫描整个列簇
hbase(main):049:0> scan 'table1',{COLUMN=>'info'}
ROW COLUMN+CELL
zhangsan column=info:age, timestamp=2021-10-21T14:47:31.087, value=20
zhangsan column=info:birthday, timestamp=2021-10-21T14:48:09.703, value=2001-01-11
zhangsan column=info:industry, timestamp=2021-10-21T14:48:33.678, value=it
1 row(s)

#指定扫描其中的某个列
hbase(main):050:0> scan 'table1',{COLUMNS=>'info:age'}
ROW COLUMN+CELL
debugo column=info:age, timestamp=2021-10-21T14:47:31.087, value=20
1 row(s)
-------------本文结束感谢您的阅读-------------