(图片来源于网络,侵删)
一、HBASE简介
【1.1】 什么是HBase
1)HBase译为“Hadoop Database”,是一个构建在HDFS之上的高可靠
、高性能
、列存储
、可伸缩
、实时读写
的NoSQL
数据库系统,利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群
NoSQL = Not Only SQL:会有一些把 NoSQL 数据的原生查询语句封装成 SQL,比如 HBase 就有 Phoenix 工具
2)因为HDFS可以存储存储结构化
和半结构化
的松散数据
,所以HBase也可以用来存储结构化
和半结构化
的松散数据
3)HBase是Google Bigtable的开源实现,但是也有很多不同之处👇
- HBase 依赖于 HDFS 做底层的数据存储,BigTable 依赖 Google GFS 做数据存储
- HBase 依赖于 MapReduce 做数据计算,BigTable 依赖 Google MapReduce 做数据计算
- HBase 依赖于 ZooKeeper 做服务协调,BigTable 依赖 Google Chubby 做服务协调
4)HBase的目标是存储并处理大型的数据
,更具体来说是仅需使用普通的硬件配置,就能够处理由成千上万的行和列所组成的大型数据
5) 扩展:结构化、半结构化和非结构化👇
术语 | 概念 |
---|---|
结构化 | 数据结构字段含义确定,清晰,典型的如数据库中的表结构 |
半结构化 | 具有一定结构,但语义不够确定,典型的如 HTML 网页,有些字段是确定的(title), 有些不确定(table) |
非结构化 | 杂乱无章的数据,很难按照一个概念去进行抽取,无规律性 |
【1.2】 与传统数据库的对比
1)关系型数据库 和 非关系型数据库的典型代表
- ①
NoSQL
:HBase、Redis、Mongodb - ②
RDBMS
:Mysql、Oracle、Sql Server、DB2
2)传统数据库遇到的问题:
- ① 数据量很大的时候无法存储
- ② 没有很好的
备份机制
- ③ 数据达到一定数量开始缓慢,很大的话基本无法支撑
3)HBASE优势:
- ① 线性扩展,随着数据量增多可以通过节点扩展进行支撑
- ② 数据存储在HDFS上,备份机制健全
- ③ 通过Zookeeper协调查找数据,访问速度快
【1.3】 HBase 中的表特点
1)大
:一个表可以有上十亿行,上百万列
2)面向列
:面向列(族)的存储和权限控制,列(簇)独立检索
3)稀疏
:对于为空(null)的列,并不占用存储空间,因此,表可以设计的非常稀疏
4)无模式
:每行都有一个可排序的主键和任意多的列,列可以根据需要动态的增加,同一 张表中不同的行可以有截然不同的列
【1.4】 HBase集群中的角色
1)一个或者多个主节点 : HMaster
2)多个从节点 : HRegionServer
3)HBase依赖项 :
- Zookeeper:管理HBase元数据、监听HMaster、HRegionServer上下线、Master选举
- HDFS:依赖HDFS作为存储、HDFS提供良好的备份机制
二、HBASE数据模型
【2.1】 Row Key
什么是Row Key?
Row Key是HBase中数据的主键,访问数据的方式都需要通过Row Key进行查询
1)访问HBase Table中的数据,只有三种
方式:
- ① 通过单个row key访问 (get)
- ② 通过row key的range(范围) (scan)
- ③ 全表扫描 (scan)
2)HBase会对表中的数据按照Row Key
排序(字典顺序
)
3)Row key只能存储64k
的字节数据
(实际应用中长度一般为 10-100bytes
),在HBase内部,Row Key
保存形式为字节数组
【2.2】 Column Family列族 & Column列
1)HBase表中的每个列都归属于某个列族,列族必须作为表模式(schema)
定义的一部分预先给出
。如 create ‘表名’, ‘列族名’
2)列名以列族作为前缀,每个“列族”都可以有多个列成员(column)
;如course:math
, course:english
, 新的列族成员(列)可以随后按需
、动态的加入
3)权限控制、存储以及调优都是在列族层面
进行的
4)HBase把同一列族
里面的数据存储在同一目录
下,由几个文件保存
【2.3】 Timestamp时间戳
1)在HBase每个cell
存储单元对同一份数据有多个版本
,根据唯一的时间戳
来区分每个版本之间的差异,不同版本的数据按照时间倒序排序
,最新的数据版本排在最前面
2)时间戳的类型是 64位整型
3)时间戳可以由HBase(在数据写入时自动)赋值,此时时间戳是精确到毫秒的当前系统时间
4)时间戳也可以由客户显式赋值,如果应用程序要避免数据版本冲突,就必须自己生成具有唯一性的时间戳
5)为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,HBase提供了两种
数据版本回收方式
:
- ①.保存数据的最后n个版本
- ②.保存最近一段时间内的版本(设置数据的生命周期TTL)
6)用户可以针对每个列族
进行设置
【2.4】 Cell单元格
1)由 RowKey 和 "ColumnFamily:Column"的座标交叉决定
2)单元格是有版本的
3)单元格的内容是未解析的字节数组
, 由 {row key, column( =<family> +<qualifier>), version}
唯一确定的单元;cell
中的数据是没有类型
的,全部是字节码形式存贮
【2.5】 VersionNum
1)数据的版本号,每条数据可以有多个版本号,默认值为系统时间戳
,类型为Long
【2.6】 Region
什么是Region?
Region可以类比成关系型数据库中的分区,每个Region存储一个表中的一部分连续数据
1)HBase自动
把表水平划分成多个区域(region)
,每个region会保存一个表里面某段连续的数据
;每个表默认
一开始只有一个region
,随着数据不断插入表,region不断增大,当增大到一个阀值
的时候,region就会等分成两个新的region(裂变)
2)当table中的行不断增多,就会有越来越多的region。这样一张完整的表被切分成多个Region,保存在多个RegionServer
上
3)因为RegionServer管理Region,所以Region是HBase负责均衡的最小单元
,最小单元就表示不同的Region可以分布在不同的HRegion Server上,对于一张表中不同的Region,可以分布在不同的HRegionServer上,但对于一张表中的一个Region是不会拆分到多个RegionServer上的
4)Region虽然是负载均衡的最小单元,但并不是物理存储的最小单元;
事实上,Region由一个或者多个Store组成
,每个Store保存一个column family;
每个Strore又由一个MemStore和0至多个StoreFile组成
三、HBase各角色作用解析
【3.1】 Client
1)Client包含了访问HBase的接口
,另外Client还维护了对应的cache
来加速HBase的访问,比如cache
的.META.
元数据的信息
【3.2】 Zookeeper
1)保证任何时候,集群中只有一个Active Master
,如果Master异常,会通过竞争机制产生新的Master提供服务
2)存贮所有Region的寻址入口
3)实时监控RegionServer的状态,将RegionServer的上线和下线信息实时通知给Master
4)存储HBase的schema
和table元数据信息
【3.3】 Master
1)为RegionServer分配Region
2)负责RegionServer的负载均衡
3)发现失效的RegionServer并重新分配其上的Region
4)处理对schema更新请求
【3.4】 RegionServer
1)RegionServer维护
Master分配给它的Region,处理对Region的IO请求
2)RegionServer负责切分
在运行过程中变得过大的Region;可以看到,Client访问HBase上数据的过程并不需要Master参与(寻址访问Zookeeper和RegionServer,数据读写访问RegioneServer),Master仅仅维护者table和Region的元数据信息,负载很低
3)负责和底层HDFS的交互,存储数据到HDFS
4)负责Storefile的合并
工作
四、HBase基于HDFS、ZK架构图
五、HBASE读写流程
【5.1】 写数据
1)Client 访问 Zookeeper,获取 hbase:meta表位于哪个 RegionServer
2)访问对应的RegionServer,获取hbase:meta表,根据rowkey的范围确定RegionServer和Region,并将该table的Region信息以及meta表的位置信息缓存在客户端的 Meta cache,方便下次访问
3)Client与目标RegionServer进行通讯,发起写入数据请求
4)Client将数据写入(追加)到HLog(WAL,Write ahead log,预写日志),以防止数据丢失
5)然后将数据写入对应的MemStore,数据会在MemStore内进行排序
6)如果HLog和Memstore都写入成功,则这条数据写入成功,向客户端发送消息,告知写入成功,如果其中一个写入失败,就表示这次写入失败
7)等达到MemStore的阈值后,将数据flush到磁盘成为StoreFile,当StoreFile达到阈值(默认3个)后进行compact操作,将多个StoreFile合并成一个大StoreFile
注意:
1)此过程没有Master的参与
2)客户端在写数据时会先去查看本地的meta cache,如果有之前的缓存数据,直接通过缓存元数据访问 RegionServer,这时不通过Zookeeper获取meta表去查找元数据,。如果集群发生某些变化导致hbase:meta元数据更改,客户端再根据本地元数据表请求的时候就会发生异常
,此时客户端需要重新加载一份最新
的元数据表到本地
3)MemStore触发时机
【5.2】读数据
1)Client 访问 Zookeeper,获取 hbase:meta表位于哪个 RegionServer
2)访问对应的RegionServer,获取hbase:meta表,根据rowkey确定当前将要读取的数据位于哪个RegionServer
中的哪个Region中。并将该table的region信息以及meta表的位置缓存在客户端 meta cache,方便下次访问
3)Client向该RegionServer服务器发起读取数据请求,然后RegionServer收到请求并响应
4)分别在BlockCache(读缓存)、MemStore和StoreFile(BlockCache中有的数据
在StoreFile中就不再读取)中查询目标数据,并将查到的所有数据进行合并;
此处的所有数据指的是同一条数据的不同版本(Time Stamp)或者不同的类型(Put/Delete)
5)将从文件中查询到的数据块(Block,HFile数据存储单元,默认大小为64KB)缓存到Block Cache
6)将合并后的最终结果返回给客户端
注意:
1)此过程没有Master的参与
2)客户端在读数据时会先去查看本地的meta cache,如果有之前的缓存数据,直接通过缓存元数据访问 RegionServer,这时不通过Zookeeper获取meta表去查找元数据,。如果集群发生某些变化导致hbase:meta元数据更改,客户端再根据本地元数据表请求的时候就会发生异常,此时客户端需要重新加载一份最新的元数据表到本地
【5.3】 Flush 机制
flush 大致可以分为三个阶段:prepare 阶段
,flush 阶段
,commit 阶段
1)prepare 阶段
遍历当前 Region
中所有的 Memstore
,将Memstore中当前数据集CellSkipListSet
做一个快照snapshot
,之后创建一个新的CellSkipListSet,后期写入的数据都会写入新的CellSkipListSet
中
2)flush 阶段
遍历所有Memstore
,将 prepare阶段生成的 snapshot 持久化为临时文件
,临时文件会统一放到目录.tmp
下。这个过程因为涉及到磁盘IO
操作,因此相对比较耗时
3)commit 阶段
遍历所有 Memstore,将 flush阶段生成的临时文件
移到指定的ColumnFamily
目录下,针对HFile生成对应的storefile
和Reader
,把storefile
添加到HStore的storefiles
列表中,最后再清空prepare阶段生成的snapshot
flush 的触发条件一般分为: memstore 级别
、region 级别
、regionServer 级别
、HLog数量上限
,具体配置可在官网文档中查询到
【5.4】 compact 机制
compact 合并机制,主要分为 minor compaction 小合并 、major compaction 大合并两个阶段
1)minor compaction 小合并
将Store中多个HFile
合并为一个HFile
,一次Minor Compaction的结果是HFile数量减少
并且合并出一个更大的StoreFile
,这种合并的触发频率很高
2)major compaction 大合并
合并 Store
中所有的 HFile
为一个HFile
,此时被清理的数据有:被标记删除的数据
、TTL过期数据
、版本号超过设定版本号的数据
。合并频率比较低,默认7天
执行一次,并且性能消耗非常大
,建议生产关闭
(设置为0
),在应用空闲时间手动触发
。一般可以是手动控制进行合并,防止出现在业务高峰期
【5.5】 region的拆分
为什么要拆分 region 呢?
因为region中存储的是大量
的 rowkey
数据 ,当 region 中的数据条数过多
, region 变得很大的时候,直接影响查询效率
,因此当 region 过大的时候.hbase会拆分region , 这也是HBase的一个优点
region 拆分策略
0.94
版本前默认切分策略,当region大小大于某个阈值(hbase.hregion.max.filesize=10G)
之后就会触发切分,一个region等分为2个region
0.94版本~2.0
版本默认切分策略 :根据拆分次数来判断触发拆分的条件
region split的计算公式是:
regioncount^3 * 128M * 2
,当region达到该 size 的时候进行split
例如:
第一次split:1^3 * 256 = 256MB
第二次split:2^3 * 256 = 2048MB
第三次split:3^3 * 256 = 6912MB
第四次split:4^3 * 256 = 16384MB > 10GB
,因此取较小的值10GB
后面每次split的size都是10GB了
预分区机制
当一个Hbase 表刚被创建的时候,Hbase默认的分配一个 region
给table。也就是说这个时候,所有的读写请求
都会访问到同一个 regionServer 的同一个region中
,这个时候就达不到负载均衡
的效果了,集群中的其他 regionServer 就可能会处于比较空闲的状态
为了解决这个问题,就有了 pre-splitting
,也就是预分区机制
,在创建table的时候就配置好,生成多个region
,这样的好处就是可以优化数据读写效率
,并且使用负载均衡机制
,防止数据倾斜
操作很简单,在创建表时,手动指定分区就好了:
例如👇
create 'person','info1','info2',SPLITS => ['1000','2000','3000','4000']
【5.6】 region的合并
Region的合并不是为了性能
, 而是出于维护
的目的
比如删除了大量的数据 ,这个时候每个Region都变得很小 ,存储多个 Region就浪费了 ,这个时候可以把Region合并起来,进而可以减少一些 Region 服务器节点,由此可见 region 的合并其实是为了更好的维护 Hbase 集群