1、HBase定义
Apache HBase
是以 hdfs
为数据存储的,一种分布式、可扩展的 NoSQL
数据库。
2、HBase 数据模型
HBase
的设计理念依据 Google 的 BigTable
论文,论文中对于数据模型的首句介绍。Bigtable
是一个稀疏的、分布式的、持久的多维排序 map
。
之后对于映射的解释如下:该映射由行键、列键和时间戳索引;映射中的每个值都是一个未解释的字节数组。
最终 HBase
关于数据模型和 BigTable
的对应关系如下:HBase
使用与 Bigtable
非常相似的数据模型。用户将数据行存储在带标签的表中。数据行具有可排序的键和任意数量的列。该表存储稀疏,因此如果用户喜欢,同一表中的行可以具有疯狂变化的列。
最终理解 HBase
数据模型的关键在于稀疏、分布式、多维、排序的映射。其中映射 map
指代非关系型数据库的 key-Value
结构。
3、HBase的逻辑结构
{
"row_key1": {
"personal_info": {
"name": "zhangsan",
"city": "北京",
"phone": "131********"
},
"office_info": {
"tel": "010-1111111",
"address": "atguigu"
}
},
"row_key11": {
"personal_info": {
"city": "上海",
"phone": "132********"
},
"office_info": {
"tel": "010-1111111"
}
},
"row_key2": {
......
}
}
存储数据稀疏,数据存储多维,不同的行具有不同的列。数据存储整体有序,按照RowKey
的字典序排列,RowKey
为Byte
数组。
4、Hbase物理存储结构
物理存储结构即为数据映射关系,而在概念视图的空单元格,底层实际根本不存储。
5、数据模型
5.1、Name Space
命名空间, 类似于关系型数据库的 database
概念,每个命名空间下有多个表。HBase
两个自带的命名空间,分别是 HBase
和 default
,HBase
中存放的是 HBase
内置的表,default
表是用户默认使用的命名空间。
5.2、Table
类似于关系型数据库的表概念。不同的是,HBase
定义表时只需要声明列族即可,不需要声明具体的列。因为数据存储时稀疏的,所有往 HBase
写入数据时,字段可以动态、按需指定。因此,和关系型数据库相比,HBase
能够轻松应对字段变更的场景。
5.3、Row
HBase 表中的每行数据都由一个 RowKey
和多个 Column
(列)组成,数据是按照 RowKey
的字典顺序存储的,并且查询数据时只能根据 RowKey
进行检索,所以 RowKey
的设计十分重要。
5.4、Colum
HBase
中的 每个列都由 Column Family
(列族)和Column Qualifier
(列限定符) 进行限定,例如 info: name, info: age
。建表时,只需指明列族,而列限定符无需预先定义。
5.5、Time Stamp
用于标识数据的不同版本(version
),每条数据写入时,系统会自动为其加上该字段,其值为写入 HBase
的时间。
5.6、Cell
由{rowkey, column Family: column Qualifier, timestamp}
唯一确定的单元。 cell
中的数据全部是字节码形式存贮 。
6、HBase基本架构
-
Master
实现类为
HMaster
,负责监控集群中所有的RegionServer
实例。主要作用如下:- 管理元数据表格
hbase: meta
,接收用户对表格创建修改删除的命令并执行; - 监控
region
是否需要进行负载均衡,故障转移和region
的拆分。
通过启动多个后台线程监控实现上述功能:
- LoadBalancer 负载均衡器: 周期性监控
region
分布在regionServer
上面是否均衡,由参数hbase.balancer.period
控制周期时间,默认 5 分钟; - CatalogJanitor 元数据管理器: 定期检查和清理
hbase: meta
中的数据。meta
表内容在进阶中介绍; - MasterProcWAL master 预写日志处理器: 把
master
需要执行的任务记录到预写日志WAL
中,如果master
宕机,让backupMaster
读取日志继续干。
- 管理元数据表格
-
Region Server
Region Server
实现类为HRegionServer
,主要作用如下:- 负责数据
cell
的处理,例如写入数据put
,查询数据get
等; - 拆分合并
region
的实际执行者,有master
监控,有regionServer
执行。
- 负责数据
-
Zookeeper
HBase
通过Zookeeper
来做master
的高可用、记录RegionServer
的部署信息、并且存储有meta
表的位置信息。HBase
对于数据的读写操作时直接访问Zookeeper
的,在 2.3 版本推出Master Registry
模式,客户端可以直接访问master
。使用此功能,会加大对master
的压力,减轻对Zookeeper
的压力。 -
HDFS
HDFS
为Hbase
提供最终的底层数据存储服务,同时为HBase
提供高容错的支持。
7、Master架构
Meta 表格介绍:(警告:不要去改这个表)
全称 hbase: meta
,只是在 list
命令中被过滤掉了,本质上和 HBase
的其他表格一样。
RowKey
([table], [region start key], [region id])
即 表名,region
起始位置和regionID
。
- 列
- info:
regioninfo
为region
信息,存储一个HRegionInfo
对象。 - info:
server
当前region
所处的RegionServer
信息,包含端口号。 - info:
serverstartcode
当前region
被分到RegionServer
的起始时间。
- info:
如果一个表处于切分的过程中,即region
切分,还会多出两列info: splitA
和info: splitB
,存储值也是 HRegionInfo
对象,拆分结束后,删除这两列。
在客户端对元数据进行操作的时候才会连接
master
,如果对数据进行读写,直接连接zookeeper
读取目录/hbase/meta-region-server
节点信息,会记录meta
表格的位置。直接读取即可,可不需要访问master
,这样可以减轻master
的压力,相当于master
专注meta
表的写操作,客户端可直接读取meta
表。
在HBase
的 2.3 版本更新了一种新模式:Master Registry
。客户端可以访问master
来读取meta
表信息。加大了 master
的压力,减轻了 zookeeper
的压力。
8、RegionServer 架构
8.1、MemStore
写缓存,由于 HFile
中的数据要求是有序的,所以数据是 先存储在 MemStore
中,排好序后,等到达刷写时机才会刷写到 HFile
,每次刷写都会形成一个新的 HFile
,写入到对应的文件夹 store
中。
8.2、WAL
由于数据要经 MemStore
排序后才能刷写到 HFile
,但把数据保存在内存中会有很高的概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做 Write-Ahead logfile
的文件中,然后再写MemStore
中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。
8.3、BlockCache
读缓存,每次查询出的数据会 缓存在BlockCache
中,方便下次查询。
9、写流程
写流程顺序正如 API
编写顺序,首先创建 HBase
的重量级连接:
- 首先访问
zookeeper
,获取hbase:meta
表位于哪个Region Server
; - 访问对应的
Region Server
,获取hbase:meta
表,将其缓存到连接中,作为连接属性MetaCache
,由于Meta
表格具有一定的数据量,导致了创建连接比较慢; - 之后使用创建的连接 获取
Table
,这是一个轻量级的连接,只有在第一次创建的时候会检查表格是否存在访问RegionServer
,之后在获取Table
时不会访问RegionServer
; - 调用
Table
的put
方法写入数据,此时还需要解析RowKey
,对照缓存的MetaCache
,查看具体写入的位置有哪个RegionServer
; - 将数据顺序写入(追加)到
WAL
,此处写入是直接落盘的,并设置专门的线程控制WAL
预写日志的滚动(类似Flume
); - 根据写入命令的
RowKey
和ColumnFamily
查看具体写入到哪个MemStory
,并且在MemStory
中排序; - 向客户端发送
ack
; - 等达到
MemStore
的刷写时机后,将数据刷写到对应的store
中。
10、MemStore Flush
MemStore
刷写由多个线程控制,条件互相独立,主要的刷写规则是控制刷写文件的大小,在每一个刷写线程中都会进行监控:
(1)当某个 memstroe
的大小达到了hbase.hregion.memstore.flush.size
(默认值 128M),其所在 region
的所有memstore
都会刷写。当 memstore
的大小达到了hbase.hregion.memstore.flush.size
(默认值128M)、hbase.hregion.memstore.block.multiplier
(默认值4)时,会刷写同时阻止继续往该 memstore
写数据(由于线程监控是周期性的,所有有可能面对数据洪峰,尽管可能性比较小);
(2)由 HRegionServer
中的属性 MemStoreFlusher
内部线程 FlushHandler
控制。标准为LOWER_MARK
(低水位线)和 HIGH_MARK
(高水位线),意义在于避免写缓存使用过多的内存造成 OOM
。
当 region server
中 memstore
的总大小达到低水位线java_heapsize * hbase.regionserver.global.memstore.size (默认值 0.4) * hbase.regionserver.global.memstore.size.lower.limit (默认值 0.95 )
,region
会按照其所有 memstore
的大小顺序(由大到小)依次进行刷写。直到 region server
中所有 memstore
的总大小减小到上述值以下。
当 region server
中 memstore
的总大小达到高水位线java_heapsize * base.regionserver.global.memstore.size
(默认值0.4)时,会同时阻止继续往所有的 memstore
写数据。
(3)为了避免数据过长时间处于内存之中,到达自动刷写的时间,也会触发 memstore flush
。由 HRegionServer
的属性 PeriodicMemStoreFlusher
控制进行,由于重要性比较低,5min才会执行一次。自动刷新的时间间隔由该属性进行配置 hbase.regionserver.optionalcacheflushinterval
(默认1 小时)。
(4)当 WAL
文件的数量超过 hbase.regionserver.max.logs
,region
会按照时间顺序依次进行刷写,直到 WAL
文件数量减小到 hbase.regionserver.max.log
以下(该属性名已经废弃,现无需手动设置,最大值为 32)。
11、读流程
11.1、HFile 结构
在了解读流程之前,需要先知道读取的数据是什么样子的。
HFile
是存储在 HDFS
上面每一个 store
文件夹下实际存储数据的文件。里面存储多种内容。包括数据本身(keyValue
键值对)、元数据记录、文件信息、数据索引、元数据索引和一个固定长度的尾部信息(记录文件的修改情况)。
键值对按照块大小(默认 64K)保存在文件中,数据索引按照块创建,块越多,索引越大。每一个 HFile
还会维护一个布隆过滤器(就像是一个很大的地图,文件中每有一种 key
,就在对应的位置标记,读取时可以大致判断要 get
的 key
是否存在HFile
中)。
KeyValue
内容如下:
- rowlength:
key
的长度; - row:
key
的值; - columnfamilylength: 列族长度;
- columnfamily: 列族;
- columnqualifier: 列名;
- timestamp: 时间戳(默认系统时间);
- keytype:
Put
。
由于 HFile
存储经过序列化,所以无法直接查看。可以通过 HBase
提供的命令来查看存储在HDFS
上面的 HFile
元数据内容。
bin/hbase hfile -m -f /hbase/data/命名空间/表名/regionID/列族/HFile 名
11.2、读流程
创建连接同写流程。
- 创建
Table
对象发送get
请求。 - 优先访问
Block Cache
,查找是否之前读取过,并且可以读取HFile
的索引信息和布隆过滤器。 - 不管读缓存中是否已经有数据了(可能已经过期了),都需要再次读取写缓存和
store
中的文件。 - 最终将所有读取到的数据合并版本,按照
get
的要求返回即可。
11.3、合并读取数据优化
每次读取数据都需要读取三个位置,最后进行版本的合并。效率会非常低,所有系统需要对此优化。
HFile
带有索引文件,读取对应RowKey
数据会比较快。Block Cache
会缓存之前读取的内容和元数据信息,如果HFile
没有发生变化(记录在HFile
尾信息中),则不需要再次读取。- 使用布隆过滤器能够快速过滤当前
HFile
不存在需要读取的RowKey
,从而避免读取文件。(布隆过滤器使用HASH
算法,不是绝对准确的,出错会造成多扫描一个文件,对读取数据结果没有影响)。
12、StoreFile Compaction
由于 memstore
每次刷写都会生成一个新的 HFile
,文件过多读取不方便,所以会进行文件的合并,清理掉过期和删除的数据,会进行 StoreFile Compaction
。
Compaction
分为两种,分别是 Minor Compaction
和 Major Compaction
。Minor Compaction
会将临近的若干个较小的 HFile
合并成一个较大的 HFile
,并清理掉部分过期和删除的数据,有系统使用一组参数自动控制,Major Compaction
会将一个 Store
下的所有的 HFile
合并成一个大 HFile
,并且 会清理掉所有过期和删除的数据,由 参数 hbase.hregion.majorcompaction
控制,默认 7 天。
Minor Compaction
控制机制:参与到小合并的文件需要通过参数计算得到,有效的参数有 5 个
hbase.hstore.compaction.ratio
(默认 1.2F)合并文件选择算法中使用的比率。hbase.hstore.compaction.min
(默认 3) 为Minor Compaction
的最少文件个数。hbase.hstore.compaction.max
(默认 10) 为Minor Compaction
最大文件个数。hbase.hstore.compaction.min.size
(默认 128M)为单个Hfile
文件大小最小值,小于这个数会被合并。hbase.hstore.compaction.max.size
(默认Long.MAX_VALUE
)为单个Hfile
文件大小最大值,高于这个数不会被合并。小合并机制为拉取整个store
中的所有文件,做成一个集合。之后按照从旧到新的顺序遍历。判断条件为:- 过小合并,过大不合并。
- 文件大小/
hbase.hstore.compaction.ratio
< (剩余文件大小和) 则参与压缩。所有把比值设置过大,如 10 会最终合并为 1 个特别大的文件,相反设置为 0.4,会最终产生 4 个storeFile
。不建议修改默认值。 - 满足压缩条件的文件个数达不到个数要求(
3 <= count <= 10
)则不压缩。
13、Region Split
Region
切分分为两种,创建表格时候的预分区即自定义分区,同时系统默认还会启动一个切分规则,避免单个 Region
中的数据量太大。
预分区(自定义分区)
每一个 Region
维护着 startRow
与 endRowKey
,如果加入的数据符合某个 Region
维护的rowKey
范围,则该数据交给这个 Region
维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高 HBase
性能。