HBase入门教程

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


这种表结构规整,每一行都有固定的列构成,所以,很是适合结构化数据的存储。但在NoSQL领域,数据表的模样却每每换成了另一种”画风”:github


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

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

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

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

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

HBase在国内市场已经取得了很是普遍的应用,在搜索引擎中,也能够看出来,HBase在国内呈现出了逐年上升的势态:架构


从Apache HBase所关联的github项目的commits统计信息来看,也能够看出来该项目很是活跃:并发


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

使人欣喜的是,国内的开发者也积极参与到了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”, “defg”}按字典排序后的结果为{“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主要职责

    1. 负责管理全部的RegionServer

    2. 建表/修改表/删除表等DDL操做请求的服务端执行主体

    3. 管理全部的数据分片(Region)到RegionServer的分配

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

    5. Master自身也能够做为一个RegionServer提供服务,该能力是可配置的

集群部署建议

若是基于物理机/虚拟机部署,一般建议:

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

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


  • 管理节点独立于数据节点部署

若是是基于物理机部署,每一台物理机节点上能够设置几个RegionServers/DataNodes来提高资源使用率。

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

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

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

示例数据

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


本文内容力求简洁,仅给出了几个简单字段。以下是”虚构”的样例数据:


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

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

写数据以前:创建链接

Login

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

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

建立Connection

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

CompletableFuture<AsyncConnection> createAsyncConnection(Configuration conf,
                 User user);

Connection createConnection(Configuration conf, ExecutorService pool, User user)
      throws IOException;

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

  • 线程池

  • Socket链接

这些资源都是在真正使用的时候才会被建立,所以,此时的链接还只是一个”虚拟链接”。

写数据以前:建立数据表

DDL操做的抽象接口 – Admin

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

void createNamespace(final NamespaceDescriptor descriptor) throws IOException;

void createTable(final HTableDescriptor desc, byte[][] splitKeys) throws IOException;

TableName[] listTableNames() throws IOException;

预设合理的数据分片 – Region

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

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

  • Key的组成结构

  • Key的数据分布预估

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

  • 读写并发度需求

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

为表定义合理的Schema

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

  • NameSpace设置

  • Column Family的数量

  • 每个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到各个RegionServers

新建立的全部的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。

  9. ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------