1.基本概念
1.1简介
Apache HBase(Hadoop DataBase)是一个开源的、高可靠性、高性能、面向列(这里指列族,非列式存储)、可伸缩、实时读写的分布式数据库,其设计思想来源于 Google 的 BigTable 论文。利用 Hadoop HDFS 作为其文件存储系统,利用 ZooKeeper 作为其分布式协同服务。主要用来存储非结构化和半结构化的松散数据(列式存储 NoSQL 数据库)。
HBase 良好的分布式架构设计为海量数据的快速存储、随机访问提供了可能,基于数据副本机制和分区机制可以轻松实现在线扩容、缩容和数据容灾,是大数据领域中 Key-Value 数据结构存储最常用的数据库方案。
1.2特点
易扩展:基于运算能力和基于存储能力的扩展,增加节点数量;
容量大:单表可以有十亿行、百万列,面向PB级数据实时入库和快速访问;
面向列:列族存储;
多版本:列数据支持version,及历史记录;
稀疏性:为空的列不占用存储内存,表设计可以稀疏;
高性能:底层采用LSM树(Log-Structured Merge-Tree)数据结构进行存储;
高可靠:日志先行机制,先写日志再执行操作,采用可靠的HDFS;
1.3应用
Hbase是一种NoSQL数据库;
海量分布式数据管理;
存储业务数据:车辆 GPS 信息,司机点位信息,用户操作信息,设备访问信息。
存储日志数据:架构监控数据(登录日志,中间件访问日志,推送日志,短信邮件发送记录),业务操作日志信息。
存储业务附件:UDFS 系统(去中心化文件系统)存储图像,视频,文档等附件信息。
1.3应用
存储业务数据:车辆 GPS 信息,司机点位信息,用户操作信息,设备访问信息。
存储日志数据:架构监控数据(登录日志,中间件访问日志,推送日志,短信邮件发送记录),业务操作日志信息。
存储业务附件:UDFS 系统(去中心化文件系统)存储图像,视频,文档等附件信息。
2.数据模型(重点)
HBase 表中,一条数据拥有一个全局唯一的主键(RowKey)和任意数量的列(Column Qualifier),每个列的数据存储支持多个版本(Version),一列或多列组成一个列族(Column Family),同一个列族中列的数据在物理上都存储在同一个 HFile 中。这样基于列存储的数据结构有利于数据缓存和查询。
所以,在 HBase 中定位一条数据需要通过:RowKey → Column Family → Column Qualifier → Version。
HBase 表中的数据是疏松地存储的,因此用户可以动态地为数据定义各种不同的列。HBase 中的数据按主键排序(字典序),同时 HBase 会将表按主键划分为多个 HRegion 存储在不同的 HRegionServer 上,以完成数据的分布式存储和读取。
2.1. NameSpace
命名空间类似于关系型数据库中的数据库的概念,逻辑分组;
default:没有明确指定命名空间的表将自动落入此命名空间
hbase:系统命名空间,用于包含 HBase 的内部表和元数据表
2.2. Table
Table 和关系型数据库中的表一个意思,由行和列组成。
2.3. RowKey
唯一标识一行数据;
2.4. Column Family
列族;
2.5. Column Qualifier
列族的限定词,理解为列的唯一标识。
使用的时候必须 列族:列 ,列可以根据需求动态添加或者删除,同一个表中不同行的数据列都可以不同。
2.6. Timestamp
时间戳;
一是保存数据的最后 n 个版本
二是保存最近一段时间内的版本(比如最近七天)
2.7. Cell
Cell 由 Row,Column Family,Column Qualifier,Version 组成。Cell 中的数据是没有类型的,全部使用字节码形式存贮,因为 HDFS 上的数据都是字节数组。
3. 架构模型(重点)
HBase 可以将数据存储在本地文件系统,也可以存储在 HDFS 文件系统。在生产环境中,HBase 一般运行在 HDFS上,以 HDFS 作为基础的存储设施。用户通过 HBase Client 提供的 Shell 或 Java API 来访问 HBase 数据库,以完成数据的写入和读取。HBase 集群主要HMaster、HRegionServer 和 ZooKeeper 组成。
布隆过滤器:Hash算法拦截无效请求;
3.1. ZooKeeper
选主;
监控HRegionServer状态,有节点故障通知HMaster迁移数据;
维护元数据表的元数据,集群配置
3.2. Client
Client 维护着一些 Cache 来加快对 HBase 的访问,比如 HRegione 的位置信息。
发送查询请求
3.3. HMaster
管理分配:将HRegion分配给不同的HRegionServer;
负载均衡:防止单个HRegionServer数据过多,压力过大;
维护数据:迁移故障节点数据至其它节点
权限控制;
3.4. HRegionServer
工作节点;
实时和 HMaster 保持心跳,汇报当前节点的信息;
当接收到 HMaster 的命令创建表时,会分配一个 HRegion 对应一张表;
负责切分在运行过程中变得过大的 HRegion;
当 HRegionServer 意外关闭的时候,当前节点的 HRegion 会被其他 HRegionServer 管理;
3.5. HRegion
一个RowKey空间
3.5.1. 负载均衡
3.5.2. Split
在 HBase 中 Split 是一个很重要的功能,HBase 是通过把数据分配到一定数量的 HRegion 来达到负载均衡的。就是当HRegion过大时将HRegion切分成小分;
0.94版本之前默认达到10G后切分;
0.94版本之后采用一个公式:
hbase.hregion.memstore.flush.size 默认值 128MB。
hbase.hregion.max.filesize 默认值为 10GB。
如果初始时 R=1 ,那么 Min(128MB, 10GB)=128MB ,也就是说在第一个 Flush 的时候就会触发分裂操作。
当 R=2 的时候 Min(2*2*128MB, 10GB)=512MB ,当某个 StoreFile 大小达到 512MB 的时候,就会触发分裂。
如此类推,当 R=9 的时候,StoreFile 达到 10GB 的时候就会分裂,也就是说当 R>=9 的时候,StoreFile 达到 10GB 的时候就会分裂。
3.6. Store
一个 HRegion 由多个 Store 组成,每个 Store 都对应一个 Column Family,Store 包含 1 个 MemStore 和 0 或多个StoreFile 组成。
MemStore:作为 HBase 的内存数据存储,数据的写操作会先写到 MemStore 中,当 MemStore 中的数据增长到指定阈值(默认 128M)后,HRegionServer 会启动 FlushCache 进程将 MemStore 中的数据写入 StoreFile 持久化存储,每次写入后都形成一个单独的 StoreFile。当客户端检索数据时,先在 MemStore 中查找,如果 MemStore 中不存在,则会在
StoreFile 中继续查找。
StoreFile:MemStore 中的数据写到文件后就是 StoreFile,StoreFile 底层是以 HFile 格式保存的。HBase 以 StoreFile 的大小来判断是否需要切分 HRegion。当一个 HRegion 中所有 StoreFile 的大小和数量都增长到超过指定阈值时,HMaster会把当前 HRegion 分割为两个,切分后其中一个 HRegion 会被转移到其他的 HRegionServer 上,实现负载均衡。
HFile:HFile 和 StoreFile 是同一个文件,只不过站在 HDFS 的角度称这个文件为 HFile,站在 HBase 的角度就称这个文件为 StoreFile。是 HBase 在 HDFS 中存储数据的格式,它包含多层的索引,这样在 HBase 检索数据的时候就不用完全的加载整个文件。
3.7. HFile
HFile 小节内容基于 HBase官网和《HBase原理与实践》。
3.8. HLog
一个 HRegionServer 只有一个 HLog 文件。负责记录数据的操作日志,当 HBase 出现故障时可以进行日志重放、故障恢复。例如磁盘掉电导致 MemStore 中的数据没有持久化存储到 StoreFile,这时就可以通过 HLog 日志重放来恢复数据。
HBase是一个日志先行系统,做什么事情都是先写日志;
4. BlockCache
4.1. LRUBlockCache
近期最少使用算法;
基于JVM,老年代GC时会让系统停滞一段事件,不推荐;
4.2. SlabCache
使用本地内存,基于JVM元空间,实现堆外内存存储,不再由 JVM 管理数据内存。
SlabCache 中固定大小内存设置会导致实际内存使用率比较低,而且使用 LRUBlockCache 缓存 Block 依然会因为 JVM GC 产生大量内存碎片。因此在 HBase 0.98 版本之后,已经不建议使用该方案(已废弃)。
4.3. BucketCache
BucketCache 借鉴了 SlabCache 的创意,也用上了堆外内存。不过它是这么用的:相比起只有 2 个区域的SlabeCache,BucketCache 一上来就分配了 14 种区域。
这 14 种区域分别放的是大小为 4KB、8KB、16KB、32KB、40KB、 48KB、56KB、64KB、96KB、128KB、192KB、256KB、384KB、 512KB 的 Block。而且这个种类列表还是可以手动通过设置 hbase.bucketcache.bucket.sizes 属性来定义。
5. 集群搭建
6. 交互方式
6.1. HBase Shell
help查看,或直接网上搜索。
6.2. Java API
业内人员不敲,直接浏览了解即可。
7. 读写流程(重点)
7.1. 三层索引
7.1.1. HBase 0.96 以前
7.1.2. HBase 0.96 以后
7.2. 读取数据流程
7.2.1. 数据组织
7.2.2. 面试题
面试题:为什么 HBase 可以做到百亿数据秒级查询?
HBase 适合存储 PB 级别的海量数据(百亿千亿量级条记录),如果根据记录主键 RowKey 来查询,能在几十到百毫秒内返回数据。HBase 是如何做到的呢?接下来,简单阐述一下数据的查询思路和过程。
7.3. 写入数据流程
Client 访问 ZooKeeper,获取 hbase:meta 所在 HRegionServer 的节点信息;
Client 访问 hbase:meta 所在的 HRegionServer,获取 hbase:meta 记录的元数据后先加载到内存中,然后再从内存中查询出 RowKey 所在的 HRegion (HRegion 所在的 HRegionServer);
Client 对 RowKey 所在的 HRegion 对应的 HRegionServer 发起写入数据请求;
建立连接后,首先将 DML 要做的操作写入到日志 HLog;
然后将数据的修改更新到 MemStore 中,本次操作结束。一个 HRegion 由多个 Store 组成,一个 Store 对应一个列族,Store 包括位于内存中的 Memstore 和位于磁盘的 StoreFile,写操作先写入 MemStore;
当 MemStore 数据达到阈值后(默认 128M),创建一个新的 MemStore;
旧的 MemStore 将刷写为一个独立的 StoreFile(HRegionServer 会启动 FlushCache 进程写入 StoreFile)并存放到HDFS,最后删除 HLog 中的历史数据。
当 StoreFile 文件的数量增长到一定阈值后,系统会进行合并(次/小 Minor Compaction、主/大 Major Compaction);
在合并过程中会进行版本合并和删除工作,形成更大的 StoreFile;
当一个 HRegion 所有 StoreFile 的大小和数量超过一定阈值后,会把当前的 HRegion 分割为两个,并由 HMaster 分配到相应的 HRegionServer 服务器,实现负载均衡。
访问Zookeeper 获取HBase元数据表所在HRegionServer,访问其;
通过元数据表查询RowKey所在HRegion,然后对所在HRegionServer发起写入请求;
建立连接后先写日志;
然后将数据的修改更新到 MemStore 中,本次操作结束。
MemStore达到默认128M后刷写数据(StoreFile/HFile)至HDFS。
8. 数据刷写(次重点)
8.1. 触发时机
8.1.1. 内存阈值
HRegion 中的每个 MemStore 占用的内存超过相关阈值 hbase.hregion.memstore.flush.size 时会触发刷写,默认为 128MB。
如果我们的数据增加得很快,达到了 hbase.hregion.memstore.flush.size * hbase.hregion.memstore.block.multiplier(默认为 4) 的大小,也就是 128 * 4 = 512MB 的时候,除了触发 MemStore 刷写之外,HBase 还会在刷写的时候阻塞所有写入该Store 的请求。
8.1.2. 内存总和
整个 HRegionServer 的 MemStore 占用内存总和大于相关阈值时会触发刷写。如果达到了 HRegionServer 级别的刷写,当前 HRegionServer 的所有写操作将会被阻塞,这个阻塞可能会持续到分钟级别。
HBase 为 HRegionServer 所有的 MemStore 分配了一定的写缓存,大小等于 hbase_heapsize(HRegionServer 占用的堆内存大小)*hbase.regionserver.global.memstore.size(默认为 0.4)。
相关阈值计算公式为: hbase_heapsize * hbase.regionserver.global.memstore.size *
hbase.regionserver.global.memstore.size.lower.limit(默认为 0.95) = MAX_SIZE 。例如:HBase 堆内存总共是 32G,MemStore 占用内存为:32 * 0.4 * 0.95 = 12.16G 将触发刷写。
8.1.3. 日志阈值
日志到达一定的数量时进行一次刷写操作。相关公式为:Math.max(32, hbase_heapsize *
hbase.regionserver.global.memstore.size * 2 / logRollSize)。
8.1.4. 定期刷写
HBase 会自动触发刷写。一般建议调大,比如 10 小时,因为很多场景下 1 小时 Flush 一次会产生很多小文件,一方面导致Flush 比较频繁,另一方面导致小文件很多,影响随机读性能。
8.1.5. 更新频率
如果 HBase 的某个 HRegion 更新的很频繁,而且既没有达到自动刷写阀值,也没有达到内存的使用限制,但是内存中的更新数量已经足够多,比如超过 hbase.regionserver.flush.per.changes 参数配置,默认为 30000000 次,也会触发刷写。
8.1.6. 手动刷写
8.1.7. 注意
8.2. 刷写策略
8.3. 刷写流程
8.3.1. prepareFlush 阶段
对 MemStore 做 Snapshot(快照),防止突然出现故障;
8.3.2. flushCache 阶段
flushCache 阶段:将 prepareFlush 阶段创建好的快照写到临时文件里面,临时文件是存放在对应 HRegion 文件夹下面的 .tmp 目录里面。
commit 阶段:将 flushCache 阶段生产的临时文件移到(rename)对应的列族目录下面,并做一些清理工作,比如删除第一步生成的 Snapshot。
9. 数据合并(次重点)
9.1. 合并分类
HBase 根据合并规模将压实 Compaction 分为了两类:Minor Compaction 和 Major Compaction。
Minor Compaction:快速让小文件合并成大文件
Major Compaction:清理大文件不必要的数据,释放空间
9.2. 合并时机
触发 Compaction 的方式有三种:MemStore 刷盘、后台线程周期性检查、手动触发。
9.3. 合并策略
HBase 主要有两种 Minor Compaction 策略:RatioBasedCompactionPolicy(0.96.x 之前) 和 ExploringCompactionPolicy(当前默认)。
10. 数据切分(次重点)
通过切分,一个 HRegion 变为两个近似相同大小的子 HRegion,再通过 balance 机制均衡到不同 HRegionServer上,使系统资源使用更加均衡。
10.1. 切分原因
数据分布不均匀;
Compaction 性能损耗严重;
资源耗费严重;
10.2. 触发时机
0.94之前和之后 同Split
10.3. 切分流程
寻找切分点:基于 HRegion 执行的,一段区间;
开启切分事务;
prepare 阶段:日志先行,副本创建;
execute 阶段;
rollback 阶段:出现异常回退;
切分优化;
11. 表设计(重点)
11.1. 行健设计
唯一性;
长度相同;
散列;
HBase 的 RowKey 设计需要遵循以下原则:
唯一原则:
单主键
组合主键(注意顺序)
长度原则:
不要超过 16 个字节
对齐 RowKey 长度
散列原则:
反转
加盐
Hash
11.2. 列族设计
追求原则:在合理范围内,尽可能的减少列族。
最优设计:将所有相关性很强的 key-value 都放在同一个列族。这样既能做到查询效率最高,也能保证尽可能少的访问不同的磁盘文件。
控制长度:列族名的长度要尽量小,一个为了节省空间,一个为了加快效率,最好是一个字符,比如 d 表示 data 或default,v 表示 value。
12. 案例设计
13. 常用优化(进阶掌握)
13.1. 表优化
预分区;
13.2. 写入优化
多 Table 并发写;
13.3. 读取优化
作为 NoSQL 数据库,增删改查是其最基本的功能,其中查询是最常用的一项,接下来聊聊查询相关的优化。
13.4. 缓存优化
Scan缓存;
缓存块;
缓存查询结果;
14. 框架整合
HBase 和 MapReduce
HBase 和 Hive
15. HBase 缺点