HBase 工作机制及读写流程

tech2025-04-25  10

HBase

一图看懂 HBase 架构

HBase 最常用的shell操作

HBase 工作机制及读写流程


HBase 工作机制及读写流程

HBase前言HBase 工作机制Master工作机制Master 上线Master下线 RegionServer 工作机制Region 管理region server上线region server下线 HBase 读写流程HBase 读流程HBase 写流程flush 机制Compact合并操作Region Split操作


前言

本篇博客,为大家梳理 HBase 工作机制及读写流程。HBase 工作机制涉及 Master 工作机制和RegionServer 工作机制;读写流程涉及Region split分裂机制和compact 合并机制。过程难免枯燥乏味,但结果却很充实。


HBase 工作机制

HBase 工作机制涉及 Master 工作机制和RegionServer 工作机制。

Master工作机制

Master 的工作机制也分为上线和下线两种。

Master 上线

HBase集群中可以设置多个Hmaster,真正对外提供服务的只有一个。这是HBase集群 Master 上线 的前提条件。

HBase 高可用集群多个Hmaster设置步骤如下:

关闭 hbase: stop-hbase.sh在conf目录下创建backup-masters文件:写入本机的hostname将backup-masters文件拷贝至其他节点同目录不要在非主机上启动hbase: start-hbase.sh

配置成功结果如下:

Master启动进行以下步骤:

从zookeeper上获取唯一 一个首先获得 active master的排他锁,用来阻止其它master成为真正的Master。

扫描zookeeper上的/hbase/rs节点,通过zookeeper集群协调监督的特性(主要通过心跳包机制)获得当前可用的region server列表。

Master和每个region server通信,获得当前已分配的region和region server的对应关系。

Master扫描META表,计算得到当前还未分配的region,将他们放入待分配region列表。

从 Master 的上线过程可以看到,Master 保存的信息全是可以冗余的信息(都可以从系统其它地方(WAL)收集或者计算得到)。因此,一般 HBase 集群中总是有一个 Master 在提供服务,还有一个以上的 Master 在等待时机抢占它的位置。

Master下线

master只维护表和region的元数据,不参与表数据IO的过程,所以master下线短时间内对整个hbase集群几乎没有影响。表的数据读写还可以正常进行。

Hmaster下线后的影响有无法创建删除表,无法修改表的schema,无法进行region的负载均衡,无法处理region 上下线,无法进行region的合并(region的split可以正常进行,它只有 RegionServer 参与)。

当Master下线后,启动Zookeeper的选举机制,选出新的Master,新的Master上线,执行上线流程。

RegionServer 工作机制

RegionServer 工作机制的前提条件是 Master 使用 ZooKeeper集群 来跟踪 RegionServer 状态。

Region 管理

任何时刻,一个Region只能分配给一个RegionServer。具体如下:

Master记录了当前有哪些可用的RegionServer。以及当前哪些Region分配给了哪些RegionServer,哪些Region还没有分配。当需要分配的新的Region,并且有一个RegionServer上有可用空间时,Master就给这个RegionServer发送一个装载请求,把Region分配给这个RegionServer。RegionServer得到请求后,就开始对此Region提供服务。

region server上线

当某个 RegionServer 启动时,会首先在 ZooKeeper 上的 server 目录下创建一个代表自己的临时 ZNode,相当于抢占了server文件的独占锁。由于 Master 订阅了 server 目录下的变更消息(Watcher),当 server 目录下的文件出现新增或删除操作时,Master 可以得到来自 ZooKeeper 的实时通知,并将实时信息和操作记录到HLog文件中。因此一旦 RegionServer 上线,Master 就能马上得到消息。

region server下线

当 RegionServer 下线时,它和 ZooKeeper 的会话就会断开,ZooKeeper 会自动删除代表这台 RegionServer 的 ZNode,意味着该台 RegionServer 自动释放了 server 文件的独占锁(ZNode),也就是说Master 就可以确定这台 RegionServer 节点挂了。

无论出现哪种情况,RegionServer 都无法继续为它的 Region 提供服务了,此时,ZooKeeper 会将该台节点的变化信息反馈给Master,Master收到反馈, 就会将这台 RegionServer 上的未完成的 Region 任务分配给其它还活着的 RegionServer。

HBase 读写流程

HBase 读流程

HBase meta表

meta表存储了所有用户HRegion的位置信息,meta表内记录一行数据是用户表一个region的start key 到endkey的范围。meta表存储在一个zookeeper 知道的一个regionserver里。

HBase meta表 是HBase 读流程 寻址的关键所在。具体步骤如下图所示:

如上图HBase meta 寻址的过程一致,HBase 读流程具体步骤如下:

客户端通过 ZooKeeper 以及META表找到目标数据所在的 RegionServer(就是数据所在的 RegionServer 的主机地址);

客户端发生查询请求:联系 RegionServer 查询目标数据,然后 RegionServer 定位到目标数据所在的 Region,将信息反馈给RegionServer ;

客户端直接读取装载目标数据的 RegionServer读取 Region的结果数据。

此外, RegionServer 查询目标数据,会因为相同的cell(RowKey/ColumnFamily/Column相同)可能存在3个不同的位置,即Block Cache,MemStore,HFile中读取目标数据。具体如下图所示:

总结如下:

从Block Cache中读取从MemStore中读取从多个HFile中读取,用Bloom Filter筛掉明显不存在所需数据的HFile,Index用于快速定位HFile中的数据块

HBase 写流程

Client先访问zookeeper,找到Meta表,并获取Meta表元数据。确定当前将要写入的数据所对应的HRegion和HRegionServer服务器。Client向该HRegionServer服务器发起Put写入数据请求,在HRegionServer中它首先会将该Put操作写入WAL日志文件中,以防止数据丢失。

当写完WAL日志文件后,HRegionServer根据Put中的TableName和RowKey找到对应的HRegion,并根据Column Family找到对应的HStore,并将Put写入到该HStore的MemStore中。写入成功,返回给客户端。 当Memstore达到阈值,会把Memstore中的数据flush到Storefile中。

当Storefile越来越多,达到一定数量时,会触发Compact合并操作,将多个小文件合并成一个大文件。Storefile越来越大,Region也会越来越大,达到阈值后,会触发Split操作,变成两个文件。

flush 机制

HBase flush 机制涉及以下6大点:

hbase.regionserver.global.memstore.size: 默认堆大小的40% regionServer的全局memstore的大小,超过该大小会触发flush到磁盘的操作,默认是堆大小的40%,而且regionserver级别的flush会阻塞客户端读写

hbase.hregion.memstore.flush.size:默认128M 单个region里memstore的缓存大小,超过那么整个HRegion就会flush。

hbase.regionserver.optionalcacheflushinterval:默认1h 内存中的文件在自动刷新之前能够存活的最长时间

hbase.regionserver.global.memstore.size.lower.limit :默认堆大小 * 0.4 * 0.95 有时候集群的“写负载”非常高,写入量一直超过flush的量,这时,我们就希望memstore不要超过一定的安全设置。在这种情况下,写操作就要被阻塞一直到memstore恢复到一个“可管理”的大小,这个大小就是默认值是堆大小 * 0.4 * 0.95,也就是当regionserver级别的flush操作发送后,会阻塞客户端写,一直阻塞到整个regionserver级别的memstore的大小为 堆大小 * 0.4 *0.95为止

hbase.hregion.preclose.flush.size:默认为5M 当一个 region 中的 memstore 的大小大于这个值的时候,我们就触发 了 close,会先运行“pre-flush”操作,清理这个需要关闭的memstore,然后 将这个 region 下线。当一个 region 下线了,我们无法再进行任何写操作。 如果一个 memstore 很大的时候,flush 操作会消耗很多时间。“pre-flush” 操作意味着在 region 下线之前,会先把 memstore 清空。这样在最终执行 close 操作的时候,flush 操作会很快。

hbase.hstore.compactionThreshold:默认超过3个 一个store里面允许存的hfile的个数,超过这个个数会被写到新的一个hfile里面 ,也即是每个region的每个列族对应的memstore在fulsh为hfile的时候,默认情况下当超过3个hfile的时候就会 对这些文件进行合并重写为一个新文件,设置个数越大可以减少触发合并的时间,但是每次合并的时间就会越长。

HBase flush 机制过程如下:

Compact合并操作

StoreFile 是只读的,一旦创建后就不可以再修改。因此 HBase 的更新/修改其实是不断追加的操作。

HBase 只是增加数据,所有的更新和删除操作,都是在 Compact 阶段做。所以,用户写操作只需要进入到内存即可立即返回,从而保证 I/O 的高性能。HBase中Compact分为两种:Minor Compact和Major Compact。

Minor Compact:是指选取一些小的、相邻的HFile将他们合并成一个更大的StoreFile,在这个过程中不会处理已经Deleted或Expired的Cell。(BigTable中将memtable的数据flush的一个HFile/SSTable称为一次Minor Compaction)

Major Compact:是指将所有的HFile合并成一个HFile,可以手动触发或者自动触发,但是会引起性能问题,一般安排在周末。

Region Split操作

Client 写入数据 --> 存入 MemStore,一直到 MemStore 写满 --> Flush 成一个 StoreFile,直至增长到一定阈值 --> 触发 Compact 合并操作 --> 多个 StoreFile 合并成一个 StoreFile,同时进行版本合并和数据删除 --> 当 StoreFile 经过 Compact 之后,逐步形成越来越大的 StoreFile --> 单个 StoreFile 大小超过一定阈值后,触发 Split 操作,把当前 Region Split 成 2 个 Region,原来的 Region 会下线,新 Split 出来的 2 个子 Region 会被 HMaster 分配到相应的 RegionServer 上,使得原先的一个 Region 的压力被分流到 2 个 Region 上。

Region Split操作中是怎么样把 Region Split 成 2 个 Region的?什么情况下开始分裂,分裂有哪些方式,可以修改分裂配置吗?下面我将详细的介绍这些问题。

Region Split 流程

借鉴HBase:分布式列式NoSQL数据库的Region Split 流程说到非常好,这里我直接引用介绍如下:

RegionServer在本地决定分割HRegion,并准备分割。第一步,汇报给zookeeper。master获取zookeeper中的状态。RegionServer在HDFS的父目录区域目录下创建一个名为“.splits”的子目录。RegionServer关闭父HRegion,强制刷新缓存,并将该区域标记为本地数据结构中的脱机状态。此时,来到父区域的客户端请求将抛出NotServingRegionException异常。客户端将重试一些备用值。RegionServer在.splits目录下创建Region目录,为子区域A和B创建必要的数据结构。然后,它分割存储文件,因为它在父区域中为每个存储文件创建两个引用文件。那些引用文件将指向父Region文件。RegionServer在HDFS中创建实际的区域目录,并移动每个子Region的引用文件。RegionServer向META表发送请求。将父HRegion设置为.META中的脱机状态,并添加关于子HRegion的信息。在这时,在META中不会为女儿分配单独的条目。客户端会看到父区域是分割的,如果他们扫描.META,但不知道子HRegion,直到他们出现在.META。RegionServer并行open子HRegion接受写入。RegionServer将女儿A和B添加到.META。以及它所在地区的信息。此后,客户可以发现新的地区,并向新的地区发出请求,之前的缓存失效。HRegion Server向zookeeper汇报split结束的消息,master进行负载均衡。拆分后,meta表和HDFS仍将包含对父HRegion的引用。当子HRegion进行Compaction时,这些引用信息会被删除。Master也会定期检查子HRegion,如果没有父HRegion中的信息,父HRegion将被删除。

Region Split的大小上限统一配置有以下两种方法进行更改默认配置项

通过修改全局配置文件hbase.hregion.max.filesize和hbase.hregion.memstore.flush.size:

<property> <name>hbase.hregion.max.filesize</name> <value>10 *1024* 1024 * 1024</value> # 默认为10G </property> <property> <name>hbase.hregion.memstore.flush.size</name> <value>128</value> # 默认为128M </property>

单独设置

# 第一步 禁用 TABLE_NAME disable 'TABLE_NAME' # 第二步 单独设置TABLE_NAME最大文件MAX_FILESIZE alter 'TABLE_NAME',METHOD => 'table_att',MAX_FILESIZE=>'134217728' # 第三步,启用 TABLE_NAME enable'TABLE_NAME'

Region Split 策略有以下四种方法

1. 最简单粗暴的策略ConstantsizeRegionSplitPolicy

就是Hbase默认的拆分。

2. 限制不断增长的文件尺寸分裂策略IncreasingToUpperBoundRegionSplitPolicy

说明:

这是HBase 0.94.0 默认regionsplit策略

根据根据公式Math.min(r^2*flushSize,maxFileSize)确定split的maxFilesize;这里假设flushsize为128M。拆分过程如下:

第一次拆分大小为: min(10G,l*1*128M)=128M 第二次拆分大小为: min(10G,3*3*128M)=1152M 第三次拆分大小为: min(10G,5*5*128M)=3200M 第四次拆分大小为: min(10G,7*7*128M)=6272M 第五次拆分大小为: min(10G,9*9*128M)=10G

两种split 拆分方式

同键(以定义的分隔符左侧作为是否同键认定)进同区DelimitedKeyPrefixRegionsplitPolicy.delimiter,比如"-"为分隔符进行分区同键(截取从左向右固定长度的RowKey)进同区KeyPrefixRegionSplitPolicy.prefix_length,默认指定长度为16

3. 热点Region Split分裂策略BusyRegionSplitPolicy

说明:

热点 Region 分裂策略:为被高频访问的 Region 设计的 Region Split分裂策略

请求阻塞率:未达到该阻塞率不分裂,默认为20%

<property> <name>hbase.busy.policy.blockedRequests</name> <value>0.2</value> </property>

最小年龄10分钟:少于该时间不分裂,默认10分钟

<property> <name>hbase.busy.policy.minAge</name> <value>600000</value> </property>

计算是否繁忙的窗口:每隔多久统计一次:默认5分钟

<property> <name>hbase.busy.policy.aggWindow</name> <value>300000</value> </property> 统计前提:当前时间-上次统计时间 >= hbase.busy.policy.aggWindow计算公式:被阻塞访问次数/总访问次数 > hbase.busy.policy.blockedRequests以上1和2条件成立:当前Region为Busy注意:经常出现繁忙Region可以采用,但不确定因素会存在

4. 关闭/启用自动分裂DisabledRegionSplitPolicy

预分裂:建表时定义自动拆分

# 语法: hbase org.apache.hadoop.hbase.util.RegionSplitter test_split_table HexStringSplit -c 3 -f test_col # 说明: test_split_table 表名 HexStringSplit 拆分点算法 -c 分区数量 -f 列族名称

例如:

hbase(main):011:0> scan 'hbase:meta',{STARTROW=>'productinfo',LIMIT=>10}

结果:

预分裂:建表时定义手动拆分

create 'test_split2','mycf2',SPLITS=>['aaaa','bbbb','cccc','dddd','eeee','fff']

结果:

强制分裂:

split 强制分裂方法的调用方式: split 'tableName' split 'namespace:tableName' split 'regionName' # format:'tableName,startKey,id' split 'tableName','splitKey' split 'regionName','splitKey'

例如:

hbase (main) :015:0>split 'student', 'b'

结果:

推荐方案

(1)用预拆分导入初始数据。

(2)然后用自动拆分来让HBase来自动管理Region

最新回复(0)