HBase

一、HBase简介

Hadoop Database是一个高可靠性、高性能、面向列、可伸缩、实时读写的分布式数据库。

利用Hadoop的HDFS作为其文件存储系统,利用zookeeper作为其分布式协调服务主要用来存储半结构化或非结构化的松散数据。

二、HBase数据模型

HBase是一个稀疏的、分布的、多维、排序的映射,它以行键、列簇、列名、和时间戳为索引。

2.1 NameSpace

命名空间类似于关系型数据库中数据库的概念,它其实是表的逻辑分组。

2.2 Table

Hbase的Table由多个行组成

2.3 RowKey

RowKey用来检索记录的主键,是一行数据的唯一标识

RowKey可以是任意字符串最大长度是64KB,以字节数组保存

存储时,数据按照Row Key的字典序排序,设计RowKey时要充分考虑排序存储这个特性,将经常读取的行存放到一起。

2.4 Colum Family:

列族在物理上包含了许多列与列的值,每个列族都有一些存储的属性可配置。

将功能相近的列存放到同一个列族中,相同列族中的列会存放在同一个store中

列族一般需要在创建表的时候声明,一般一个表中的列族不超过3个

列隶属于列族,列族隶属于表。

2.5 ColumQualifier

列族的限定词,理解为列的唯一标识。但是列标识是可以改变的,因此每一行会有不同的列标识

使用的时候必须 列族:列

列可以根据需求动态添加或删除,同一个表中的不同行的数据列都可以不同

2.6 Timestamp

通过rowkey、columFamily、columqualifier确定一个存储单元通过时间戳来索引

每个cell中,不同版本的数据按照时间顺序倒叙排序,即最新的数据排到最前面。

为了避免数据存在过多版本中造成管理负担,HBase提供了俩种数据版本回收方式

一是存储数据的最后n个版本

二是保存最近一段时间的版本

2.7 cell

Cell是由row columFamily、columQualifier、version组成

cell中数据没有类型,全部是字节码存储的

因为HDFS上的数据是字节数组

三、HBase架构模型

HBase有三个主要部分组成:客户端库,主服务器,区域服务器

 3.1 Client

客户端负责发送请求到数据库,发送的请求包括

DDL 数据库定义语言(表的建立,删除,添加删除列族,控制版本)

DML 数据库操作语言(增删改)

DQL 数据库查询语言(查询--全表扫描--基于主键--基于过滤器)

client中维护着一些cache来加快对Hbase的访问,比如regione的位置信息

3.2 HMaster

HBase的主节点,HMaster也可以实现高可用(active-standby)

        通过zookeeper来维护主副节点的切换

为region server分配region并负责region server的负载均衡

管理用户对table的DDL操作

当HRegion Server下线的时候,HMaster会将当前HRegion Server的Region转移到其他的RegionServer上

3.3 zookeeper

保证任何时候集群只有一个master

存储所有region的选址入口,存储所有的元数据信息

实时监控RegionServer的状态,将RegionServer的上线和下线实时通知给Master

存储Hbase的schame,包括有那些table,每个table上有哪些Colum family

3.4 HRegionServer

RegionServer属于HBase的具体数据管理者

RegionServer维护master分配给它的region,处理这些region的io请求

会实时和master保持心跳,汇报当前节点的信息

当接收到master命令创建表的时候,分配一个region对应一张表。

regionServer负责切分在运行过程中变得过大的region

当客户端发送DML或DQL的时候,HRegionServer负责和客户端建立连接

当意外关闭的时候当前regionServer的region会被其他regionServer管理

3.5 HRegion

HRegion是HBase分布式存储和负载均衡的最小单元。最小单元就是表示不同的HRegion可以分布在不同的HRegionServer上

HBase会自动把表拆分成多个Region,每个Region会保存表里边某段连续的数据

每个表一开始只有一个region,一个region只属于一张表,随着数据的不断写入,region不断增大,Region的大小达到10G的时候会被平分 

当table的行不断增多,就会有越来越多的region,这样一张完整的表就会被保存到多个region Server中

为了防止前期的数据都集中在一个HRegionServer中,我们可以根据自己的业务进行预分区

3.6 store

HRegion是表获取和分布的基本单位,由一个或者多个store组成,每个store保存一个columsfamily

每个store又由一个memstore和0或多个storeFile组成

HFile是Hbase在HDFS中存储数据的格式,它包含多层索引,这样在HBase检索数据的时候就不用完全加载整个文件了

四、HBase读写流程

HBase读取数据的流程

01Client 访问 zookeeper ,获取 hbase:meta 所在 RegionServer 的节点信息
02Client 访问 hbase:meta 所在的 RegionServer ,获取 hbase:meta 记录的元数据后先加载到内存
中,然后再从内存中根据需要查询的 RowKey 查询出 RowKey 所在的 Region 的相关信息( Region
RegionServer
03Client 访问 RowKey 所在 Region 对应的 RegionServer ,发起数据读取请求
04RegionServer 构建 RegionScanner (需要查询的 RowKey 分布在多少个 Region 中就需要构建多少
RegionScanner ),用于对该 Region 的数据检索
05RegionScanner 构建 StoreScanner Region 中有多少个 Store 就需要构建多少个 StoreScanner
Store 的数量取决于 Table ColumnFamily 的数量),用于对该列族的数据检索
06 多个 StoreScanner 合并构建最小堆(已排序的完全二叉树) StoreHeap:PriorityQueue
07StoreScanner 构建一个 MemStoreScanner 和一个或多个 StoreFileScanner (数量取决于
StoreFile 数量)
08 过滤掉某些能够确定所要查询的 RowKey 一定不在 StoreFile 内的对应的 StoreFileScanner
MemStoreScanner
09 经过筛选后留下的 Scanner 开始做读取数据的准备,将对应的 StoreFile 定位到满足的 RowKey
起始位置
10 将所有的 StoreFileScanner MemStoreScanner 合并构建最小堆
KeyValueHeap:PriorityQueue ,排序的规则按照 KeyValue 从小到大排序
11 KeyValueHeap:PriorityQueue 中经过一系列筛选后一行行的得到需要查询的 KeyValue
HBase写入数据流程
首先客户端和 RegionServer 建立连接
然后将 DML 要做的操作写入到日志 wal-log
然后将数据的修改更新到 memstore 中,然后本次操作结束
一个 region 由多个 store 组成,一个 store 对应一个 CF (列族) ,store 包括位于内存中的
memstore 和位于磁盘的 storefile, 写操作先写入 memstore.
memstore 数据写到阈值之后,创建一个新的 memstore
旧的 memstore 写成一个独立的 storefile,regionserver 会启动 flashcache 进程写入 storefile ,每次
写入形成单独的一个 storefile, 存放到 hdfs
storefile 文件的数量增长到一定阈值后,系统会进行合并( minor compaction major
compaction
在合并过程中会进行版本合并和删除工作,形成更大的 storefile
当一个 region 所有 storefile 的大小和数量超过一定阈值后,会把当前的 region 分割为两个,并由
hmaster 分配到相应的 regionserver 服务器,实现负载均衡
Store 负责管理当前列族的数据 1 memstore+n storefile
当我们进行数据 DML 的时候 , 以插入数据为例
我们会将数据先存储到 memStore 中,当 memStore 达到阈值( 128M
首先创建一个新的 memstore
然后会将 memStore 中的数据写成一个 storefile,storefile 会存储到 hdfs (hfile)
随着时间的推移:
HFile 中会存放大量的失效数据 ( 删除,修改 )
会产生多个 HFile
等达到阈值(时间、数量)会进行合并
多个 HFile 合并成一个大的 HFile
合并会触发连锁反应,相邻的 store 也会进行合并
Hbase 中,表被分割成多个更小的块然后分散的存储在不同的服务器上,这些小块叫做
Regions ,存放 Regions 的地方叫做 RegionServer Master 进程负责处理不同的 RegionServer 之间
Region 的分发。在 Hbase 实现中 HRegionServer HRegion 类代表 RegionServer Region
HRegionServer 除了包含一些 HRegions 之外,还处理两种类型的文件用于数据存储
HLog 预写日志文件,也叫做 WAL(write-ahead log)
HFile HDFS 中真实存在的数据存储文件

五、数据刷写

5.1. 触发时机
Region 中所有 MemStore 占用的内存超过相关阈值
hbase.hregion.memstore.flush.size 参数控制,默认为 128MB
如果我们的数据增加得很快,
达到了 hbase.hregion.memstore.flush.size * hbase.hregion.memstore.block.multiplier
的大小, hbase.hregion.memstore.block.multiplier 默认值为 4
也就是 128*4=512MB 的时候,
那么除了触发 MemStore 刷写之外, HBase 还会在刷写的时候同时阻塞所有写入该 Store
写请求!
整个 RegionServer MemStore 占用内存总和大于相关阈值
HBase RegionServer 所有的 MemStore 分配了一定的写缓存(),大小等于
hbase_heapsize RegionServer 占用的堆内存大小) *
hbase.regionserver.global.memstore.size( 默认值是 0.4)
如果整个 RegionServer MemStore 占用内存总和大于阈值将会触发 MemStore 的刷写。
hbase.regionserver.global.memstore.size.lower.limit (默认值为 0.95 * MAX_SIZE
例如: HBase 堆内存总共是 32G MemStore 占用内存为: 32 * 0.4 * 0.95 = 12.16G 将触发
刷写
如果达到了 RegionServer 级别的 Flush ,当前 RegionServer 的所有写操作将会被阻塞,这
个阻塞可能会持续到分钟级别。
WAL 数量大于相关阈值
数据到达 Region 的时候是先写入 WAL ,然后再被写到 Memstore 。如
WAL 的数量越来越大,这就意味着 MemStore 中未持久化到磁盘的数据越来越多。
RS 挂掉的时候,恢复时间将会变得很长,所以有必要在 WAL 到达一定的数量时进行一次
刷写操作
定期自动刷写
hbase.regionserver.optionalcacheflushinterval
默认值 3600000 (即 1 小时), HBase 定期 Flush 所有 MemStore 的时间间隔。
一般建议调大,比如 10 小时,因为很多场景下 1 小时 Flush 一次会产生很多小文件,一方
面导致 Flush 比较频繁,另一方面导致小文件很多,影响随机读性能
数据更新超过一定阈值
如果 HBase 的某个 Region 更新的很频繁,而且既没有达到自动刷写阀值,也没有达到内存
的使用限制,但是内存中的更新数量已经足够多,
比如超过 hbase.regionserver.flush.per.changes 参数配置,默认为 30000000 ,那么也是会
触发刷写的。
手动触发刷写
Shell 中通过执行 flush 命令
特别注意
以上所有条件触发的刷写操作最后都会检查对应的 HStore 包含的 StoreFiles 文件数是否超过
hbase.hstore.blockingStoreFiles 参数配置的个数,默认值是 16
如果满足这个条件,那么当前刷写会被推迟到 hbase.hstore.blockingWaitTime 参数设置的时
间后再刷写。
在阻塞刷写的同时, HBase 还会请求 Compaction 或者 Split 操作。
5.2. 刷写策略
HBASE1.1 之前:
MemStore 刷写是 Region 级别的。就是说,如果要刷写某个 MemStore MemStore 所在
Region 中其他 MemStore 也是会被一起刷写的
HBASE2.x 之后
FlushAllStoresPolicy
每次刷写都是对 Region 里面所有的 MemStore 进行的
FlushAllLargeStoresPolicy
判断 Region 中每个 MemStore 的使用内存是否大于某个阀值 , 大于这个阀值的
MemStore 将会被刷写。
flushSizeLowerBound = max((long)128 / 3, 16) = 42
FlushNonSloppyStoresFirstPolicy
5.3. 刷写流程
prepareFlush 阶段:
刷写的第一步是对 MemStore snapshot
为了防止刷写过程中更新的数据同时在 snapshot MemStore 中而造成后续处理的困难,
所以在刷写期间需要持有 updateLock 。持有了 updateLock 之后,这将阻塞客户端的写操
作。
所以只在创建 snapshot 期间持有 updateLock
max(32, hbase_heapsize * hbase.regionserver.global.memstore.size * 2 /
logRollSize)
而且 snapshot 的创建非常快,所以此锁期间对客户的影响一般非常小。
MemStore snapshot internalPrepareFlushCache 里面进行的。
flushCache 阶段:
如果创建快照没问题,
那么返回的 result.result 将为 null
这时候我们就可以进行下一步 internalFlushCacheAndCommit
其实 internalFlushCacheAndCommit 里面包含两个步骤: flushCache commit 阶段。
flushCache 阶段:
其实就是将 prepareFlush 阶段创建好的快照写到临时文件里面,
临时文件是存放在对应 Region 文件夹下面的 .tmp 目录里面。
commit 阶段:
flushCache 阶段生产的临时文件移到( rename )对应的列族目录下面,
并做一些清理工作,比如删除第一步生成的 snapshot

 六、数据合并

6.1. 合并分类
HBase 根据合并规模将 Compaction 分为了两类: MinorCompaction MajorCompaction
Minor Compaction
是指选取一些小的、相邻的 StoreFile 将他们合并成一个更大的 StoreFile ,在这个过程中不会
处理已经 Deleted Expired Cell
但是会处理超过 TTL 的数据
一次 Minor Compaction 的结果是让小的 storefile 变的更少并且产生更大的 StoreFile
Major Compaction
是指将所有的 StoreFile 合并成一个 StoreFile
清理三类无意义数据:被删除的数据、 TTL 过期数据、版本号超过设定版本号的数据。
一般情况下, Major Compaction 时间会持续比较长,整个过程会消耗大量系统资源,对上层
业务有比较大的影响。因此线上业务都会将关闭自动触发 Major Compaction 功能,改为手动
在业务低峰期触发。
6.2. 合并时机
触发 compaction 的方式有三种: Memstore 刷盘、后台线程周期性检查、手动触发。
Memstore 刷盘
memstore flush 会产生 HFile 文件,文件越来越多就需要 compact
每次执行完 Flush 操作之后,都会对当前 Store 中的文件数进行判断,一旦文件数大于配置,就
会触发 compaction
compaction 都是以 Store 为单位进行的,而在 Flush 触发条件下,整个 Region 的所有 Store
会执行 compact
后台线程周期性检查
后台线程定期触发检查是否需要执行 compaction ,检查周期可配置。
hbase.server.thread.wakefrequency( 默认 10000 毫秒)
*hbase.server.compactchecker.interval.multiplier( 默认 1000)
CompactionChecker 大概是 2hrs 46mins 40sec 执行一次
线程先检查文件数是否大于配置,一旦大于就会触发 compaction
如果不满足,它会接着检查是否满足 major compaction 条件,
如果当前 store hfile 的最早更新时间早于某个值 mcTime
就会触发 major compaction (默认 7 天触发一次,可配置手动触发)。
手动触发
一般来讲,手动触发 compaction 通常是为了执行 major compaction ,一般有这些情况需要手
动触发合并
是因为很多业务担心自动 major compaction 影响读写性能,因此会选择低峰期手动触
发;
也有可能是用户在执行完 alter 操作之后希望立刻生效,执行手动触发 major
compaction
HBase 管理员发现硬盘容量不够的情况下手动触发 major compaction 删除大量过期数
据;
6.3. 合并策略
承载了大量 IO 请求但是文件很小的 HFile compaction 本身不会消耗太多 IO ,而且合并完成之后对读的
性能会有显著提升。
线程池选择
HBase CompacSplitThread 类内部对于 Split Compaction 等操作专门维护了各自所使用的
线程池
Compaction 相关的是如下的 longCompactions shortCompactions
前者用来处理大规模 compaction ,后者处理小规模 compaction
默认值为 2 * maxFlilesToCompact * hbase.hregion.memstore.flush.size
如果 flush size 大小是 128M ,该参数默认值就是 2 * 10 * 128M = 2.5G
合并策略选择
HBase 主要有两种 minor 策略 : RatioBasedCompactionPolicy (0.96.x 之前 )
ExploringCompactionPolicy( 当前默认 )
RatioBasedCompactionPolicy( 基于比列的合并策略 )

ExploringCompactionPolicy策略(默认策略)  

执行文件合并
分别读出待合并 hfile 文件的 KV ,并顺序写到位于 ./tmp 目录下的临时文件中
将临时文件移动到对应 region 的数据目录
compaction 的输入文件路径和输出文件路径封装为 KV 写入 WAL 日志,并打上 compaction
标记,最后强制执行 sync 将对应 region 数据目录下的 compaction 输入文件全部删除
七、  数据切分 (Region Split)
通过切分,一个 region 变为两个近似相同大小的子 region ,再通过 balance 机制均衡到不同 region
server 上,使系统资源使用更加均衡。
7.1. 切分原因
数据分布不均匀。
同一 region server 上数据文件越来越大,读请求也会越来越多。一旦所有的请求都落在同一
region server 上,尤其是很多热点数据,必然会导致很严重的性能问题。
compaction 性能损耗严重。
compaction 本质上是一个排序合并的操作,合并操作需要占用大量内存,因此文件越大,占
用内存越多
compaction 有可能需要迁移远程数据到本地进行处理( balance 之后的 compaction 就会存在
这样的场景),如果需要迁移的数据是大文件的话,带宽资源就会损耗严重。
资源耗费严重
HBase 的数据写入量也是很惊人的,每天都可能有上亿条的数据写入
不做切分的话一个热点 region 的新增数据量就有可能几十 G ,用不了多长时间大量读请求就会
把单台 region server 的资源耗光。
7.2. 触发时机
每次数据合并之后都会针对相应 region 生成一个 requestSplit 请求, requestSplit 首先会执行
checkSplit ,检测 file size 是否达到阈值,如果超过阈值,就进行切分。
检查阈值算法主要有两种: ConstantSizeRegionSplitPolicy 0.94 版本)和
IncreasingToUpperBoundRegionSplitPolicy (当前)
ConstantSizeRegionSplitPolicy :
系统会遍历 region 所有 store 的文件大小,如果有文件大小 > hbase.hregion.max.filesize(
10G) ,就会触发切分操作。
IncreasingToUpperBoundRegionSplitPolicy
如果 store 大小大于一个变化的阀值就允许 split
默认只有 1 region ,那么逻辑这个 region store 大小超过 1 * 1 * 1 * flushsize * 2 =
128M * 2 =256M 时,才会允许 split
切分之后会有两个 region ,其中一个 region 中的某个 store 大小大于 2 * 2 * 2 * flushsize * 2
= 2048M 时,则允许 split
后续超过 hbase.hregion.max.filesize + hbase.hregion.max.filesize * 随机小数 *
hbase.hregion.max.filesize.jitter 才允许 split
基本也就固定了,如果粗劣的计算可以把这个 hbase.hregion.max.filesize 的大小作为最后的
阀值,默认是 10G
7.3. 切分流程
寻找切分点
将一个 region 切分为两个近似大小的子 region ,首先要确定切分点。切分操作是基于 region
行的,每个 region 有多个 store (对应多个 column famliy )。系统首先会遍历所有 store ,找
到其中最大的一个,再在这个 store 中找出最大的 HFile ,定位这个文件中心位置对应的
rowkey ,作为 region 的切分点。
开启切分事务
切分线程会初始化一个 SplitTransaction 对象,从字面上就可以看出来 split 流程是一个类似
的过程,整个过程分为三个阶段: prepare - execute - rollback
prepare 阶段
在内存中初始化两个子 region ,具体是生成两个 HRegionInfo 对象,包含 tableName
regionName startkey endkey 等。同时会生成一个 transaction journal ,这个对象
用来记录切分的进展
execute 阶段
region server 更改 ZK 节点 /region-in-transition 中该 region 的状态为 SPLITING
master 检测到 region 状态改变。
region 在存储目录下新建临时文件夹 .split 保存 split 后的 daughter region 信息。
parent region 关闭数据写入并触发 flush 操作,将写入 region 的数据全部持久化到磁盘。
.split 文件夹下新建两个子文件夹,称之为 daughter A daughter B ,并在文件夹中生
成引用文件,分别指向父 region 中对应文件。
daughter A daughter B 拷贝到 HBase 根目录下,形成两个新的 region
parent region 通知修改 hbase.meta 表后下线,不再提供服务。
开启 daughter A daughter B 两个子 region
通知修改 hbase.meta 表,正式对外提供服务。

rollback 阶段
如果 execute 阶段出现异常,则执行 rollback 操作。
为了实现回滚,整个切分过程被分为很多子阶段,回滚程序会根据当前进展到哪个子阶
段清理对应的垃圾数据。
7.4. 切分优化
对于预估数据量较大的表,需要在创建表的时候根据 rowkey 执行 region 的预分配。
通过 region 预分配,数据会被均衡到多台机器上,这样可以一定程度解决热点应用数据量剧增导致
的性能问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值