一、HBase产生背景
在大数据时代,传统的关系型数据库(如Mysql、Oracle)在大数据量下的并发读写及可拓展性方面遇到瓶颈,尤其是处理海量的非结构化、半结构化数据时效率较低,而Hadoop的HDFS虽然支持海量数据的存储以及批处理,但其无法支持随机读写和低延迟查询
(HDFS 中的文件一旦写入不能修改,只能追加),所以HBase被设计出来,弥补了HDFS在实时访问能力
上的不足。
HBase是一个基于列式存储的分布式数据库(实质是一个KV数据库),它的设计灵感来自于Google 2006 年发表的论文《Bigtable: A Distributed Storage System for Structured Data》(BigTable也是一种基于列式存储、支持高吞吐、低延迟、可管理海量数据的分布式数据库)
HBase主要有以下几点特性:
- 提供实时读写能力(采用LSM树 + 内存缓存的方式实现毫秒级的随机读写)
- 采用列式存储,适合存储半结构化、非结构化的数据(无法明确有哪些列的情况)
- 高拓展性和高可用性(通过Region分片机制和Zookeeper协调服务,可拓展至数百节点)
- 多版本控制(默认保留3个历史版本)
二、HBase系统架构
HBase也是采用的主从架构,并且依赖Zookeeper实现动态扩展、故障转移。系统架构图如下:
主要有以下几种组件:
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的数据模型如下图
Row:每行数据由一个RowKey和多个列组成,数据按照 RowKey字典顺序存储
,所以在HBase中RowKey的设计是比较重要的
Column Family:列族,每个列族可以有多个列,并且可动态添加列
,一个列族物理存储时对应一个Store
Column:建表时无需声明列,只需要声明列族即可,列可以动态添加
TimeStamp:每条数据都有对应的时间戳,用于标识数据的不同版本(默认保留3个版本
)
Cell:通过{RowKey, 列族:列, TimeStamp}
唯一确定一个值
四、如何设计Rowkey?
由于HBase存储数据时,是按照RowKey字典顺序存储,所以要充分利用RowKey排序的特点。
- 长度要求:RowKey本身可以是任意字符串,最大长度64k,但实际应用中一般小于100byte,因为RowKey本身也是占用存储空间的,假设一个RowKey 100个字节,一亿条数据就有10G空间大小,所以RowKey不宜过长
- 大数据量的散列设计:由于HBase有多个RegionServer,所以要充分利用其并行处理的能力,假设RowKey按递增的方式,那么数据会
产生热点问题
,大部分的数据会集中在某一两个Region里,这样无法利用整个集群的处理能力,所以数据量大的时候一般采用散列设计 - 频繁访问的就近设计:假设频繁访问某一部分数据,那么这些数据的
RowKey的前缀
应尽量保持一致,这样可以保证这些RowKey是存储到同一个或相邻的Region里,有利于提高读取效率
五、HBase的读写流程
5.1 读取数据
客户端读取数据时,大致流程如下:
- 客户端连接Zookeeper,获取 hbase:meta 系统表的位置信息(存放在哪个RegionServer上)
- 客户端连接 hbase:meta 表所在的RegionServer,
查询 hbase:meta 系统表
(meta表存储了所有表的Region信息),客户端通过RowKey匹配到目标Region所在的RegionServer(可能匹配到多个Region) - 客户端连接对应的RegionServer,携带RowKey发送读取请求
- RegionServer根据RowKey定位到Region,Region内部按列族划分为多个Store,每个Store包含一个MemStore和多个StoreFile(以HFile格式存储),读取时通过
多层索引
快速定位HFile磁盘文件中的目标数据块,并且还会查询MemStore中的数据 - 将多个HFile的查询结果和MemStore的查询数据
进行合并,按时间戳排序
,将最新的数据返回给客户端
5.2 写入数据
- 客户端连接Zookeeper,获取 hbase:meta 系统表,定位到目标Region所在的RegionServer(与读取类似)
- 客户端连接对应的RegionServer,发起写入请求
- RegionServer会先将写入操作记录到
预写日志(WAL)
中(WAL文件是存储在HDFS上的),再将数据写入到MemStore写缓存(MemStore是有序的内存结构
) - 写入MemStore成功后立即向客户端返回写入成功
- MemStore数据达到阈值时,
触发flush操作
,将内存数据持久化成StoreFile(每次都会生成一个新的HFile,并按RowKey有序存储) - 随着越来越多的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命令
分裂步骤
- HBase 自动选择 Region 中间位置的 RowKey 作为
分裂点
- 父 Region 被标记为 SPLITTING 状态,暂停父 Region 的写入,
分裂为两个子 Region,创建两个子 Region 目录
- HMaster 将两个子 Region 分配到其他 RegionServer(或当前节点)
更新hbase:meta系统表
,添加两个子Region的元数据
七、总结
HBase作为Hadoop 生态的"热数据"存储
,优势是具备高拓展、高吞吐写、低延迟随机读以及与Hadoop生态的无缝集成,但缺点也很明显,强依赖Zookeeper和HDFS,运维复杂度较高,并且无法进行复杂查询、分析,所以HBase更适用于高并发写入
(如日志系统)、实时查询
(如用户画像)、动态列
等应用场景。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。