五十一、HBase的原理

上一篇文章我们介绍了如何部署HBase以及HBase常用的命令行操作,本文我们从HBase的读写流程出发来看一下HBase的原理。关注专栏《破茧成蝶——大数据篇》,查看更多相关的内容~


目录

一、HBase的架构

二、HBase的读数据流程

三、HBase的写数据流程

四、数据flush的过程

五、数据合并(compaction)过程


一、HBase的架构

尽管在前文中我们已经说过HBase的架构了,但是为了加深印象,我们这里再唠叨一下,他的架构图如下所示:

HMaster用于管理整个HBase集群,即管理每个HRegionServer,它掌握着整个集群的元数据信息。HMaster的作用主要有:1、管理用户对Table的增、删、改、查操作;2、记录Region在哪台 HRegionServer上;3、在Region Split后,负责新Region的分配;4、新机器加入时,管理HRegionServer的负载均衡,调整Region分布;5、在HRegion Server宕机后,负责失效HRegionServer上的Regions迁移。

HRegionServer是每台机器上的一个Java进程(一台机器只有一个HRegionServer进程),用来处理客户端的读写请求,并维护着元数据信息。HRegionServer的主要作用有:1、HRegionServer主要负责响应用户I/O请求,向HDFS文件系统中读写数据,是HBase中最核心的模块。2、HRegionServer管理了很多table的分区,也就是Region。

每个HRegionServer有一个HLog(有且仅有一个)。HLog是操作日志,用来做灾难恢复的,当客户端发起一个写请求时,会先往HLog中写再往Memory Store中写。为什么要设置HLog呢?假设我们进行一个写请求,会首先写到Memory Store上,等到Memory Store到达一定容量后,才会flush到StoreFile中。但是如果在这之前宕机了,那这部分操作的数据就会丢失。为了解决这个问题,于是就有了HLog。

每个HRegionServer里面有多个HRegion,一个HRegion对应于HBase的一张表(也可能是表的一部分,因为表太大了会切分,表和HRegion的对应关系是一对多),当这张表到一定大小的时候会进行切分,切分成两个HRegion,切分出来的新的HRegion会保存到另一台机器上。每个HRegionServer里面有多个HRegion,可以理解为有多张表。每个HRegion里面有多个Store(一张表中有多个列族),一个Store对应于HBase一张表的一个列族。按照这个原理,我们在设计列族的时候,可以把经常查询的列放在同一个列族,这样可以提高效率,因为在不考虑切分的情况下,同一个列族在同一个文件里面。每个Store有一个内存级别的存储Memory Store(有且仅有一个)。当Memory Store达到一定大小或一定时间后会进行数据刷写(flush),写到磁盘中(即HFile)。每个Store有多个磁盘级别的存储StoreFile,Memory Store每刷写一次就形成一个StoreFile,HFile是StoreFile在HDFS上的存储格式。

二、HBase的读数据流程

我们根据上图来说一下HBase读数据的流程。首先我们假设meta表存放在RS1上,people表行键范围1-50的存放在RS2上,行键范围51-100的存放在RS3上。Client现在要读取people表第25行,读取流程如下:

1、客户端向Zookeeper发起请求,请求元数据所在的RegionServer,Zookeeper集群存放的是HBase meta表所在的位置。

2、Zookeeper返回给客户端元数据所在的RegionServer,即RS1。

3、客户端收到应答后去请求RS1,请求查询people表的rowkey=25数据所在位置。

4、在RS1上查询meta表可知该数据在RS2机器上,即返回给客户端rowkey所在位置(RS2)。

5、客户端收到应答后去请求RS2读数据。

6、RS2查询数据返回给客户端。查询时先去内存(MemStore)查找,因为内存是最新的数据,如果找到了就返回结果,如果没找到则去缓存(blockcache(每个HRegionServer只有一个))中查找,如果找到了就返回结果,如果还没找到就去磁盘(StoreFile)找,如果在磁盘找到了,则先将结果写入缓存(blockcache),再返回给客户端,写入缓存是为了下次查询提高效率。

这里需要注意的是,在整个读数据的过程中并没有用到HMaster,即读数据的过程与HMaster无关,所以即使HMaster挂了,也仍然可以进行读数据的操作。

三、HBase的写数据流程

同样的,我们根据上图来说一下HBase写数据的流程。同样假设meta表存放在RS1上,people表行键范围1-50的存放在RS2上,行键范围51-100的存放在RS3上。Client现在要插入数据到people表中,其中rowkey=25,具体步骤如下:

1、客户端向Zookeeper发起请求,请求元数据所在的RegionServer,Zookeeper集群存放的是HBase meta表所在的位置。

2、Zookeeper返回给客户端元数据所在的RegionServer,即RS1。

3、客户端收到应答后去请求RS1,请求查询people表rowkey=25数据所在的位置。

4、在RS1上查询meta表可知该数据在RS2机器上,即返回给客户端rowkey所在位置(RS2)。

5、客户端收到应答后去请求RS2写入数据。

6、RS2收到请求,先将数据写入HLog,再将数据写入MemStore,写入MemStore后就返回给客户端写入成功信息。此时,客户端的写流程完成了。

这里同样需要注意的是,在整个写流程中HMaster也没有参与,所以如果HMaster挂了,也是可以进行写数据的。但是,如果HMaster挂的时间长了,不会触发Region切分,表就会一直变大,这样就会导致数据倾斜。

四、数据flush的过程

首先我们来看一下在hbase-default.xml配置文件中的几个配置项:

1、hbase.hregion.memstore.flush.size:默认值:128M。当Region中任意一个MemStore的大小(压缩后的大小)达到了设定值,会触发MemStore flush。

2、hbase.regionserver.optionalcacheflushinterval:默认值:3600000。HBase定期刷新MemStore,默认周期为1小时,确保MemStore不会长时间没有持久化。为避免所有的MemStore在同一时间都进行flush,定期的flush操作有20000左右的随机延时。

3、hbase.hregion.memstore.block.multiplier:默认值:2(3.0版本是4)。当Region中所有MemStore的大小总和达到了设定值(hbase.hregion.memstore.block.multiplier * hbase.hregion.memstore.flush.size,默认2*128M = 256M),会触发MemStore flush,禁止当前Region读写。

4、hbase.regionserver.global.memstore.upperLimit:默认值:0.4。当一个RegionServer中所有MemStore的大小总和达到了设定值(hbase.regionserver.global.memstore.upperLimit * hbase_heapsize,默认 0.4 * RegionServer堆内存大小),会触发全部MemStore flush,不管MemStore有多小。而且regionserver级别的flush会阻塞客户端读写。

5、hbase.regionserver.global.memstore.lowerLimit:默认值:0.38。与hbase.regionserver.global.memstore.upperLimit类似,区别是:当一个RegionServer中所有MemStore的大小总和达到了设定值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,默认 0.38 * RS堆内存大小),会触发部分MemStore flush。Flush顺序是按照Region的总MemStore大小,由大到小执行,先操作MemStore最大的Region,再操作剩余中最大的Region,直至总体MemStore的内存使用量低于设定值(hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize)。

6、hbase.regionserver.maxlogs:默认值:32。当一个RegionServer中HLog数量达到设定值,系统会选取最早的一个HLog对应的一个或多个Region进行flush。当增加MemStore的大小以及调整其他的MemStore的设置项时,也需要去调整HLog的配置项。否则,WAL的大小限制可能会首先被触发。因而,将利用不到其他专门为Memstore而设计的优化。需要关注的HLog配置是HLog文件大小,由参数hbase.regionserver.hlog.blocksize设置(默认512M),HLog大小达到上限,或生成一个新的HLog。通过WAL限制来触发Memstore的flush并非最佳方式,这样做可能会一次flush很多Region,尽管“写数据”是很好的分布于整个集群,进而很有可能会引发flush“大风暴”。

除此之外,用户还可以使用命令在交互界面手动触发flush:

# 对某张表进行flush
flush 'TABLENAME'

# 对某个Region进行flush
flush 'REGIONNAME'

Flush是由HMaster触发的,Flush顺序是按照Memstore由大到小执行,先Flush Memstore最大的Region,再执行次大的,直至总体Memstore内存使用量低于阈值(hbase.regionserver.global.memstore.lowerLimit)。这里需要注意的是HBase的最小flush单元是HRegion而不是单个MemStore。

flush的过程大致分为三个阶段,分别是:

1、prepare阶段:遍历当前Region中的所有MemStore,将MemStore中当前数据集kvset做一个快照snapshot,然后再新建一个新的kvset,后期的所有写入操作都会写入新的kvset中。整个flush阶段读操作读MemStore的部分,会分别遍历新的kvset和snapshot。prepare阶段需要加一把updateLock对写请求阻塞,结束之后会释放该锁。因为此阶段没有任何费时操作,因此持锁时间很短。

2、flush阶段:遍历所有MemStore,将prepare阶段生成的snapshot持久化为临时文件,临时文件会统一放到目录.tmp下。这个过程因为涉及到磁盘IO操作,因此相对比较耗时。

3、commit阶段:遍历所有的MemStore,将flush阶段生成的临时文件移到指定的Column family目录下,生成对应的Storefile(HFile)和Reader,把Storefile添加到HStore的Storefiles列表中,最后再清空prepare阶段生成的snapshot。

五、数据合并(compaction)过程

由于在flush过程中,可能会产生很多小文件,而HDFS不适合存储小文件,所以在写入HDFS之前会进行合并操作。首先我们来看一下在hbase-default.xml配置文件中的几个配置项:

1、hbase.hregion.majorcompaction:一个region进行major compaction合并的周期,在这个点的时候,这个region下的所有hfile会进行合并,默认是7天,major compaction非常耗资源,建议生产关闭(设置为0),在应用空闲时间手动触发。

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

需要注意的是,当我们利用shell命令或者API删除数据的时候,数据并没有被删除,而是被打上标记,而是在这里的compaction合并过程中才会被完全删除。

 

本文到此已经接近尾声了,本文主要讲述了一下HBase的原理,内容可能比较抽象。你们在此过程中遇到了什么问题,欢迎留言,让我看看你们都遇到了哪些问题~

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

象在舞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值