初识 HBase - HBase 基础知识

2022-01-18 15:01:27 浏览数 (1)

数人之道原创文章,转载请关注本公众号联系我们

文章大纲:

Hadoop 中的 HDFS 是文件存储的基础,但是如果要对存储在 HDFS 中的文件进行更改、删除等操作会十分费劲。这是由于 Hadoop 只能执行批量处理,且只能以顺序方式访问数据,当需要更改数据时,必须搜索整个数据集,从海量文件数据中取出需要进行更改的内容,读取内容,进行更改操作,然后再写回文件对应位置。这个过程既耗时又繁杂,有没有更好的可以随机访问数据的办法?

2008 年,正值北京奥运会,一个受 Google 的 Bigtable 论文启发而诞生的 HBase 项目成为了 Apache 的子项目。这个项目就是在 HDFS 的基础之上,提供高可靠性、高性能、列存储、可伸缩、实时读写的非关系型数据库系统,以解决海量数据的存储及处理问题。

1 HBase 介绍

HBase 是一个开源的、分布式的、版本化的典型非关系型数据库,作为 Apache 基金会的 Hadoop 项目的一部分,使用 Java 语言实现,它将 HDFS 作为底层文件存储系统,在此基础上运行 MapReduce 进行分布式的批量并行的海量数据存储及处理工作。因此它的一些特点与 Hadoop 相同,可依靠横向扩展,通过不断增加性价比高的商用服务器来增加计算和存储能力。

1.1 HBase 与 HDFS 的关系

HBase 可以直接写入并存储数据至 HDFS 上,也可以在 HDFS 上读取消费、随机访问数据。HBase 在 Hadoop 的文件系统 HDFS 之上,提供了随机读写访问数据的能力。类似的数据库还有:MongoDB, Cassandra, Redis, couchDB 等。

图1-1-1:HBase 与 HDFS 的关系

HBase 与 HDFS 的具体区别如下:

图1-1-2:HBase 与 HDFS 的区别

1.2 HBase 与 RDBMS 的区别

HBase 介于 NoSQL 和 RDBMS 之间,可以存储结构化和半结构化的松散数据,因此它不具备 RDBMS 的一些特点,例如,它不支持 SQL 的跨行事务(可通过 Hive 来实现多表 join 等复杂操作),也不要求数据之间有严格的范式关系,同时它允许在同一列的不同行中存储不同类型的数据。

HBase 与 RDBMS 在结构和功能方面的具体区别如下:

图1-2-1:HBase 与 RDBMS 的区别

1.3 HBase 与 Bigtable 的差异

HBase 是对 Google 的 Bigtable 的开源实现,但它们之间还是有很多差别的。例如:Bigtable 经常被描述成键值数据库,而 HBase 则是面向列存储的分布式数据库;Bigtable 利用 GFS 作为其文件存储系统,而 HBase 利用 Hadoop HDFS 作为其文件存储系统;Bigtable 利用 Chubby 作为协调服务管理系统,而 HBase 利用 Zookeeper 作为协调服务管理系统。

2 HBase 特点

下面介绍 HBase 具备的显著特性,这些特性让 HBase 成为当前和未来大数据平台最实用的数据库之一。

2.1 海量存储

HBase 的单表可以有百亿行、百万列,适合存储 PB 级别的海量数据,可以在横向和纵向两个维度插入数据,具有很大的弹性。

当 RDBMS 单个表的记录在亿级时,查询和写入的性能都会呈现指数级下降,这种庞大的数据量对传统数据库来说是一种灾难,而 HBase 在限定某个列(族)的情况下对于单表存储百亿甚至更多的数据都没有性能问题。在 PB 级别的数据以及采用廉价 PC 存储的情况下,能在几十到百毫秒内返回数据。

这得益于 HBase 采用 LSM 树作为内部数据存储结构,这种结构会周期性地将较小文件合并成大文件,以减少对磁盘的访问。

2.2 列式存储

与很多面向行存储的关系型数据库不同,HBase 是面向列族进行存储和权限控制的,列族下面可以有非常多的列,每个列族单独存储,且支持基于列族的独立检索。

通过下图可以看出行存储与列存储的区别:

图2-2-1:行存储与列存储数据库的区别

可以看到,行存储里一张表的数据都是放在一起的,但在列存储里是按照列分开保存的。在这种情况下,进行数据的插入和更新,行存储会相对容易;而进行行存储时,查询操作需要读取所有的数据,列存储则只需要读取相关列,可以大幅降低系统 I/O 的吞吐量,减少磁盘读写。

2.3 强扩展性

Hbase 的扩展性主要体现在两个方面:

2.3.1. 基于存储的扩展

HBase 工作在 HDFS 之上,理所当然地支持分布式表,也继承了 HDFS 存储的可扩展性。HBase 的扩展是横向的,横向扩展是指在扩展时不需要提升服务器本身的性能,只需添加服务器到现有集群即可。

2.3.2. 基于上层处理能力的扩展

HBase 表根据 Region 大小进行分片,分别存放在集群中不同的 RegionServer 节点上,当添加新的节点机器时,集群就重新调整,在新的节点启动 HBase 服务器,动态地实现热扩展(即在不停止现有服务的前提下,可以随时添加或者减少节点机器数量)。

2.4 高可用性

HBase 运行在 HDFS 上,HDFS 的多副本存储可以让它在岀现故障时自动恢复,同时 HBase 内部也提供 WAL 和 Replication 机制,保证高可用性。

WAL(Write-Ahead Log)预写日志是在 HBase 服务器处理数据插入和删除的过程中用来记录操作内容的日志(类似 Oracle 中的 redo log),保证了数据写入时不会因集群异常而导致写入数据的丢失;而 Replication 机制是基于日志操作来做数据同步的。

当集群中单个节点出现故障时,Zookeeper 会通知集群的主节点,将故障节点的 HLog 中的日志信息分发到其他节点进行数据恢复。

2.5 稀疏性

通常在传统的 RDBMS 中,每一列的数据类型是事先定义好的,会占用固定的内存空间,在此情况下,属性值为空(NULL)的列也需要占用存储空间。

而在 HBase 中的数据都是以字符串形式存储的,为空的列并不占用存储空间,而且在列族中可以指定任意多的列,因此 HBase 的列式存储解决了数据稀疏性的问题,在很大程度上节省了存储开销。

3 HBase 数据模型

HBase 是一种列存储模式与 key-value 存储模式结合的数据库,它具有灵活的数据模型,不仅可以基于 key 进行快速查询,还可以实现基于 value、列名等的全文遍历和检索。

HBase 可以实现自动的数据分片,用户不需要知道数据存储在哪个节点上,只要说明检索的要求,系统会自动进行数据的查询和反馈。

3.1 术语概念

HBase 不支持关系模型,它可以根据用户的需求提供更灵活和可扩展的表设计。与传统的关系型数据库类似,HBase 也是以表的方式组织数据,但其存在形式是 Region, 表也由行和列组成,应用将数据写入 HBase 的表中。但有一点不同的是,HBase 有列族的概念,它将一列或多列组织在一起。

图3-1-1:HBase 的数据结构

3.1.1. 命名空间(Name Spaces)

HBase 中的命名空间是表的逻辑分组,类似 RDBMS 中的数据库实例,这种抽象为多租户的相关功能奠定了基础。命名空间包含:

  • 表(Table):所有表都是命名空间的成员,即表必须属于某个命名空间,若没有指定,则建在 default 命名空间中。
  • RS 组(RegionServer Group):一个命名空间或一张表可以被固定到一组 RegionServer 上,从而保证了数据的隔离性。
  • 权限管理(Permission):可定义控制访问列表(ACL),例如,创建表、读取表、更新表、删除表等操作。
  • 配额管理(Quota):限制一个命名空间可以使用的资源(Region 或者 Table 等)。

3.1.2. 表(Table)

HBase 中的数据以表的形式存储于 Region 中。同一个表中的数据通常是相关的,使用表主要是可以把某些列组织起来一起访问。表名作为 HDFS 存储路径的一部分来使用,在 HDFS 中可以看到每个表名都作为独立的目录结构。

3.1.3. 行键(Row Key)

访问 HBase 表中的行,有三种方式:

  1. 通过单个行键访问
  2. 通过行键的 range
  3. 全表扫描

在 HBase 表里,每一行代表一个数据对象,每一行都以行键来进行唯一标识,是用来检索记录的主键。行键可以是任意字符串,最大长度是 64KB, 实际应用中长度一般为 10-100bytes.

在 HBase 内部,行键是不可分割的字节数组,并且行键是按照字典排序(byte order)由低到高存储在表中的。设计 Key 时,要充分考虑排序存储这个特性,将经常一起读取的行存储放到一起(位置相关性)。注意:字典排序对 int 类型排序的结果是 1,10,100,11,12,13,14,15,16,17,18,19,2,20,21 ... 。因此要保持 int 的自然序,行键必须用 0 作左填充。

行的一次读写是原子操作(不论一次读写多少列),这个设计决策能够使用户很容易理解程序在对同一个行进行并发更新操作时的行为。

在 HBase 中可以针对行键建立索引,以提高检索数据的速度。

3.1.4. 列族(Colunm Family)

HBase 中的列族是一些列的集合,必须在使用表之前进行定义,每个列必须归属于某个列族,列族的名字必须是可显示的字符串,列族中所有列成员的列名都以列族名字作为前缀,例如,info:name, info:age 都属于 info 这个列族。

列族支持动态扩展,用户可以很轻松地添加一个列族或列,无须预定义列的数量以及类型。但需要注意的是,访问控制、磁盘和内存的使用统计都是在列族层面进行的,列族越多,在取一行数据时所需要参与 I/O、搜寻的文件就越多,所以,如果没有必要,不要设置太多的列族。

列族中的所有列均以字符串形式存储,用户在使用时需要自行进行数据类型的转换。

3.1.5. 列标识(Column Qualifier)

列族中的数据通过列标识来进行定位,属于某一个列族,类似 RDBMS 表中的字段名。列标识没有特定的数据类型,以二进制字节来存储。通常以 Column Family:Colunm Qualifier 来确定列族中的某列。

3.1.6. 单元格(Cell)

每一个行键、列族、列标识共同确定一个单元格,最小单元格还需要加上时间戳。单元格的内容也没有特定的数据类型,以二进制字节来存储。最小单元格可以用以下元组方式来进行访问:

代码语言:javascript复制
<RowKey,Column Family:Column Qualifier,Timestamp>

3.1.7. 时间戳(Timestamp)

在默认情况下,每一个单元格插入数据时都会用时间戳来进行版本标识,每个单元格保存着同一份数据的多个版本,不同时间版本的数据按照时间先后倒序排序,最新的数据排在最前面。版本通过时间戳来索引,时间戳的类型是 64 位整型,其格式是毫秒级 Unix 时间戳。

读取单元格数据时,如果时间戳没有被指定,则默认返回最新的数据;写入新的单元格数据时,时间戳可以由客户显式赋值,如果没有设置,默认使用精确到毫秒的当前系统时间。如果应用要避免数据版本冲突,就必须自己生成具有唯一性的时间戳。

每一个列族的单元数据的版本数量都被 HBase 单独维护,默认情况下 HBase 保留 3 个版本数据,另外 HBase 还提供了两种数据版本回收方式:

  • 保存数据的最后 n 个版本
  • 保存最近一段时间内的版本(设置数据的生命周期 TTL)

用户可以针对每个列族进行设置。

3.2 逻辑模型

表是 HBase 中数据的逻辑组织方式,从用户视角来看,HBase 表的逻辑模型如下图所示:

图3-2-1:HBase 的数据逻辑模型

图3-2-1展示的是 HBase 中的学生成绩表 Performance:有四行记录和两个列族,行键分别为 0001、0002、0003 和 0004,两个列族分别为 StudentInfo 和 Grades, 每个列族中含有若干列。表存储在 Region 中,列族存放在 Store 中(HBase 架构中会介绍)。

从图3-2-1的表逻辑模型来看,HBase 表与 RDBMS 中的表结构之间似乎没有太大差异,只不过多了列族的概念,但实际上是有很大差别的。

RDBMS 中表的结构需要预先定义,如列名及其数据类型和约束等内容,如果需要添加新列,则需要修改表结构,这会对已有的数据产生很大影响。而在 HBase 中,列不是固定的表结构,在创建表时,不需要预先定义列名,可以在插入数据时临时创建。

同时,RDBMS 中的表为每个列预留了存储空间,即图3-2-1表中的空白 Cell 数据在 RDBMS 中以“NULL”值占用存储空间。而在 HBase 中,如图3-2-1表中的空白 Cell 在物理上是不占用存储空间的,即不会存储空白的键值对。因此,若一个请求获取 RowKey 为 0001 的 StudentInfo:Age 值时,其结果为空。类似地,若一个请求获取 RowKey 为 0002 的 StudentInfo:Address 值时,其结果也为空。

3.3 物理模型

与面向行存储的关系型数据库不同,HBase 是面向列存储的,且在实际的物理存储中,列族是分开存储的,即图3-2-1表中的学生成绩表将被存储为 StudentInfo 和 Grades 两个部分。

图3-3-1:HBase 的数据物理模型

图3-3-1展示了 StudentInfo 这个列族中 RowKey 为 0001 的数据的实际物理存储方式,列族的数据会从内存写到 StoreFile 中(HBase 架构中会介绍)。在图3-3-1中可以看到空白 Cell 是没有被存储下来的。

在图3-3-1中还可以看到,0001 的 StudentInfo:Address 存储了两个版本的数据,通过时间戳(Timestamp)区分开来,最新的数据放在前面,在没有指定的情况下默认读取此最新版本的数据(dataman_road 而非 alibaba)。通过加上时间戳指定可读取旧版本的数据:

代码语言:javascript复制
<0001,StudentInfo:Address,1638502591507>

HBase 的数据物理存储格式为多维稀疏排序Map, 由 key 及 value 组成:

  • key 的构成:rowkey family qualifier timestamp type
  • value 的构成:字节形式存储

例如:

代码语言:javascript复制
{"0001","StudentInfo","Name","1638716097131","put"} -> "Jack Ma"

Type 记录的是数据的操作类型。

4 HBase 架构

HBase 由 Client, Zookeeper, HMaster, HRegionServer, HDFS 等几个组件组成,HRegionServer 组件中又有 HLog, HRegion, Store, MemStore, StoreFile, HFile 几个组件,可以说 HBase 架构的核心及复杂点正是在于 HRegionServer 组件。

图4-1:HBase 架构图

4.1 Client

Client 包含了访问 HBase 的接口,另外还维护了两张 cache 表来加速 HBase 的访问:

  • .META.:记录了用户所有表拆分出来的的 Region 元数据映射信息,.META.可以有多个 Regoin
  • -ROOT-:记录了.META.表的 Region 信息,-ROOT-只有一个 Region,无论如何不会分裂

Client 访问用户数据前需要先访问 Zookeeper,找到-ROOT-表的 Region 所在的位置,然后访问-ROOT-表,接着访问.META.表,最后才能找到用户数据的位置去访问。

4.2 Zookeeper

在 HBase 的系统架构中,Zookeeper 是串联 HBase 集群和 Client 的关键。主要负责以下工作:

4.2.1. Master 选举

跟 HDFS 的 HA 机制一样,HBase 集群通过多个 HMaster 节点实现 HA,Zookeeper 的选举机制保证同一时刻只有一个 Active HMaster,一旦这个 HMaster 无法使用,则选举出一个节点进行切换,以保证集群的高可用性。

4.2.2. RegionServer 监控

在 HBase 启动时,每个 RegionServer 在加入集群时都需要到 Zookeeper 中进行注册,创建一个状态节点,Zookeeper 会实时监控每个 RS 的状态,同时 HMaster 会监听这些注册的 RS. 当某个 RS 挂掉,Zookeeper 会因为一段时间内接收不到它的心跳信息而删除该 RS 对应的状态节点,并给 HMaster 发送节点删除的通知。这时,HMaster 获知集群中某节点断开,会立即调度其他节点开启容错机制。

4.2.3. Region 元数据管理

在 HBase 集群中,数据库表信息、列族信息及列族存储位置信息都属于元数据,这些 Region 元数据被存储在 .META. 表中。每次 Client 发起新的请求时,需要查询 .META. 表来获取 Region 的位置,而 .META. 表是存储在 Zookeeper 中的。当 Region 发生变化时,就能够通过 Zookeeper 感知这一变化,保证客户端能够获得正确的 Region 元数据信息。

4.2.4. Region 状态管理

HBase 集群中 Region 会经常发生变更,其原因可能是系统故障,配置修改,或者是因负载均衡而进行的分裂和合并。只要 Region 发生变化,就需要让集群的所有节点知晓,否则就会出现某些事务性的异常。

而对于 HBase 集群,Region 的数量会达到 10 万,甚至更多。如此规模的 Region 状态管理如果直接由 HMaster 来实现,则 HMaster 的负担会很重,因此只有依靠 Zookeeper 系统来完成。

4.3 HMaster

HMaster 是 HBase 集群中的主服务器,负责监控集群中的所有 RegionServer,并且是所有元数据更改的接口。

在分布式集群中,HMaster 服务器通常运行在 HDFS 的 NameNode 上,并通过 Zookeeper 实现集群的高可用,主要负责表和 Region 的管理工作。

4.3.1. 管理用户对表的增、删、改、查操作

HMaster 提供了以下的一些基于元数据方法的接口,便于用户与 HBase 进行交互:

图4-3-1:HMaster 元数据接口

4.3.2. 管理 RegionServer 的负载均衡

当一个 RegionServer 中出现大量数据,而另一个 RegionServer 中的数据十分稀少,即出现了所谓的“数据倾斜”现象。此时 HMaster 会调整 Region 的分布,实现 RegionServer 的负载均衡,避免“数据倾斜”现象的发生。

4.3.3. Region 的分配和移除

HMaster 负责为 RegionServer 分配 Region,并负责发现失效的 Region ,将其重新分配到正常的 RegionServer 上。

4.3.4. 处理 RegionServer 的故障转移

当某台 RegionServer 出现故障时,总有一部分新写入的数据还没有持久化地存储到磁盘中,因此在迁移该 RegionServer 的服务时,需要从 HLog 修改记录中恢复这部分还在内存中的数据,HMaster 需要遍历该 RegionServer 的修改记录,并按 Region 拆分成小块移动到新的地址下。

另外,当 HMaster 节点发生故障时,由于客户端是直接与 RegionServer 交互的,且 .META. 表也是存在于 Zookeeper 当中,整个集群的工作会继续正常运行,所以当 HMaster 发生故障时,集群仍然可以稳定运行。但是 HMaster 的工作则无法执行,因此还是需要通过 HA 机制尽快恢复 HMaster 的工作。

4.4 HRegionServer

RegionServer 是 HBase 中最核心的模块,主要负责响应用户的请求,向 HDFS 读写数据。

每个 RegionServer 内部管理了一系列 Region 对象,其负责的功能如下:

  • 维护 RegionServer 分配的 Region, 处理这些 Region 的 I/O 请求
  • 处理来自 Client 的读写请求
  • 负责和底层 HDFS 交互,刷新缓存到 HDFS 中
  • 处理 Region 分片,Region 进行拆分后新产生的 Region,由 HMaster 来决定发到哪个 RegioneServer 中, 从而达到负载均衡目的,而 RegioneServer 则负责执行这个拆分
  • StoreFile 的合并工作

一般在分布式集群中,RegionServer 运行在 DataNode 服务器上,实现数据的本地性。

4.5 HRegion

Region 是 HBase 中分布式存储和负载均衡的最小单元,可以看作是一个表,一个 Region 对应一个表, 存储在 RegionServer 中。在一个 RegionServer 中可以有多个不同的 Region.

但随着数据不断插入表,Region 不断增大,当 Region 的某个列族达到一个阈值时就会根据 RowKey 值被拆分成两个新的 Region. 因此一个表可能对应多个 Region.

4.6 HLog

HLog,即 WAL(Write-Ahead Log)预写日志,类似 Oracle 中的 redo log, 记录了数据的所有变更记录,用来进行灾难恢复时使用。

当对 HBase 读写数据的时候,数据不是直接写进磁盘中,它会在内存中保留一段时间(时间以及数据量阈值可以设定),但把数据保存在内存中可能有较高的概率引起数据丢失。为了解决这个问题,数据会先写在 HLog 文件中,然后再写入内存中。所以一旦 RegionServer 出现故障时,数据可以通过这个日志文件进行恢复重建。

4.7 Store

每一个 Region 由一个或多个 Store 组成,HBase 会把一起访问的数据放在一个 Store 里面,即为每个列族建一个 Store,因此一个 Store 对应 HBase 表中的一个列族。

HBase 以 Store 的大小来判断是否需要拆分 Region,一旦被拆分,列族就会被拆分到不同 Region 的 Store 中,因此一个列族可能对应多个 Store.

4.8 MemStore

MemStore 位于内存存储中,以 key-value 的形式保存当前的数据操作。数据保存在 HLog 中后,RegsionServer 就会在内存中存储相应的键值对。

当 MemStore 的大小达到一个阈值(默认 128MB)时,MemStore 会被 flush 到文件中,即生成一个快照。目前 HBase 会有一个线程来负责 MemStore 的 flush 操作。

4.9 StoreFile

MemStore 内存中的数据写到文件后就是 StoreFile, StoreFile 底层以 HFile 的格式保存。当 StoreFile 文件的数量增长到一定阈值后,RegionServer 会对其进行合并,在合并过程中会进行版本合并和删除工作,形成更大的 StoreFile.

4.10 HFile

HBase 中 key-value 数据的存储格式,HFile 是 Hadoop 的二进制格式文件,是在磁盘上保存原始数据的实际的物理文件。实际上 StoreFile 就是对 Hfile 做了轻量级包装,即 StoreFile 底层就是 HFile.

4.11 HDFS

RegionServer 最终会将 Region 数据以 HFile 形式通过 HDFS Client 存储在 HDFS 的 DataNode 中,HDFS 为 HBase 提供了最终的底层数据存储服务。

HBase 自身并不具备数据复制和维护数据副本的功能,而是依赖于 HDFS 为 HBase 提供高可靠和高可用的存储支持。

5 HBase 使用场景

HBase 解决不了所有的问题,但是针对某些特点的数据可以使用 HBase 高效地解决,如以下的应用场景:

  • 数据模式是动态的或者可变的,且支持半结构化和非结构化的数据。
  • 数据库中的很多列都包含了很多空字段,在 HBase 中的空字段不会像在关系型数据库中占用空间。
  • 需要很高的吞吐量,数据库的瞬间写入量很大。
  • 数据有很多版本需要维护,HBase 可以利用时间戳来区分不同版本的数据。
  • 具有高可扩展性,能动态地扩展整个存储系统。

THE END

0 人点赞