【知识】HBase 的服务器体系架构

HBase 的服务器体系结构遵循简单的主从服务器架构,它由 HRegion 服务器 (HRegion Server) 群和 HBase Master 服务器 (HBase Master Server) 构成。HBase Master 服务器负责管理所有的 HRegion 服务器,而 HBase 中所有的服务器都是通过 ZooKeeper 来进行协调,并处理 HBase 服务器运行期间可能遇到的错误。HBase Master Server 本身不存储 HBase 中的任何数据,HBase 逻辑上的表可能会被划分为多个 HRegion,然后存储到 HRegion Server 群中,HBase Master Server 中存储的是从数据到 HRegion Server 中的映射。

Client

HBase Client 使用 HBase 的 RPC 机制与 HMaster 和 HRegionServer 进行通信,对于管理类操作,Client 与 HMaster 进行 RPC; 对于数据读写类操作,Client 与 HRegionServer 进行 RPC

Zookeeper

Zookeeper Quorum 中除了存储了 -ROOT- 表的地址和 HMaster 的地址,HRegionServer 也会把自己以 Ephemeral 方式注册到 Zookeeper 中,使得 HMaster 可以随时感知到各个 HRegionServer 的健康状态。此外,Zookeeper 也避免了 HMaster 的单点问题,见下文描述

HBase Master 服务器

每台 HRegion 服务器都会和 HMaster 服务器通信,HMaster 的主要任务就是要告诉每台 HRegion 服务器它要维护哪些 HRegion。

当一台新的 HRegion 服务器登录到 HMaster 服务器时,HMaster 会告诉它先等待分配数据。而当一台 HRegion 死机时,HMaster 会把它负责的 HRegion 标记为未分配,然后再把它们分配到其他 HRegion 服务器中。

HRegion 服务器

HRegionServer 主要负责响应用户 I/O 请求,向 HDFS 文件系统中读写数据,是 HBase 中最核心的模块。

所有的数据库数据一般是保存在 Hadoop 分布式文件系统上面的,用户通过一系列 HRegion 服务器来获取这些数据,一台机器上面一般只运行一个 HRegion 服务器,且每一个区段的 HRegion 也只会被一个 HRegion 服务器维护。

当用户需要更新数据的时候,他会被分配到对应的 HRegion 服务器上提交修改,这些修改显示被写到 Hmemcache(内存中的缓存,保存最近更新的数据) 缓存和服务器的 Hlog(磁盘上面的记录文件,他记录着所有的更新操作) 文件里面。在操作写入 Hlog 之后,commit() 调用才会将其返回给客户端。

在读取数据的时候,HRegion 服务器会先访问 Hmemcache 缓存,如果缓存里没有改数据,才会回到 Hstores 磁盘上面寻找,每一个列族都会有一个 HStore 集合,每一个 HStore 集合包含很多 HstoreFile 文件,如下图:

HStore 存储是 HBase 存储的核心了,其中由两部分组成,一部分是 MemStore,一部分是 StoreFiles。MemStore 是 Sorted Memory Buffer,用户写入的数据首先会放入 MemStore,当 MemStore 满了以后会 Flush 成一个 StoreFile(底层实现是 HFile),当 StoreFile 文件数量增长到一定阈值,会触发 Compact 合并操作,将多个 StoreFiles 合并成一个 StoreFile,合并过程中会进行版本合并和数据删除,因此可以看出 HBase 其实只有增加数据,所有的更新和删除操作都是在后续的 compact 过程中进行的,这使得用户的写操作只要进入内存中就可以立即返回,保证了 HBase I/O 的高性能。当 StoreFiles Compact 后,会逐步形成越来越大的 StoreFile,当单个 StoreFile 大小超过一定阈值后,会触发 Split 操作,同时把当前 Region Split 成 2 个 Region,父 Region 会下线,新 Split 出的 2 个孩子 Region 会被 HMaster 分配到相应的 HRegionServer 上,使得原先 1 个 Region 的压力得以分流到 2 个 Region 上。

HRegion

当表的大小超过设置值的是偶,HBase 会自动地将表划分为不同的区域,每个区域包含所有行的一个子集。对用户来说,每个表是一堆数据的集合,靠主键来区分。从物理上来说,一张表被拆分成了多块,每一块就是一个 HRegion。我们用表名 + 开始 / 结束主键来区分每一个 HRegion,一个 HRegion 会保存一个表里某段连续的数据,从开始主键到结束主键,一张完整的表是保存在多个 HRegion 上面的。

HBase 存储格式

HBase 中的所有数据文件都存储在 Hadoop HDFS 文件系统上,主要包括上述提出的两种文件类型:

1. HFile, HBase 中 KeyValue 数据的存储格式,HFile 是 Hadoop 的二进制格式文件,实际上 StoreFile 就是对 HFile 做了轻量级包装,即 StoreFile 底层就是 HFile

2. HLog File,HBase 中 WAL(Write Ahead Log) 的存储格式,物理上是 Hadoop 的 Sequence File

HFile 已经介绍了,接下来就是介绍 HLog File。

在分布式系统环境中,无法避免系统出错或者宕机,因此一旦 HRegionServer 意外退出,MemStore 中的内存数据将会丢失,这就需要引入 HLog 了。每个 HRegionServer 中都有一个 HLog 对象,HLog 是一个实现 Write Ahead Log 的类,在每次用户操作写入 MemStore 的同时,也会写一份数据到 HLog 文件中 (HLog 文件格式见后续),HLog 文件定期会滚动出新的,并删除旧的文件 (已持久化到 StoreFile 中的数据)。当 HRegionServer 意外终止后,HMaster 会通过 Zookeeper 感知到,HMaster 首先会处理遗留的 HLog 文件,将其中不同 Region 的 Log 数据进行拆分,分别放到相应 region 的目录下,然后再将失效的 region 重新分配,领取 到这些 region 的 HRegionServer 在 Load Region 的过程中,会发现有历史 HLog 需要处理,因此会 Replay HLog 中的数据到 MemStore 中,然后 flush 到 StoreFiles,完成数据恢复。

HFile

下图是 HFile 的存储格式:

首先 HFile 文件是不定长的,长度固定的只有其中的两块:Trailer 和 FileInfo。正如图中所示的,Trailer 中有指针指向其他数据块的起始点。File Info 中记录了文件的一些 Meta 信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY 等。Data Index 和 Meta Index 块记录了每个 Data 块和 Meta 块的起始点。

Data Block 是 HBase I/O 的基本单元,为了提高效率,HRegionServer 中有基于 LRU 的 Block Cache 机制。每个 Data 块的大小可以在创建一个 Table 的时候通过参数指定,大号的 Block 有利于顺序 Scan,小号 Block 利于随机查询。每个 Data 块除了开头的 Magic 以外就是一个个 KeyValue 对拼接而成, Magic 内容就是一些随机数字,目的是防止数据损坏。后面会详细介绍每个 KeyValue 对的内部构造。

HFile 里面的每个 KeyValue 对就是一个简单的 byte 数组。但是这个 byte 数组里面包含了很多项,并且有固定的结构。我们来看看里面的具体结构:

开始是两个固定长度的数值,分别表示 Key 的长度和 Value 的长度。紧接着是 Key,开始是固定长度的数值,表示 RowKey 的长度,紧接着是 RowKey,然后是固定长度的数值,表示 Family 的长度,然后是 Family,接着是 Qualifier,然后是两个固定长度的数值,表示 Time Stamp 和 Key Type(Put/Delete)。Value 部分没有这么复杂的结构,就是纯粹的二进制数据了。

HLogFile

上图中示意了 HLog 文件的结构,其实 HLog 文件就是一个普通的 Hadoop Sequence File,Sequence File 的 Key 是 HLogKey 对象,HLogKey 中记录了写入数据的归属信息,除了 table 和 region 名字外,同时还包括 sequence number 和 timestamp,timestamp 是“写入时间”,sequence number 的起始值为 0,或者是最近一次存入文件系统中 sequence number。