1

一、HBase产生背景

在大数据时代,传统的关系型数据库(如Mysql、Oracle)在大数据量下的并发读写及可拓展性方面遇到瓶颈,尤其是处理海量的非结构化、半结构化数据时效率较低,而Hadoop的HDFS虽然支持海量数据的存储以及批处理,但其无法支持随机读写和低延迟查询(HDFS 中的文件一旦写入不能修改,只能追加),所以HBase被设计出来,弥补了HDFS在实时访问能力上的不足。

HBase是一个基于列式存储的分布式数据库(实质是一个KV数据库),它的设计灵感来自于Google 2006 年发表的论文《Bigtable: A Distributed Storage System for Structured Data》(BigTable也是一种基于列式存储、支持高吞吐、低延迟、可管理海量数据的分布式数据库)

HBase主要有以下几点特性:

  1. 提供实时读写能力(采用LSM树 + 内存缓存的方式实现毫秒级的随机读写)
  2. 采用列式存储,适合存储半结构化、非结构化的数据(无法明确有哪些列的情况)
  3. 高拓展性和高可用性(通过Region分片机制和Zookeeper协调服务,可拓展至数百节点)
  4. 多版本控制(默认保留3个历史版本)

二、HBase系统架构

HBase也是采用的主从架构,并且依赖Zookeeper实现动态扩展、故障转移。系统架构图如下:
image.png
主要有以下几种组件:
HMaster:集群的Master节点

  • 元数据管理:管理表的元数据(如表的 Schema、列族配置,存储路径为 /hbase/data/<命名空间>/<表名>/.tableinfo)和 Region 的分布信息(存储在hbase:meta系统表中,存储路径为 /hbase/data/hbase/meta/)
  • Region的分配:负责 Region 的分配 及 RegionServer 故障时的 Region 迁移
  • 处理DDL操作:处理表的增、删、改操作
  • 维护系统表:管理hbase:meta系统表

HRegionServer:Region的管理节点

  • 管理Region:每个RegionServer会托管多个Region,处理这些Region的读写请求
  • Region分裂:当Region大小达到阈值,会对Region进行分裂
  • 依赖HDFS存储:会将StoreFile和WAL日志持久化到HDFS

Zookeeper:集群的协调服务

  • 监控RegionServer:当RegionServer存活状态异常,会通知HMaster进行Region迁移
  • 存储hbase:meta系统表的位置:客户端通过zookeeper获取Region的信息
  • HMaster的故障转移:当主节点宕机,会选举出一个新的leaer作为HMaster节点

HDFS:底层数据存储

  • 数据持久化:存储所有 Region 的 StoreFile(HFile 格式),并且还存储WAL日志
  • 高可用:多副本机制保障数据安全

这里还有相关的一些概念:
Region:表部分数据的集合,表会根据RowKey值被切分成不同的 Region 被不同的RegionServer托管,这些Region组合起来就是一张表(每RegionServer会托管多个Region
Store:每个Store对应HBase表的一个列族的数据
MemStore:写缓存,数据会先写到MemStore中,并排好序,当MemStore达到阈值,则会刷写到HDFS中,形成一个新的StoreFile
StoreFile:保存数据的实际物理文件,以HFile格式存储在HDFS上(每个Store会有多个StoreFile,并且每个StoreFile内部有序

三、HBase数据模型

HBase的数据模型如下图
image.png
Row:每行数据由一个RowKey和多个列组成,数据按照 RowKey字典顺序存储,所以在HBase中RowKey的设计是比较重要的
Column Family:列族,每个列族可以有多个列,并且可动态添加列,一个列族物理存储时对应一个Store
Column:建表时无需声明列,只需要声明列族即可,列可以动态添加
TimeStamp:每条数据都有对应的时间戳,用于标识数据的不同版本(默认保留3个版本
Cell:通过{RowKey, 列族:列, TimeStamp} 唯一确定一个值

四、如何设计Rowkey?

由于HBase存储数据时,是按照RowKey字典顺序存储,所以要充分利用RowKey排序的特点。

  1. 长度要求:RowKey本身可以是任意字符串,最大长度64k,但实际应用中一般小于100byte,因为RowKey本身也是占用存储空间的,假设一个RowKey 100个字节,一亿条数据就有10G空间大小,所以RowKey不宜过长
  2. 大数据量的散列设计:由于HBase有多个RegionServer,所以要充分利用其并行处理的能力,假设RowKey按递增的方式,那么数据会产生热点问题,大部分的数据会集中在某一两个Region里,这样无法利用整个集群的处理能力,所以数据量大的时候一般采用散列设计
  3. 频繁访问的就近设计:假设频繁访问某一部分数据,那么这些数据的RowKey的前缀应尽量保持一致,这样可以保证这些RowKey是存储到同一个或相邻的Region里,有利于提高读取效率

五、HBase的读写流程

5.1 读取数据

image.png

客户端读取数据时,大致流程如下:

  1. 客户端连接Zookeeper,获取 hbase:meta 系统表的位置信息(存放在哪个RegionServer上)
  2. 客户端连接 hbase:meta 表所在的RegionServer,查询 hbase:meta 系统表(meta表存储了所有表的Region信息),客户端通过RowKey匹配到目标Region所在的RegionServer(可能匹配到多个Region)
  3. 客户端连接对应的RegionServer,携带RowKey发送读取请求
  4. RegionServer根据RowKey定位到Region,Region内部按列族划分为多个Store,每个Store包含一个MemStore和多个StoreFile(以HFile格式存储),读取时通过 多层索引 快速定位HFile磁盘文件中的目标数据块,并且还会查询MemStore中的数据
  5. 将多个HFile的查询结果和MemStore的查询数据进行合并,按时间戳排序,将最新的数据返回给客户端

5.2 写入数据

image.png

  1. 客户端连接Zookeeper,获取 hbase:meta 系统表,定位到目标Region所在的RegionServer(与读取类似)
  2. 客户端连接对应的RegionServer,发起写入请求
  3. RegionServer会先将写入操作记录到预写日志(WAL)中(WAL文件是存储在HDFS上的),再将数据写入到MemStore写缓存(MemStore是有序的内存结构
  4. 写入MemStore成功后立即向客户端返回写入成功
  5. MemStore数据达到阈值时,触发flush操作,将内存数据持久化成StoreFile(每次都会生成一个新的HFile,并按RowKey有序存储)
  6. 随着越来越多的HFile产生,HBase会定期合并这些这些HFile,包括Minor Compaction和Major Compaction(Minor Compaction会将相邻的多个HFile合并成一个大的HFile,而Major Compaction会将一个列族的所有HFile全部合并为一个HFile)

六、Region的分裂

Region不可能无限扩大,所以当Region的大小超过阈值(默认10G)时,HBase会将其分裂为两个子Region,以实现负载均衡。

触发条件

  • 默认策略:当Region的大小超过阈值(默认10G)时触发分裂
  • 其他策略:可以通过 hbase.regionserver.region.split.policy 进行配置
  • 手动触发:通过shell或api主动执行split命令

分裂步骤

  1. HBase 自动选择 Region 中间位置的 RowKey 作为分裂点
  2. 父 Region 被标记为 SPLITTING 状态,暂停父 Region 的写入,分裂为两个子 Region,创建两个子 Region 目录
  3. HMaster 将两个子 Region 分配到其他 RegionServer(或当前节点)
  4. 更新hbase:meta系统表,添加两个子Region的元数据

七、总结

HBase作为Hadoop 生态的"热数据"存储,优势是具备高拓展、高吞吐写、低延迟随机读以及与Hadoop生态的无缝集成,但缺点也很明显,强依赖Zookeeper和HDFS,运维复杂度较高,并且无法进行复杂查询、分析,所以HBase更适用于高并发写入(如日志系统)、实时查询(如用户画像)、动态列等应用场景。


kamier
1.5k 声望497 粉丝