一条数据的 HBase 之旅,简明 HBase 入门教程 - 开篇



转载请注明 AIQ - 最专业的机器学习大数据社区  http://www.6aiq.com

AIQ 机器学习大数据 知乎专栏 点击关注

一些常见的 HBase 新手问题

  1. 什么样的数据适合用 HBase 来存储?

  2. 既然 HBase 也是一个数据库,能否用它将现有系统中昂贵的 Oracle 替换掉?

  3. 存放于 HBase 中的数据记录,为何不直接存放于 HDFS 之上?

  4. 能否直接使用 HBase 来存储文件数据?

  5. Region(HBase 中的数据分片) 迁移后,数据是否也会被迁移?

  6. 为何基于 Spark/Hive 分析 HBase 数据时性能较差?

开篇

用惯了 Oracle/MySQL 的同学们,心目中的数据表,应该是长成这样的:

这种表结构规整,每一行都有固定的列构成,因此,非常适合结构化数据的存储。但在 NoSQL 领域,数据表的模样却往往换成了另外一种 "画风":

行由看似 "杂乱无章" 的列组成,行与行之间也无须遵循一致的定义,而这种定义恰好符合半结构化数据或非结构化数据的特点。本文所要讲述的 HBase,就属于该派系的一个典型代表。这些 "杂乱无章" 的列所构成的多行数据,被称之为一个 "稀疏矩阵“,而上图中的每一个 " 黑块块”,在 HBase 中称之为一个 KeyValue。

Apache HBase 官方给出了这样的定义:

Apache HBase™ is the Hadoop database, a distributedscalablebig data store.

即:Apache HBase 是基于 Hadoop 构建的一个分布式的、可伸缩海量数据存储系统

HBase 常被用来存放一些海量的(通常在 TB 级别以上)结构比较简单的数据,如历史订单记录,日志数据,监控 Metris 数据等等,HBase 提供了简单的基于 Key 值的快速查询能力

HBase 在国内市场已经取得了非常广泛的应用,在搜索引擎中,也可以看出来,HBase 在国内呈现出了逐年上升的势态:

从 Apache HBase 所关联的 github 项目的 commits 统计信息来看,也可以看出来该项目非常活跃:

(需要说明的一点:HBase 中的每一次 commit,都已经过社区 Commiter 成员严格的 Review,在 commit 之前,一个 Patch 可能已经被修改了几十个版本 )

令人欣喜的是,国内的开发者也积极参与到了 HBase 社区贡献中,而且被社区接纳了多名 PMC 以及 Committer 成员

本文将以 ** 一条数据在 HBase 中的“旅程”** 为线索,介绍 HBase 的核心概念与流程,几乎每一部分都可以展开成一篇独立的长文,但本文旨在让读者能够快速的了解 HBase 的架构轮廓,所以很多特性 / 流程被被一言带过,但这些特性在社区中往往经历了漫长的开发过程。至于讲什么以及讲到什么程度,本文都做了艰难的取舍,在讲解的过程中,将会穿插解答本文开始所提出的针对初学者的一些常见问题。

本文适用于 HBase 新手,而对于具备一定经验的 HBase 开发人员,相信本文也可以提供一些有价值的参考。本文内容基于 HBase 2.0 beta 2 版本,对比于 1.0 甚至是更早期的版本,2.0 出现了大量变化,下面这些问题的答案将揭示部分关键的变化(新手可以直接跳过这些问题):

  1. HBase **meta **Region 在哪里提供服务?

  2. HBase 是否可以保证单行操作的原子性

  3. Region 中写 WAL 与写 MemStore 的顺序是怎样的?

  4. 你是否遇到过 Region 长时间处于RIT的状态? 你认为旧版本中 Assignment Manager 的主要问题是什么?

  5. 在面对Full GC问题时,你尝试做过哪些优化?

  6. 你是否深究过 HBase Compaction 带来的“写放大”有多严重?

  7. HBase 的 RPC 框架存在什么问题?

  8. 导致查询时延毛刺的原因有哪些?

本系列文章的整体行文思路如下:

  1. 介绍 HBase 数据模型

  2. 基于数据模型介绍 HBase 的适用场景

  3. 快速介绍集群关键角色以及集群部署建议

  4. 示例数据介绍

  5. 写数据流程

  6. 读数据流程

  7. 数据更新

  8. 负载均衡机制

  9. HBase 如何存储小文件数据

这些内容将会被拆成几篇文章。至于集群服务故障的处理机制,集群工具,周边生态,性能调优以及最佳实践等进阶内容,暂不放在本系列文章范畴内。

约定

1. 本文范围内针对一些关键特性 / 流程,使用了加粗以及加下划线的方式做了强调,如 "ProcedureV2"。这些特性往往在本文中仅仅被粗浅提及,后续计划以独立的文章来介绍这些特性 / 流程。

2. 术语缩写:对于一些进程 / 角色名称,在本文范围内可能通过缩写形式来表述:

数据模型

RowKey

用来表示唯一一行记录的主键,HBase 的数据是按照 RowKey 的字典顺序进行全局排序的,所有的查询都只能依赖于这一个排序维度。

通过下面一个例子来说明一下 "字典排序" 的原理:

RowKey 列表 {“abc”, “a”, “bdf”, “cdf”, “def”} 按字典排序后的结果为{“a”, “abc”, “bdf”, “cdf”, “defg”}

也就是说,当两个 RowKey 进行排序时,先对比两个 RowKey 的第一个字节,如果相同,则对比第二个字节,依次类推…如果在对比到第 M 个字节时,已经超出了其中一个 RowKey 的字节长度,那么,短的 RowKey 要被排在另外一个 RowKey 的前面。

稀疏矩阵

参考了 Bigtable,HBase 中一个表的数据是按照稀疏矩阵的方式组织的,"开篇" 部分给出了一张关于 HBase 数据表的 _ 抽象图 _,我们再结合下表来加深大家关于 "稀疏矩阵" 的印象:

看的出来:每一行中,列的组成都是灵活的,行与行之间并不需要遵循相同的列定义, 也就是 HBase 数据表 "schema-less" 的特点。

Region

区别于 Cassandra/DynamoDB 的 "Hash 分区" 设计,HBase 中采用了 "Range 分区",将 Key 的完整区间切割成一个个的 "Key Range" ,每一个 "Key Range" 称之为一个 Region。

也可以这么理解:将 HBase 中拥有数亿行的一个大表,横向切割成一个个 "子表“,这一个个”子表" 就是Region

Region 是 HBase 中负载均衡的基本单元,当一个 Region 增长到一定大小以后,会自动分裂成两个。

Column Family

如果将 Region 看成是一个表的横向切割,那么,一个 Region 中的数据列的纵向切割,称之为一个Column Family。每一个列,都必须归属于一个 Column Family,这个归属关系是在写数据时指定的,而不是建表时预先定义。

KeyValue

KeyValue 的设计不是源自 Bigtable,而是要追溯至论文 "The log-structured merge-tree(LSM-Tree)"。每一行中的每一列数据,都被包装成独立的拥有特定结构的 KeyValue,KeyValue 中包含了丰富的自我描述信息:

看的出来,KeyValue 是支撑 "稀疏矩阵" 设计的一个关键点:一些 Key 相同的任意数量的独立 KeyValue 就可以构成一行数据。但这种设计带来的一个显而易见的缺点:每一个 KeyValue 所携带的自我描述信息,会带来显著的数据膨胀

适用场景

在介绍完了 HBase 的数据模型以后,我们可以回答本文一开始的前两个问题:

  1. 什么样的数据适合用 HBase 来存储?

  2. 既然 HBase 也是一个数据库,能否用它将现有系统中昂贵的 Oracle 替换掉?

HBase 的数据模型比较简单,数据按照 RowKey 排序存放,适合 HBase 存储的数据,可以简单总结如下:

  • 实体为中心的数据

    实体可以包括但不限于如下几种:

    描述这些实体的,可以有基础属性信息、实体关系 (图数据)、所发生的事件(如交易记录、车辆轨迹点) 等等。

  • 自然人/账户/手机号/车辆相关数据

  • 用户画像数据(含标签类数据)

  • 图数据(关系类数据)

  • 事件为中心的数据

  • 监控数据

  • 时序数据

  • 实时位置类数据

  • 消息 / 日志类数据

上面所描述的这些数据,有的是结构化数据,有的是半结构化非结构化数据。HBase 的“稀疏矩阵”设计,使其应对非结构化数据存储时能够得心应手,但在我们的实际用户场景中,结构化数据存储依然占据了比较重的比例。由于 HBase 仅提供了基于 RowKey 的单维度索引能力,在应对一些具体的场景时,依然还需要基于 HBase 之上构建一些专业的能力,如:

  • OpenTSDB 时序数据存储,提供基于 Metrics+ 时间 + 标签的一些组合维度查询与聚合能力

  • GeoMesa 时空数据存储,提供基于时间 + 空间范围的索引能力

  • JanusGraph 图数据存储,提供基于属性、关系的图索引能力

HBase 擅长于存储结构简单的海量数据但索引能力有限,而 Oracle 等传统关系型数据库 (RDBMS) 能够提供丰富的查询能力,但却疲于应对 TB 级别的海量数据存储,HBase 对传统的 RDBMS 并不是取代关系,而是一种补充

HBase 与 HDFS

我们都知道 HBase 的数据是存储于 HDFS 里面的,相信大家也都有这么的认知:

HBase 是一个分布式数据库,HDFS 是一个分布式文件系统

理解了这一点,我们先来粗略回答本文已开始提出的其中两个问题:

  1. HBase 中的数据为何不直接存放于 HDFS 之上?

HBase 中存储的海量数据记录,通常在几百 Bytes 到 KB 级别,如果将这些数据直接存储于 HDFS 之上,会导致大量的小文件产生,为 HDFS 的元数据管理节点 (NameNode) 带来沉重的压力。

  1. 文件能否直接存储于 HBase 里面?

如果是几 MB 的文件,其实也可以直接存储于 HBase 里面,我们暂且将这类文件称之为小文件,HBase 提供了一个名为 MOB 的特性来应对这类小文件的存储。但如果是更大的文件,强烈不建议用 HBase 来存储,关于这里更多的原因,希望你在详细读完本系列文章所有内容之后能够自己解答。

集群角色

关于集群环境,你可以使用国内外大数据厂商的平台,如 Cloudera,Hontonworks 以及国内的华为,都发行了自己的企业版大数据平台,另外,华为云、阿里云中也均推出了全托管式的 HBase 服务。

我们假设集群环境已经 Ready 了,先来看一下集群中的关键角色

相信大部分人对这些角色都已经有了一定程度的了解,我们快速的介绍一下各个角色在集群中的主要职责:

  • ZooKeeper

    在一个拥有多个节点的分布式系统中,假设,只能有一个节点是主节点,如何快速的选举出一个主节点而且让所有的节点都认可这个主节点?这就是 HBase 集群中存在的一个最基础命题。

    利用 ZooKeeper 就可以非常简单的实现这类 "仲裁" 需求,ZooKeeper 还提供了基础的事件通知机制,所有的数据都以 ZNode 的形式存在,它也称得上是一个 "微型数据库"。

  • NameNode

    HDFS 作为一个分布式文件系统,自然需要文件目录树的元数据信息,另外,在 HDFS 中每一个文件都是按照 Block 存储的,文件与 Block 的关联也通过元数据信息来描述。NameNode 提供了这些元数据信息的存储

  • DataNode

    HDFS 的数据存放节点。

  • RegionServer

    HBase 的数据服务节点

  • Master

    HBase 的管理节点,通常在一个集群中设置一个主 Master,一个备 Master,主备角色的 "仲裁" 由 ZooKeeper 实现。 Master主要职责

    ①负责管理所有的 RegionServer。

    ②建表 / 修改表 / 删除表等 DDL 操作请求的服务端执行主体。

    ③管理所有的数据分片 (Region) 到 RegionServer 的分配。

    ④如果一个 RegionServer 宕机或进程故障,由 Master 负责将它原来所负责的 Regions 转移到其它的 RegionServer 上继续提供服务。

    ⑤Master 自身也可以作为一个 RegionServer 提供服务,该能力是可配置的。

集群部署建议

如果基于物理机 / 虚拟机部署,通常建议:

1. RegionServer 与 DataNode 联合部署,RegionServer 与 DataNode 按 1:1 比例设置。

这种部署的优势在于,RegionServer 中的数据文件可以存储一个副本于本机的 DataNode 节点中,从而在读取时可以利用 HDFS 中的 "短路径读取 (Short Circuit)" 来绕过网络请求,降低读取时延。

2. 管理节点独立于数据节点部署

如果是基于物理机部署,每一台物理机节点上可以设置几个 RegionServers/DataNodes 来提升资源使用率。

也可以选择基于容器来部署,如在 HBaseCon Asia 2017 大会知乎的演讲主题中,就提到了知乎基于 Kubernetes 部署 HBase 服务的实践。

对于公有云 HBase 服务而言,为了降低总体拥有成本 (TCO),通常选择 "计算与存储物理分离“的方式,从架构上来说,可能导致平均时延略有下降,但可以借助于共享存储底层的 IO 优化来做一些 " 弥补”。

HBase 集群中的 RegionServers 可以按逻辑划分为多个 Groups,一个表可以与一个指定的 Group 绑定,可以将 RegionServer Group 理解成将一个大的集群划分成了多个逻辑子集群,借此可以实现多租户间的隔离,这就是 HBase 中的RegionServer Group特性。

示例数据

以我们日常生活都熟悉的手机通话记录的存储为例,先简单给出示例数据的字段定义:

如上定义与电信领域的通话记录字段定义相去甚远,本文力求简洁,仅给出了最简单的示例。如下是 "虚构" 的样例数据:

在本文大部分内容中所涉及的一条数据,是上面加粗的最后一行 "MSISDN1“为”13400006666" 这行记录。

在本系列文章的流程图中,我们将会使用一个 ** 红色的五角星 ****** 来表示该数据所在的位置。

写数据之前:创建连接

Login

在启用了安全特性的前提下,Login 阶段是为了完成用户认证(确定用户的合法身份),这是后续一切安全访问控制的基础。

当前 Hadoop/HBase 仅支持基于 Kerberos 的用户认证,ZooKeeper 除了 Kerberos 认证,还能支持简单的用户名 / 密码认证,但都基于静态的配置,无法动态新增用户。如果要支持其它第三方认证,需要对现有的安全框架做出比较大的改动。

创建 Connection

Connection 可以理解为一个 HBase 集群连接的抽象,建议使用 ConnectionFactory 提供的工具方法来创建。因为 HBase 当前提供了两种连接模式:同步连接,异步连接,这两种连接模式下所创建的 Connection 也是不同的。我们给出 ConnectionFactory 中关于获取这两种连接的典型方法定义:

Connection 中主要维护着两类共享的资源

  • 线程池

  • Socket 连接

这些资源都是在真正使用的时候才会被创建,因此,此时的连接还只是一个 "虚拟连接"。

写数据之前:创建数据表

DDL 操作的抽象接口 - Admin

Admin 定义了常规的 DDL 接口,列举几个典型的接口:

预设合理的数据分片 - Region

分片数量会给读写吞吐量带来直接的影响,因此,建表时通常建议由用户主动指定划分Region 分割点,来设定 Region 的数量。

HBase 中数据是按照 RowKey 的字典顺序排列的,为了能够划分出合理的 Region 分割点,需要依据如下几点信息:

  • Key 的组成结构

  • Key 的数据分布预估

    如果不能基于 Key 的组成结构来预估数据分布的话,可能会导致数据在 Region 间的分布不均匀

  • 读写并发度需求

    依据读写并发度需求,设置合理的 Region 数量

为表定义合理的 Schema

既然 HBase 号称 "schema-less" 的数据存储系统,那何来的是 "schema “?的确,在数据库范式的支持上,HBase 非常弱,这里的"schema”,主要指如下一些信息的设置:

1. NameSpace设置

2. Column Family 的数量

3. 每一个 Column Family 中所关联的一些关键配置

① Compression

HBase 当前可以支持 Snappy,GZ,LZO,LZ4,Bzip2 以及 ZSTD 压缩算法

**② **DataBlock Encoding

HBase 针对自身的特殊数据模型所做的一种压缩编码

**③ **BloomFilter

可用来协助快速判断一条记录是否存在

④ TTL

指定数据的过期时间

**⑤ **StoragePolicy

指定 Column Family 的存储策略,可选项有:“ALL_SSD”,“ONE_SSD”,“HOT”,“WARM”,“COLD”,“LAZY_PERSIST”

HBase 中并不需要预先设置 Column 定义信息,这就是 HBase schema less 设计的核心。

Client 发送建表请求到 Master

建表的请求是通过 RPC 的方式由 Client 发送到 Master:

  • RPC 接口基于Protocol Buffer定义

  • 建表相关的描述参数,也由Protocol Buffer进行定义及序列化

Client 端侧调用了 Master 服务的什么接口,参数是什么,这些信息都被通过 RPC 通信传输到 Master 侧,Master 再依据这些接口 \ 参数描述信息决定要执行的操作。2.0 版本中,HBase 目前已经支持基于 Netty 的异步 RPC 框架

关于 HBase RPC 框架

早期的 HBase RPC 框架,完全借鉴了 Hadoop 中的实现,那时,Netty 项目尚不盛行。

Master 侧接收到 Client 侧的建表请求以后,一些主要操作包括:

1. 生成每一个 Region 的描述信息对象 HRegionInfo,这些描述信息包括:Region ID, Region 名称,Key 范围,表名称等信息。

2. 生成每一个 Region 在 HDFS 中的文件目录。

3. 将 HRegionInfo 信息写入到记录元数据的 hbase:meta 表中。

说明

meta 表位于名为 "hbase" 的 namespace 中,因此,它的全称为 "hbase:meta"。但在本系列文章范畴内,常将其缩写为 "meta"。

整个过程中,新表的状态也是记录在 hbase:meta 表中的,而不用再存储在 ZooKeeper 中。

如果建表执行了一半,Master 进程故障,如何处理?这里是由 HBase 自身提供的一个名为 **Procedure(V2)** 的框架来保障操作的事务性的,备 Master 接管服务以后,将会继续完成整个建表操作。

一个被创建成功的表,还可以被执行如下操作:

  • Disable  将所有的 Region 下线,该表暂停读写服务

  • Enable  将一个 Disable 过的表重新 Enable,也就是上线所有的 Region 来正常提供读写服务

  • Alter  更改表或列族的描述信息

Master 分配 Regions

新创建的所有的 Regions,通过AssignmentManager将这些 Region 按照轮询 (Round-Robin) 的方式分配到每一个 RegionServer 中,具体的分配计划是由LoadBalancer来提供的。

AssignmentManager 负责所有 Regions 的分配 / 迁移操作,Master 中有一个定时运行的线程,来检查集群中的 Regions 在各个 RegionServer 之间的负载是否是均衡的,如果不均衡,则通过 LoadBalancer 生成相应的 Region 迁移计划,HBase 中支持多种负载均衡算法,有最简单的仅考虑各 RegionServer 上的Regions 数目的负载均衡算法,有基于迁移代价的负载均衡算法,也有数据本地化率优先的负载均衡算法,因为这一部分已经提供了插件化机制,用户也可以自定义负载均衡算法。

总结

本文主要介绍了如下内容:

1. HBase 项目概述,呈现了 HBase 社区的活跃度以及搜索引擎热度等信息

2. HBase 数据模型部分,讲到了 RowKey,稀疏矩阵,Region,Column Family,KeyValue 等概念

3. 基于 HBase 的数据模型,介绍了 HBase 的适合场景(以实体/事件为中心的简单结构的数据)

4. 介绍了 HBase 与 HDFS 的关系

5. 介绍了集群的关键角色:ZooKeeper, Master, RegionServer,NameNode, DataNode

6. 集群部署建议

7. 给出了一些示例数据

8. 写数据之前的准备工作:建立集群连接,建表(建表时应该定义合理的 Schema 以及设置合理的 Region 数量),建表由 Master 处理,新创建的 Regions 由 Region AssignmentManager 负责分配到各个 RegionServer。

下一篇文章将正式开始介绍写数据的流程。

致谢

感谢 Apache HBase PMC 成员 Ted Yu,李钰,张铎对本文中与 2.0 版本相关特性 / 流程描述内容方面的 Review,感谢王琼、向宏伟以及陈土炎等几位同学给本文内容方面提出的宝贵意见。感谢毕美杰为本文提供的漂亮的封面图片:)


更多高质资源 尽在AIQ 机器学习大数据 知乎专栏 点击关注

转载请注明 AIQ - 最专业的机器学习大数据社区  http://www.6aiq.com