一文带你读懂Hbase概念、架构及原理

0. 前言

    Hbase在大数据领域中起着重要角色,在处理海量数据时候能达到秒级响应,很多公司都有自己的Hbase集群,在存储处理数据方面有着明显的优势。本文从Hbase的基本概念及架构原理进行深入解读,旨在帮助读者能从整体上认识Hbase,并对Hbase基本架构原理有个深入了解。

   通过本文你可以获取如下几方面知识:

  • (1)Hbase是什么
  • (2)Hbase与关系型数据库之间的区别
  • (3)Hbase的特点
  • (4)Hbase架构及原理
  • (5)Hbase读写流程
  • (6)storefile合并过程
  • (7)region切分过程

1. 初识Hbase

1.1 Hbase的定义

    HBase 是一种分布式、可扩展、支持海量数据存储的 NoSQL 数据库。一句话概括为:hbase是hadoop的数据库,是构建在hadoop之上的分布式数据库。逻辑上,HBase 的数据模型同关系型数据库很类似,数据存储在一张表中,有行有列。但从 HBase 的底层物理存储结构(K-V)来看,HBase 更像是一个 multi-dimensional map(可以理解为多维map结构)。

1.2 Hbase的逻辑结构

Hbase逻辑结构图

     如上图所示反映了Hbase逻辑结构,如字段name下的张三数据是如何被定位的呢?首先每一条数据都有一个rowkey,rowkey类似于mysql表中的主键具有唯一性。其次有一个列族(列簇)这里我们就叫为列族吧,正确的应该是列簇,但是呢很多程序员也都这么叫,错误的叫的多了也便是正确的了,(哈哈,所以说这个世界上在人类所能认知的范围内,真理还是掌握在大多数人手中,因为少数人服从多数人。),列族其实就是列的集合,一个列族对应物理存储上一个文件夹。为什么要设计列族这个概念呢?我想可能是对表的一种切分吧,当一个列族所能容纳字段越来越多的时候,此时表越来越宽就需要对表进行纵向切分,便有了列族的概念。当表的记录数越来越多的时候,表变得越来越高(高表),此时就需要对表进行横向切分,横向切分后得到的切片就是region,所以列族是对表的垂直切分,region是对表的横向切分。   一张表一条数据实际上是K-V结构,只不过这个key是多维的。

1.3 Hbase物理存储结构

Hbase物理存储结构图

     由上图可以看出,对比关系型数据库中表结构,一条数据被当成了多条记录存储,每一个value值前面都对应rowkey->column family->column qualifier->timestamp作为一个key,不同数据的版本需要timestamp进行标记,从上面也可以看出hbase存储相对于关系型数据库结构化表来讲,数据存在一定膨胀,因此在使用时候需要进行压缩。在表中我们也可以看到Type类型,他标记着这条数据操作行为,put为插入数据,delete为删除数据。

1.4 Hbase数据模型

(1)Name Space

        Name Space命名空间,类似于关系型数据库中的数据库的概念(database),每个命名空间下有多张表。HBase有两个自带的命名空间,分别是 hbase 和 default,hbase 中存放的是 HBase 内置的表,default 表是用户默认使用的命名空间。

(2)Region

     类似于关系型数据库的概念。不同的是,HBase 定义表时只需要声明列族即可,不需要声明具体的列(与关系型数据库最主要的一个区别)。这意味着,往 HBase 写入数据时,字段可以动态、按需指定。因此,和关系型数据库相比,HBase 能够轻松应对字段变更的场景。

(3) Row
     HBase 表中的每行数据都由一个 RowKey 和多个  Column(列)组成,数据是按照 RowKey的字典顺序存储的,并且查询数据时只能根据 RowKey 进行检索,所以 RowKey 的设计十分重要。
(4) Column
     HBase 中的每个列都由 Column Family(列族)和 Column Qualifier(列限定符)进行限定,例如 info:name,info:age。建表时,只需指明列族,而列限定符无需预先定义,也就是定义一个hbase表时候只需要指定列族即可,不像关系型数据库建表时候字段类型是固定的,这也说明了Hbase的灵活性。
(5) Time Stamp
    用于标识数据的不同版本(version),每条数据写入时,如果不指定时间戳,系统会自动为其加上该字段,其值为写入 HBase 的时间。

(6) Cell
     由{rowkey, column Family:column Qualifier, time Stamp} 唯一确定的单元。cell 中的数据是没有类型的,全部是字节码形式存贮。

2 Hbase与关系型数据库之间的对比

表1:Hbase与RMDBS数据之间的对比
  Hbase  RDBMS                           
结构上 数据库以region的形式存在 数据库以表的形式存在
构建在HDFS之上 支持FAT、NTFS、EXT、文件系统
使用WAL(Write-Ahead Logs)存储日志 使用Commit log存储日志
借助Zookeeper系统 参考系统是坐标系统
使用行键(row key) 使用主键(PK)
支持分片 支持分区
使用行、列、列族和单元格 使用行、列、单元格
功能上 支持向外扩展 支持向上扩展
使用API和MapReduce来访问HBase表数据 使用SQL查询
面向列,即每一列都是一个连续的单元 面向行,即每一行都是一个连续单元
数据总量不依赖具体某台机器,而取决于机器数量 数据总量依赖于服务器配置
HBase不支持ACID(AtomicityConsistencyIsolationDurability 支持ACID
适合结构化数据和非结构化数据 适合结构化数据
一般都是分布式的 传统关系型数据库一般都是中心化的
HBase不支持事务 支持事务
不支持Join 支持Join

3 Hbase的优势

图1:Hbase优势

(1)横向扩展

        HBase支持横向扩展,也就是说如果现有服务器硬件性能出现瓶颈,不需要停现有的集群提升硬件配置,而只需要在现有的正在运行的集群中添加新的机器节点即可,而且新的RegionServer一旦建立完毕,集群会开始重新调整。

(2)列式存储

        HBase是面向列存储的,每个列都单独存储,所以在HBase中列是连续存储的,而行不是。并且列可以动态增加,列为空时就不存储数据,节省存储空间,RDBMS的行有多少列是固定的,若某一列为null时就浪费了存储空间。HBase为null的列不会被存储,这样既节省了空间又提高了读性能。

(3)半结构化或非结构化数据

      Hbase支持半结构化或非结构化的数据存储,对于数据结构字段不够确定或杂乱无章非常难按一个概念去进行抽取的数据适合用HBase,因为HBase支持动态添加列。

(4)高并发、随机读写

        HBase采用LSMT(log-structured merge-tree)架构进行设计,这种架构会周期性地将小文件合并成大文件以减少磁盘访问同时减少NameNode压力。本质上解决了HDFS不能随机读写的问题,在写入(插入)数据方面具有很大优势,特别在PB级以上数据优势明显,适用于插入比查询操作更频繁的情况,写比查询更高效。(强调写的能力)

(5)自动切分

        HBase表是由分布在多个RegionServer中的region组成的,这些RegionServer又分布在不同的DataNode上,如果一个region增长到了一个阈值,为了负载均衡和减少IO,HBase可以自动或手动干预的将region切分为更小的region,也称之为subregion。

(6)自动故障处理和负载均衡

        HBase运行在HDFS上,所以HBase中的数据以多副本形式存放,数据也服从分布式存放,数据的恢复也可以得到保障。另外,HMaster和RegionServer也是多副本的。

4 Hbase基本架构及原理                                                

图1:Hbase集群服务架构图

   图1反映了Hbase集群间各个服务角色之间的关系,其服务之间的关系及作用如下描述:

(1)架构角色

   1)Region Server(RS)

   Region Server 为 Region 的管理者,其实现类为 HRegionServer,主要作用如下:

  •   对于数据的操作:get, put, delete;
  •   对于 Region 的操作:splitRegion、compactRegion。

   总结:RS其实实现的是类似于关系型数据库中的DML操作,实现对数据的管理

   2)Master

    Master 是所有 Region Server 的管理者,其实现类为 HMaster,主要作用如下:

  •     对于表的操作:create, delete, alter.(类似于关系型数据库的DDL操作,实现的是对表的管理);
  •     对于RegionServer的操作:分配regions到每个RegionServer;监控每个RegionServer的状态;负载均衡和故障转移;发  现失效的RegionSercer重新为他们分配自己负责的region。
  •    hdfs上的垃圾文件回收(标记为删除的且经过major compact的文件)

  3)zookeeper

    HBase 通过 Zookeeper 来做 Master 的高可用、RegionServer 的监控、元数据的入口以及集群配置的维护等工作。

  • 负责Master高可用时候切换工作
  • 降低Master的负担,承担Master部分工作

  4)HDFS

     HDFS 为 HBase 提供最终的底层数据存储服务,同时为 HBase 提供高可用的支持,底层的最终存储是构建在HDFS之上的,这也是为什么Hbase是hadoop的数据库的缘由

     图2反映了Hbase的架构原理图,其原理如下所示:

图2:Hbase原理架构图

    1)Storefile

         保存实际数据的物理文件,StoreFile以 HFile 的形式存储在 HDFS 上。每个 Store 会有一个或多个 StoreFile(HFile),数据在每个 StoreFile 中都是有序的。HFile可以理解为一种文件格式,与orc,txt等是一样的概念,只不过是以key,value的形式存储。

    2)MemStore

      写缓存,由于HFile中数据要求是有序的,所以数据是先存储在MemStore中,排好序后,等到达刷写时机才会刷写到Hflie,每次刷写都会形成一个新的HFile

    3) WAL
    由于数据要经 MemStore 排序后才能刷写到 HFile,但把数据保存在内存中会有很高的概率导致数据丢失,为了解决这个问题,数据 会先写在一个叫做 Write-Ahead logfile 的文件中,然后再写入 MemStore 中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。WAL类似于Mysql的binlog,用来做灾难恢复。Hlog记录所有数据的变更,一旦数据修改,就可以从Hlog恢复,每个HRegionSever维护一个HLog.

 疑问,为什么是HRegionSever维护一个HLog,而不是HRgeion维护一个HLog?
 解释:这样不同region(不同表)的日志会混在一起,这样做的目的不断追加单个文件相对于同时写多个文件而言,可以减少磁盘的寻址次数,可以提高对table的写性能。
 缺点:如果一台HRegionSever下线了,为了恢复其上的region,需要将HRegionSever上的log进行拆分,然后分发到其他HRegionSever进行恢复。

   4) HRegion  

      HRegion也就是我们所说的Region,  一个Table由一个或者多个Region组成,一个Region中可以看成是Table按行切分且有序的数据块,每个Region都有自身的StartKey、EndKey。一个Region由一个或者多个Store组成,每个Store存储该Table对应Region中一个列簇的数据,相同列簇的列存储在同一个Store中。同一个Table的Region会分布在集群中不同的RegionServer上以实现读写请求的负载均衡。故,一个RegionServer中将会存储来自不同Table的N多个Region。

    Store、Region与Table的关系可以表述如下:多个Store(列簇)组成Region,多个Region(行数据块)组成完整的Table。

其中,Store由Memstore(内存)、StoreFile(磁盘)两部分组成。

5. Hbase写入数据的流程

5.1 写入流程分析

写入流程具体如下图所示:

写流程:
1)Client 先访问 zookeeper,获取 hbase:meta 表位于哪个 Region Server。
2)访问对应的 Region Server,获取 hbase:meta 表,根据读请求的 namespace:table/rowkey,
查询出目标数据位于哪个 Region Server 中的哪个 Region 中。并将该 table 的 region 信息以
及 meta 表的位置信息缓存在客户端的 meta cache,方便下次访问。
3)与目标 Region Server 进行通讯;
4)将数分别写入HLog和MemStore中,数据会在MemStore 进行排序(若MemStore中数据有丢失,则可以从HLog中进行恢复);
5)向客户端发送 ack;(当写入到内存时就可立即返回,代表写入成功,这也是Hbase,I/O高性能的保证)
6)当MemStore 的数据达到一定阈值后,将数据刷写到StoreFile文件。
7)当多个StoreFile文件达到一定大小后会触发Compact操作,合并为一个StoreFile,这里同时进行版本的合并(更新)及数据的删除
8)当Compact后会逐步形成越来越大的StoreFile,此时会触发Split操作,把当前的StoreFile进行分割,相当于一个大的Region进行分割的过程,大的Region分割成两个。

     Hbase的操作其实只有两个查询和写入(增加),其他的更新和删除数据都是在Compact阶段完成的,所以用户的写操作只需要到内存即可立即返回,从而保证I/O的高性能。

     老版本写的时候需要先找-Root-表,-Root-是存放meta表的元信息,为什么新版本取消了这一步操作呢?

  • -ROOT-:记录.META.表的Region信息。
  • hbase:meta:记录用户表的Region信息。
  • 其中-ROOT-表本身只会有一个region,这样保证了只需要三次跳转,就能定位到任意region
原因可能是Hbase根据用户使用的实际需求,大部分用户存不了这么多数据量而取消掉。最开始设计时候,Hbase

考虑到meta表有可能分裂造成找不到meta表信息,于是采用-Root-表来存放meta表的元信息,但是实际中用户根

本就存不了那么多数据致使meta表分裂。试想meta表存放元信息,默认10G分裂,我们可以简单的估算一下,假设

meta表中一条数据为1K(实际上应该比这小很多,为了估算方便暂时取1K),1K对应10G,那么等到分裂时,10G

的meta表元信息对应数据量大概为:1024*1024*10*10=104857600G=100PB。实际而言大部分公司使用来看是到

不了这个数据量的,因而Hbase根据实际使用需求取消掉了-Root-表。

5.2 刷写时机分析

    1.memstroe级别的限制:当Region中任意一个MemStore的大小达到了上限,会触发memstore的刷写。(木桶原理)

      一个store(列簇)对应一个memstore,一个region中包含多个列簇,也就是说一个region中可能包含多个memstore

刷写阈值:
hbase.hregion.memstore.flush.size(默认值128M),

    2.Region级别限制:当Region中所有Memstore的大小总和达到了上限,会触发memstore刷写。

      当memstore的大小达到了如下参数值时

hbase.hregion.memstore.flush.size(默认值128M)* hbase.hregion.memstore.block.multiplier(默认值4)时

     也就是128*4=512MB的时候,那么除了触发 MemStore 刷写之外,HBase 还会在刷写的时候同时阻塞所有写入该 Store 的写请求!这时候如果你往对应的 Store 写数据,会出现 RegionTooBusyException 异常。会阻止继续往该memstore写数据。

    3.Region Server级别限制:当一个Region Server中所有Memstore的大小总和达到了上限,会触发部分Memstore刷写。

       当RegionServer中memstore的总大小达到

      java_heapsize

      * hbase.regionserver.global.memstore.size(默认值0.4)

      * hbase.regionserver.global.memstore.size.lower.limit(默认值0.95),

    Flush顺序是按照Memstore由大到小执行,先Flush Memstore最大的Region,再执行次大的,直至总体Memstore内存使用量低于阈值:

hbase.regionserver.global.memstore.lowerLimit * hbase_heapsize,默认 38%的JVM内存使用量。

0.4*0.95=0.38

   需要注意的是,如果达到了 RegionServer 级别的 Flush,那么当前 RegionServer 的所有写操作将会被阻塞,而且这个阻塞可能会持续到分钟级别。

  4. HBase定期刷写MemStore:当到达自动刷写的时间,也会触发memstore flush。自动刷新的时间间隔由该属性进行配置               

hbase.regionserver.optionalcacheflushinterval(默认1小时)。

为避免所有的MemStore在同一时间都进行flush导致的问题,定期自动刷写会有 0 ~ 5 分钟的延迟

   5.HLog级别的限制:当WAL文件的数量超过hbase.regionserver.max.logs,region会按照时间顺序依次进行刷写,直到WAL文件数量减小到hbase.regionserver.max.log以下(该属性名已经废弃,现无需手动设置,最大值为32)。

   6.手动执行flush:用户可以通过shell命令 flush ‘tablename’或者flush ‘region name’分别对一个表或者一个Region进行flush。

     Shell 中通过执行 flush 命令:

hbase> flush 'TABLENAME'
hbase> flush 'REGIONNAME'
hbase> flush 'ENCODED_REGIONNAME'
hbase> flush 'REGION_SERVER_NAME'

 调用可以调用 Admin 接口提供的方法:


void flush(TableName tableName) throws IOException;
void flushRegion(byte[] regionName) throws IOException;
void flushRegionServer(ServerName serverName) throws IOException;

Memstore Flush对业务读写的影响:

影响甚微:

正常情况下,大部分Memstore Flush操作都不会对业务读写产生太大影响,比如这几种场景:

HBase定期刷新Memstore、手动执行flush操作、触发Memstore级别限制、触发HLog数量限制以及触发Region级

别限制等,这几种场景只会阻塞对应Region上的写请求,阻塞时间很短,毫秒级别。

影响较大:

然而一旦触发Region Server级别限制导致flush,就会对用户请求产生较大的影响。会阻塞所有落在该Region 

Server上的更新操作,阻塞时间很长,甚至可以达到分钟级别。一般情况下Region Server级别限制很难触发,

但在一些极端情况下也不排除有触发的可能

5.3 合并过程分析

     由于memstore每次刷写都会生成一个新的HFile,且同一个字段的不同版本(timestamp)和不同类型(Put/Delete)有可能会分布在不同的HFile中,因此查询时需要遍历所有的HFile。为了减少HFile的个数,以及清理掉过期和删除的数据,会进行StoreFile Compaction。

     Compaction分为两种,分别是Minor CompactionMajor Compaction。Minor Compaction会将临近的若干个较小的HFile合并成一个较大的HFile,但不会清理过期和删除的数据。Major Compaction会将一个Store下的所有的HFile合并成一个大HFile,并且清理掉过期和删除的数据

5.4 Region切分分析

      默认情况下,每个Table起初只有一个Region,随着数据的不断写入,Region会自动进行拆分。刚拆分时,两个子Region都位于当前的Region Server,但处于负载均衡的考虑,HMaster有可能会将某个Region转移给其他的Region Server。Hbase的Region切分策略主要有以下几个策略

     Region Split时机:

   1. ConstantSizeRegionSplitPolicy:0.94版本前默认切分策略。当1个region中的某个Store下所有StoreFile的总大小超过

hbase.hregion.max.filesize

        该Region就会进行拆分(0.94版本之前)。

    这个是比较容易产生误解的切分策略,从字面意思来看,当region大小大于某个阈(hbase.hregion.max.filesize)之后就会触发切分,实际上并不是这样,真正实现中这个阈值是对于某个store来说的,即一个region中最大store的大小大于设置阈值之后才会触发切分。另外一个大家比较关心的问题是这里所说的store大小是压缩后的文件总大小还是未压缩文件总大小,实际实现中store大小为压缩后的文件大小(采用压缩的场景)。

    ConstantSizeRegionSplitPolicy相对来来说最容易想到,但是在生产线上这种切分策略却有相当大的弊端:切分策略对于大表和小表没有明显的区分。阈值(hbase.hregion.max.filesize)设置较大对大表比较友好,但是小表就有可能不会触发分裂,极端情况下可能就1个,这对业务来说并不是什么好事。如果设置较小则对小表友好,但一个大表就会在整个集群产生大量的region,这对于集群的管理、资源使用、failover来说都不是一件好事。

    2. IncreasingToUpperBoundRegionSplitPolicy : 0.94版本~2.0版本默认切分策略。当1个region中的某个Store下所有StoreFile的总大小超过如下参数值时

    Min(R^2 * "hbase.hregion.memstore.flush.size",hbase.hregion.max.filesize")

    该Region就会进行拆分,其中R为当前Region Server中属于该Region(table)的个数(0.94版本~2.0版本)。

   这种切分策略很好的弥补了ConstantSizeRegionSplitPolicy的短板,能够自适应大表和小表。而且在大集群条件下对于很多大表来说表现很优秀,但并不完美,这种策略下很多小表会在大集群中产生大量小region,分散在整个集群中。而且在发生region迁移时也可能会触发region分裂。

   3. SteppingSplitPolicy: 2.0版本默认切分策略。这种切分策略的切分阈值又发生了变化,相比  IncreasingToUpperBoundRegionSplitPolicy简单了一些,依然和待分裂region所属表在当前regionserver上的region个数有关系。

如果region个数等于1,切分阈值为flush size * 2,否则为MaxRegionFileSize

这种切分策略对于大集群中的大表、小表会比IncreasingToUpperBoundRegionSplitPolicy更加友好,小表不会再产生大量的小region,而是适可而止。 

   4.其他策略:另外,还有一些其他分裂策略,比如使用DisableSplitPolicy:可以禁止region发生分裂;而KeyPrefixRegionSplitPolicy,DelimitedKeyPrefixRegionSplitPolicy对于切分策略依然依据默认切分策略,但对于切分点有自己的看法,比如KeyPrefixRegionSplitPolicy要求必须让相同的PrefixKey待在一个region中。在用法上,一般情况下使用默认切分策略即可,也可以在cf级别设置region切分策略,命令为:

create ’table’, {NAME => ‘cf’, SPLIT_POLICY => ‘org.apache.hadoop.hbase.regionserver. ConstantSizeRegionSplitPolicy'}

5.5 meta存储位置寻找

hbase:meta表,记录了集群中所有存储region的位置信息。

如何寻找这张表呢?首先得知道这张表储存在哪个机器上?

我们知道meta存储在zookeeper中,所以我们需要先连接到zookeeper的客户端进行查看

步骤1:先进入zookeeper的客户端

进入zk的bin目录下执行如下命令

./zkCli.sh -server bigdata1:2181

进去后连接状态如下:

执行ls /命令查看其下面的目录

 目录下有个hbase-unsecure是我们需要找的目录

查看该目录 ls /hbase-unsecure

 有个meta-region-server是我们需要寻找的表。

查看该表中的内容:

 get /hbase-unsecure/meta-region-server

可以看到该表存储在bigdata4机器上。

通过在hbase中,scan 'hbase:meta'就可以看到哪个表示哪些服务器在维护。

6. Hbase读流程分析

1)Client先访问zookeeper,获取hbase:meta表位于哪个Region Server。

2)访问对应的Region Server,获取hbase:meta表,根据读请求的namespace:table/rowkey,查询出目标数据位于哪个Region Server中的哪个Region中。并将该table的region信息以及meta表的位置信息缓存在客户端的meta cache,方便下次访问。

3)与目标Region Server进行通讯;

4)分别在Block Cache(读缓存),MemStore和Store File(HFile)中查询目标数据,并将查到的所有数据进行合并。此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete)。

5) 将从文件中查询到的数据块(Block,HFile数据存储单元,默认大小为64KB)缓存到Block Cache。

6)将合并后的最终结果返回给客户端。

7 小结

     本文对HBase的基本概念、原理架构进行了深入分析,并对其读写流程、Flush机制、切分机制、合并机制进行了研究。通过本文你可以对HBase相关的基本原理概念流程有一定的认识。

参考链接:

https://yq.aliyun.com/articles/727443?spm=a2c4e.11163080.searchblog.129.48782ec1QqNAj4

http://hbasefly.com/2016/03/23/hbase-memstore-flush/

https://www.iteblog.com/archives/category/hbase/page/3/

http://hbasefly.com/2017/08/27/hbase-split/

发布了18 篇原创文章 · 获赞 208 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/godlovedaniel/article/details/104533109