接触过大数据的朋友肯定对HBase不陌生,它是一个分布式的、面向列的开源数据库,HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库。另一个不同的是HBase基于列的而不是基于行的模式。周末参加了一场HBase的培训并参考网络资料,以供回顾总结。

 

为什么用HBase

在放出知识点前我们要先知道为什么需要用HBase,否则只是纸上谈兵了。

 

普通的业务系统,一般没必要接入HBase,毕竟相比传统的关系型数据库,分布式数据库还是蛮复杂的,如果一个系统一天就能产生1TB的数据,或者业务表很大,达到几千万上亿数据,比如各种流水数据,船舶GPS信息,我们金融行业的消费信息,贷款信息,信用卡还款信息以及电商的交易信息、物流信息和通信信息等,这些数据存MySQL、oracle里效率会很低。

 

如此大的量数据,我们现在的做法是先写到Kafka,然后落到Hive中,或者用Elasticsearch,不过目前ES主要用来做日志分析(传送门),它是也是一个分布式的数据库,用来做系统搜索引擎,主要用于检索。

 

所以HBase的主要应用场景要知道:

1. 写密集型应用,每天写入量巨大,而相对读数量较小的应用,各种交易流水消息,系统日志等等

2. 不需要复杂查询条件来查询数据的应用,HBase只支持基于rowkey的查询,对于HBase来说,单条记录或者小范围的查询是可以接受的,大范围的查询由于分布式的原因,可能在性能上有点影响,而对于像SQL的join等查询,HBase无法支持。

3. 对性能和可靠性要求非常高的应用,由于HBase本身没有单点故障,可用性非常高。数据量较大,而且增长量无法预估的应用,HBase支持在线扩展,即使在一段时间内数据量呈井喷式增长,也可以通过HBase横向扩展来满足功能。

 

HBase 架构

Hbase 是由三种类型的 server 组成的的主从式(master-slave)架构:

  1. Region Server 负责处理数据的读写请求,客户端请求数据时直接和 Region Server 交互。
  2. HBase Master 负责 Region 的分配,DDL(创建,删除 table)等操作。
  3. Zookeeper,作为 HDFS 的一部分,负责维护集群状态。

底层的存储都是基于 Hadoop HDFS, Hadoop DataNode 负责存储Region Server 所管理的数据。所有的 HBase 数据都存储在 HDFS 文件中。Region Server 和 HDFS DataNode 往往是分布在一起的,这样 Region Server 就能够实现数据本地化(data locality,即将数据放在离需要者尽可能近的地方)。HBase 的数据在写的时候是本地的,但是当 region 被迁移的时候,数据就可能不再满足本地性了,直到完成 compaction,才能又恢复到本地。

Hadoop NameNode 维护了所有 HDFS 物理 data block 的元信息。


 

HBASE为什么不是关系型 为什么用hbase_redis

HBase Master

也叫 HMaster,负责 Region 的分配,DDL(创建,删除表)等操作,筹协调所有 region server,启动时分配 regions,在故障恢复和负载均衡时重分配 regions,监控集群中所有 Region Server 实例(从 Zookeeper 获取通知信息),管理员功能:提供创建,删除和更新 HBaseTable 的接口

HBASE为什么不是关系型 为什么用hbase_分布式_02

Zookeeper

HBase 使用 Zookeeper 做分布式管理服务,来维护集群中所有服务的状态。Zookeeper 维护了哪些 servers 是健康可用的,并且在 server 故障时做出通知。Zookeeper 使用一致性协议来保证分布式状态的一致性。注意这需要三台或者五台机器来做一致性协议。

HBASE为什么不是关系型 为什么用hbase_redis_03

Regions

HBase Table)根据 rowkey 的范围被水平拆分成若干个 region。每个 region 都包含了这个region 的 start key 和 endkey 之间的所有row)。Regions 被分配给集群中的某些节点来管理,即 RegionServer,由它们来负责处理数据的读写请求。每个 Region Server 大约可以管理 1000 个 regions。

HBASE为什么不是关系型 为什么用hbase_java_04

RegionServer

Region Server 运行在 HDFS DataNode 上,由以下组件组成:

  • WAL:Write Ahead Log 是分布式文件系统上的一个文件,用于存储新的还未被持久化存储的数据,它被用来做故障恢复。
  • BlockCache:这是读缓存,在内存中存储了最常访问的数据,是 LRU(Least Recently Used)缓存。
  • MemStore:这是写缓存,在内存中存储了新的还未被持久化到硬盘的数据。当被写入硬盘时,数据会首先被排序。注意每个 Region 的每个 Column Family 都会有一个 MemStore。
  • HFile 在硬盘上(HDFS)存储 HBase 数据,以有序 KeyValue 的形式。

HBASE为什么不是关系型 为什么用hbase_数据库_05

HBase读写数据步骤

当客户端发起一个写数据请求(Put 操作),第一步首先是将数据写入到 WAL 中,新数据会被顺序追加到 WAL 文件尾部,WAL 用来在故障恢复时恢复还未被持久化的数据。数据被写入 WAL 后,会被加入到 MemStore 即写缓存。然后服务端就可以向客户端返回 ack 表示写数据完成。

HBASE为什么不是关系型 为什么用hbase_HBASE为什么不是关系型_06

简单过程为:

写请求过程:client---------->WAL(WriteAhead LOG)----------->MemStore-------------> HFile--------->END

读请求过程:client---------->MemStore-------->BlockCache------->HFile------------>END

 

HBase数据写入首先会写入缓存,缓存写满会执行一次flush操作,每次flush都会生成一个HFile文件。随着HFile的增多,文件的读取效率势必会降低,HBase采用compact机制不断的对这些文件进行合并,将小文件合并成大文件。所以Compact 的作用就是:

1、合并文件

2、清除过期,多余版本的数据

3、提高读写数据的效率

 

 

 

HBaseRegion Flush

MemStore 中累积了足够多的的数据后,整个有序数据集就会被写入一个新的 HFile 文件到 HDFS 上。HBase为每个 Column Family 都创建一个 HFile,里面存储了具体的 Cell,也即 KeyValue 数据。随着时间推移,HFile 会不断产生,因为 KeyValue 会不断地从 MemStore 中被刷写到硬盘上。

注意这也是为什么 HBase 要限制 Column Family 数量的一个原因。每个 Column Family 都有一个 MemStore;如果一个 MemStore 满了,所有的 MemStore 都会被刷写到硬盘。

HBaseMinor Compaction

HBase 会自动合并一些小的 HFile,重写成少量更大的 HFiles。这个过程被称为 minorcompaction。它使用归并排序算法,将小文件合并成大文件,有效减少HFile 的数量。

HBASE为什么不是关系型 为什么用hbase_HBASE为什么不是关系型_07

HBase Major Compaction

Major Compaction 合并重写每个 Column Family 下的所有的 HFiles,成为一个单独的大 HFile,在这个过程中,被删除的和过期的 cell 会被真正从物理上删除,这能提高读的性能。但是因为 majorcompaction 会重写所有的 HFile,会产生大量的硬盘 I/O 和网络开销。这被称为写放大Write Amplification)。

Major compaction 可以被设定为自动调度。因为存在 write amplification 的问题,major compaction 一般都安排在周末和半夜。MapR 数据库对此做出了改进,并不需要做 compaction。Major compaction 还能将因为服务器 crash 或者负载均衡导致的数据迁移重新移回到离 Region Server 的地方,这样就能恢复 datalocality

HBASE为什么不是关系型 为什么用hbase_HBASE为什么不是关系型_08

 

HBase故障恢复

当某个 Region Server 发生 crash 时,它所管理的 region 就无法被访问了,直到 crash 被检测到,然后故障恢复完成,这些 region 才能恢复访问。Zookeeper 依靠心跳检测发现节点故障,然后 HMaster 会收到 region server 故障的通知。

当 HMaster 发现某个 region server 故障,HMaster 会将这个 region server 所管理的 regions 分配给其它健康的 region servers。为了恢复故障的 region server 的 MemStore 中还未被持久化到 HFile 的数据,HMaster 会将 WAL 分割成几个文件,将它们保存在新的 region server 上。每个 region server 然后回放各自拿到的 WAL 碎片中的数据,来为它所分配到的新 region 建立 MemStore。

HBASE为什么不是关系型 为什么用hbase_redis_09

WAL 包含了一系列的修改操作,每个修改都表示一个 put 或者 delete 操作。这些修改按照时间顺序依次写入,持久化时它们被依次写入 WAL 文件的尾部。

当数据仍然在 MemStore 还未被持久化到 HFile 怎么办呢?WAL 文件会被回放。操作的方法是读取 WAL 文件,排序并添加所有的修改记录到 MemStore,最后 MemStore 会被刷写到 HFile。

HBASE为什么不是关系型 为什么用hbase_数据库_10

(点评:故障恢复是 HBase 可靠性保障的一个重要特性。WAL 在这里扮演了关键角色,在分割 WAL 时,数据会根据 region 分配到对应的新的 region server 上,然后 region server 负责回放这一部分数据到 MemStore 中。)

 

HBase架构的优点

  • 强一致性:当 write 返回时,所有的 reader 都会读到同样的值。
  • 自动扩展性
  • 数据变大时 region 会分裂,用 HDFS 存储备份数据。
  • 内置恢复功能,使用 Write Ahead Log (类似于文件系统中的日志)
  • 与 Hadoop 结合:使用 MapReduce 处理 HBase 数据会非常直观。

HBase 架构的缺点

  • 业务持续可靠性:
  • WAL 回放很慢。
  • 故障恢复很慢。
  • Major Compaction 时候 I/O 会飙升