1.4 HBase技术∗
在Spark结构化数据操作章节中,本教程的案例采用HBase作为结构化数据存储的工具,将Spark处理的数据输出到HBase,或从HBase中读入数据。HBase是建立在Hadoop文件系统之上的分布式面向列的数据库,可以快速、随机访问海量结构化数据。HBase利用了Hadoop的文件系统(HDFS)提供的容错能力,是Hadoop的生态系统中的重要部分,提供对数据的随机实时读/写访问,是Hadoop文件系统的一部分。人们可以直接或通过HBase存储HDFS数据,使用HBase在HDFS读取消费/随机访问数据。HBase在Hadoop的文件系统之上,并提供了读写访问。
自1970年以来,关系数据库用于存储数据和维护有关问题的解决方案。大数据出现后,很多互联网公司开始选择像Hadoop的解决方案实现处理大数据并从中受益。Hadoop使用分布式文件系统,用于存储大数据,并使用MapReduce处理。Hadoop擅长存储各种格式的庞大的数据、任意格式甚至非结构化的处理。但Hadoop只能执行批量处理,并且只以顺序方式访问数据。这意味着必须搜索整个数据集,即使是最简单的搜索工作。当处理结果在另一个庞大的数据集,也是按顺序处理一个巨大的数据集。在这一点上需要一个新的解决方案,用来实现访问数据中的任何点(随机访问)单元,所以出现了一些应用程序,如HBase、Cassandra、couchDB、Dynamo和MongoDB都是用来实现存储大量数据和以随机方式访问数据的数据库。
HBase是面向列的数据库,是Google Big Table存储架构的开源实现,可以管理结构化和半结构化数据,并具有一些内置功能,如可伸缩性、版本控制、压缩和垃圾收集。由于它使用预写日志记录和分布式配置,因此可以提供容错能力,并可以从单个服务器故障中快速恢复。可以使用Hadoop的MapReduce功能操纵基于Hadoop/HDFS构建的HBase以及存储在HBase中的数据。HBase读写访问如图1-8所示。
图1-8 HBase读写访问
HBase和HDFS的比较见表1-3。
表1-3 HBase和HDFS的比较
1.4.1 系统架构
HBase物理体系结构由处于主从关系的服务器组成。通常,HBase集群具有一个称为HMaster的主节点和多个称为HRegionServer的区域服务器。每个区域服务器包含多个区域HRegion。就像在关系数据库中一样,HBase中的数据存储在表中,而这些表存储在区域中,当表太大时,该表将被划分为多个区域,这些区域被分配给整个集群中的区域服务器上,每个区域服务器托管大约相同数量的区域。从HBase的架构图可以看出,HBase中的存储包括HMaster、HRegionServer、HRegion、Store、MemStore、StoreFile、HFile、HLog等,而HBase依赖的外部系统有ZooKeeper、HDFS等。HBase的系统架构如图1-9所示。
图1-9 HBase的系统架构
HMaster是一个轻量级进程,可将区域分配给Hadoop集群中的区域服务器,以实现负载平衡。HMaster是HBase主从集群架构中的中央节点,通常一个HBase集群存在多个HMaster节点,其中一个为活动节点,其余为备份节点。Hbase每时每刻只有一个HMaster主服务器程序在运行,HMaster将区域分配给区域服务器,协调区域服务器的负载并维护集群的状态。HMaster不会对外提供数据服务,而是由区域服务器负责所有区域的读写请求及操作。由于HMaster只维护表和区域的元数据,不参与数据的输入/输出过程,因此HMaster失效仅会导致所有的元数据无法被修改,但表的数据读写还是可以正常进行的。HMaster的职责包括:管理和监视Hadoop集群;执行管理用于创建、更新和删除表的接口;发现失效的区域服务器并重新分配其上的区域;HDFS上的垃圾文件回收;每当想更改数据结构并更改任何元数据操作时,负责所有这些操作。
另一方面,HRegionServer的作用包括:维护HMaster分配区域,处理对这些区域的读写请求;负责切分在运行过程中变得过大的区域。可以看到,客户端访问HBase上的数据并不需要HMaster参与,HMaster仅维护表和区域的元数据信息,表的元数据信息保存在ZooKeeper上,所以负载很低。HMaster上存放的元数据是区域的存储位置信息,但是用户读写数据时,都是先写到区域服务器的WAL日志中,之后由区域服务负责将其刷新到HFile中,即区域中。所以,用户并不直接接触区域,无须知道区域的位置,其并不从HMaster处获得什么位置元数据,只需要从ZooKeeper中获取区域服务器的位置元数据,之后便直接和区域服务器通信。HRegionServer存取一个子表时,会创建一个HRegion对象,然后对表的每个列族创建一个Store实例,每个Store都会有一个MemStore和0个或多个StoreFile与之对应,每个StoreFile都会对应一个HFile,HFile就是实际的存储文件。因此,一个HRegion有多少个列族,就有多少个Store。一个HRegionServer会有多个HRegion和一个HLog。区域服务器在HDFS数据节点上运行,其组件包括:块缓存是读取缓存,最常读取的数据存储在读取缓存中,并且只要块缓存已满,就会驱逐最近使用的数据;MemStore是写缓存,用于存储尚未写入磁盘的新数据,一个区域中的每个列族都有一个MemStore;预写日志(WAL)是一个文件,用于存储未持久保存到磁盘中的新数据;HFile是实际的存储文件,将行作为已排序的键值存储在磁盘上。HBase已经无缝集成了HDFS,其中所有的数据最终都会通过DFS客户端API持久化到HDFS中。
HBase使用ZooKeeper作为用于区域分配的分布式协调服务,并通过将它们加载到正在运行的其他区域服务器上恢复任何区域服务器崩溃。ZooKeeper是集中式监视服务器,用于维护配置信息并提供分布式同步。每当客户想与区域通信时,他们都必须首先联系ZooKeeper。HMaster和区域服务器已向ZooKeeper服务注册,客户端需要访问ZooKeeper仲裁,才能与区域服务器和HMaster连接。如果HBase集群中的节点发生故障,ZKquoram将触发错误消息并开始修复故障节点。ZooKeeper服务跟踪HBase集群中存在的所有区域服务器,从而跟踪有关存在多少区域服务器,以及哪些区域服务器持有哪个数据节点的信息。HMaster联系ZooKeeper以获取区域服务器的详细信息。ZooKeeper提供的各种服务包括:与区域服务器建立客户端通信,区域服务器向ZooKeeper注册,提供区域服务器的状态信息,表示是否在线;HMaster启动时会将HBase系统表加载到ZooKeeper集群中,通过ZooKeeper集群可以获取当前系统表.META.的存储对应的区域服务器信息。区域到区域服务器的映射保存在名为.META.的系统表中,如果尝试从HBase读取或写入数据时,客户端将从.META.表读取所需的区域信息,并直接与适当的区域服务器通信。ZooKeeper是HBase集群的协调器。由于ZooKeeper的轻量级特性,因此可以多个HBase集群共用一个ZooKeeper集群,以节约服务器。多个HBase集群共用ZooKeeper集群的方法是使用同一组IP,修改不同HBase集群的zookeeper.znode.parent属性,让它们使用不同的根目录。
1.4.2 存储机制
现在看一下面向列的数据库、面向行与面向列的数据存储的数据结构和概念有何不同。如图1-10所示,在面向行的数据存储中,行是一起读取或写入的数据单元,而在面向列的数据存储中,列中的数据存储在一起,因此可以快速检索。
1.面向行的数据存储
每次存储和检索一行数据,如果仅需要一行中的某些数据,则需要读取一行中其他不必要的数据;易于读取和写入记录,非常适合OLTP系统;执行操作整个数据集的效率不高,因此聚合是一项昂贵的操作;与面向列的数据存储相比,典型的压缩机制提供效果较差。
图1-10 面向列的数据库与面向行的数据库
2.面向列的数据存储
数据按列存储和检索,如果只需要一些数据,则只会读取相关数据;读和写操作通常较慢,非常适合OLAP系统;可以有效地执行适用于整个数据集的操作,因此可以对许多行和列进行聚合;由于列中的数据属于同一类型的值,因此允许较高的压缩率。
HBase中的数据模型可以容纳半结构化数据,其中的字段大小、数据类型和列是可以变化的。此外,数据模型的布局可以使数据分区以及在整个集群中的分布更加容易。HBase中的数据模型由不同的逻辑组件组成,如表、行、列族、列、单元格和版本。HBase列族如图1-11所示。
图1-11 HBase列族
HBase表根据Row Key的范围被水平拆分成若干个区域,每个区域都包含这个区域的起始键和结束键之间的所有行。区域被分配给集群中的区域服务器管理,由它们负责处理数据的读写请求。HBase中的行是逻辑上的行,模型上的行是按列族分别存取的。HBase表中的列归属某个列族,创建表时必须指定列族,必须在使用表之前定义。列名都以列族作为前缀,图1-11显示了Customer和Sales列族,Customer列族由2列(Customer:Name和Customer:City)组成,而Sales列族由2列(Sales:Product和Sales:Amount)组成。每个列族都有一个以上的列,列族中的这些列一起存储在HFile的低级存储文件中。另外,某些HBase功能将应用于列族,如访问控制、磁盘和内存的使用统计都是在列族层进行的。在实际应用中,列族上的控制权限能用来管理不同类型的应用,例如允许一些应用可以添加新的基本数据;允许一些应用可以读取基本数据并创建继承的列族、允许一些应用只浏览数据(甚至可能因为隐私,不能浏览所有数据)。因此,设计表中的列族时必须注意这些问题。
HBase中通过行和列确定一个存储单元。每个存储单元都保存着同一份数据的多个版本,版本通过时间戳索引,时间戳的类型是64位整型。时间戳可以由HBase(在数据写入时自动)赋值,此时时间戳是精确到毫秒的当前系统时间。时间戳也可以由用户显示赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。每个存储单元中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。为了避免数据存在过多版本造成的管理负担(包括存储和索引),HBase提供了两种数据版本回收方式:一种是保存数据的最后N个版本;另一种是保存最近一段时间内(如最近七天)的版本,用户可以针对每个列族进行设置。存储单元唯一确定的格式为
存储单元中的数据是没有类型的,全部是字节码形式存储。
HBase读取数据的过程:客户端请求读取数据时,先转发到ZooKeeper集群,在ZooKeeper集群中寻找对应的区域服务器,再找到对应的区域,先是查MemStore,如果在MemStore中获取到数据,就会直接返回,否则再由区域找到对应的Store File,从而查到具体的数据。在整个架构中,HMaster和HRegionServer可以在同一个节点上,可以有多个HMaster存在,但是只有一个HMaster活跃。在客户端会进行Row Key->HRegion映射关系的缓存,降低下次寻址的压力。
HBase写入数据的过程:先是客户端进行发起数据的插入请求,如果客户端本身存储了关于Row Key和区域的映射关系,就会先查找具体的对应关系,如果没有,就会在ZooKeeper集群中查找对应的区域服务器,然后再转发到具体的区域上。所有数据在写入的时候先是记录在WAL中,同时检查MemStore是否已满,如果已满,就会进行刷盘,输出到一个HFile中,如果没有满,就先写进MemStore中,然后再刷到WAL中。
1.4.3 常用命令
HBase提供了可以与数据库进行通信的交互管理命令。HBase使用Hadoop文件系统存储数据,拥有一个主服务器和区域服务器。数据区域的形式存储,这些区域被分割并存储在区域服务器。主服务器管理这些区域服务器,所有这些任务都发生在HDFS。下面给出的是一些由HBase支持的交换管理命令。
1.通用命令
status:提供HBase的状态,例如服务器的数量。
version:提供正在使用HBase版本。
table_help:表引用命令提供帮助。
whoami:提供有关用户的信息。
2.数据定义语言,这些是关于HBase在表中操作的命令
create:创建一个表。
list:列出HBase的所有表。
disable:禁用表。
is_disabled:验证表是否被禁用。
enable:启用一个表。
is_enabled:验证表是否已启用。
describe:提供了一个表的描述。
alter:改变一个表。
exists:验证表是否存在。
drop:从HBase中删除表。
drop_all:丢弃在命令中给出匹配regex的表。
3.数据操纵语言
put:添加或修改表的值。
get:获取行或存储单元格的内容。
delete:删除表中存储单元格中的值。
deleteall:删除给定行的所有存储单元。
scan:扫描并返回表数据。
count:计数并返回表中的行的数目。
truncate:禁用、删除和重新创建一个指定的表。
首先使用start-hbase.sh命令启动HBase服务,然后使用“hbase shell”命令启动HBase的交互shell。如果已成功在系统中安装HBase,那么它会给出HBase Shell提示符。
代码1-12
要退出交互命令,输入exit命令或使用组合键Ctrl+C即可。进一步,检查shell功能之前,使用list命令列出所有可用命令。list是用来获取所有HBase表的列表。首先,验证安装HBase在系统中使用list命令,当输入这个命令后,会给出下面的输出。
代码1-13
1.4.3.1 创建表
可以使用命令创建一个表,在这里必须指定表名和列族名。在HBase Shell中创建表使用create命令。下面给出的是一个表名为order的样本模式,有Customer和Sales两个列族,见表1-4。
表1-4 HBase列族
在HBase Shell中创建order表,如下所示。
命令1-10
可以使用list命令验证是否已经创建表。在这里可以看到创建的order表。
命令1-11
1.4.3.2 禁用表
要删除表或改变其设置,首先需要使用disable命令关闭表。使用enable命令,可以重新启用表。下面给出的语法是禁用一个表。
命令1-12
禁用表之后,仍然可以通过list和exists命令查看到。若无法扫描到表,则会出现下面的错误信息。
命令1-13
is_disabled命令用来查看表是否被禁用。下面的例子验证表order是否被禁用。如果表order被禁用,则返回true;如果表order没有被禁用,则返回false。
命令1-14
disable_all命令用于禁用所有匹配给定正则表达式的表,假设有5个表在HBase,即order01、order02、order03、order04和order05,下面的代码将禁用所有以order开始的表。
命令1-15
1.4.3.3 启用表
下面是启用一个表的例子。
命令1-16
启用表之后,扫描表能看到表的结构和数据,那么证明表已成功启用。
命令1-17
is_enabled命令用于查找表是否被启用。下面的代码验证表order是否被启用,如果已启用,则返回true;如果没有启用,则返回false。
命令1-18
1.4.3.4 增、删、改
在HBase表中创建数据,可以使用put命令。
以表1-4为例,使用put命令,可以插入行到一个表。将第一行的值插入order表如下所示。
命令1-19
以相同的方式使用put命令插入剩余的行。如果插入完成整个表格,会得到下面的输出。
命令1-20
可以使用put命令更新现有的单元格值。假设HBase中有一个表order拥有下列数据:
命令1-21
以下命令将更新名为Jack Black客户的城市值为Chongqing。
更新后的表如下所示,观察这个城市Guangzhou的值已更改为Chongqing。
命令1-22
get命令用于从HBase表中读取数据。使用get命令,可以同时获取一行数据。下面的例子说明了如何使用get命令扫描order表的101行。
命令1-23
下面给出的示例,用于读取HBase表中的特定列。
命令1-24
使用delete命令,可以在一个表中删除特定的单元格。下面是一个删除特定单元格的例子,在这里删除City。
命令1-25
使用deleteall命令,可以删除一行中所有的单元格,这里是使用deleteall命令删除order表中101行的所有单元。
命令1-26
使用scan命令验证表,表被删除后的快照如下。
命令1-27
1.4.3.5 其他
describe命令用于返回表的说明。下面给出的是order表的describe命令的输出。
命令1-28
alter命令用于更改现有的表。使用此命令,可以更改列族的最大单元数,设置和删除表范围运算符,以及从表中删除列族。在下面的例子中,单元的最大数目设置为5。
命令1-29
使用alter命令可以设置和删除表范围运算符,如MAX_FILESIZE、READONLY、MEMSTORE_FLUSHSIZE、DEFERRED_LOG_FLUSH等。在下面的例子中设置表order为只读。
命令1-30
下面给出的是一个从order表中删除列族的例子。假设在HBase中有一个order表,包含以下数据:
命令1-31
使用alter命令删除指定的Sales列族。
命令1-32
验证该表中变更后的数据,观察到没有列族Sales了。
命令1-33
可以使用exists命令验证表的存在。下面的示例演示了如何使用这个命令。
命令1-34
用drop命令可以删除表。在删除一个表之前,必须先将其禁用。
命令1-35
exists命令用于验证表是否被删除。
命令1-36
count命令用于计算表的行数量。
命令1-37
truncate命令将禁止删除并重新创建一个表。下面是一个使用truncate命令的例子。
命令1-38
使用scan命令验证,会得到表的行数为零。
命令1-39
可以通过输入exit命令退出交互程序。
命令1-40
要停止HBase,须键入以下命令:
命令1-41