学习笔记之hbase

1、存储模型

在这里插入图片描述

  • table中的所有行都按照row key的字典序排列。
  • table在行的方向上分割为多个HRegion,每个HRegion分散在不同的RegionServer中。
  • 每个HRegion由多个Store构成(每个表一开始只有一个region,随着数据不断插入表,region不断增大,当增大到一个阀值的时候(依据store的大小判断,默认初次分割阈值为128M,详细分割策略见:https://blog.csdn.net/jiangmingzhi23/article/details/78609864),HRegion就会等分会两个新的HRegion),每个Store由一个MemStore和零个或多个StoreFile组成,每个Store保存一个Columns Family中
  • StoreFile以HFile格式存储在HDFS中。
  • HRegion是Hbase中分布式存储和负载均衡的最小单元。最小单元就表示不同的HRegion可以分布在不同的HRegion server上。但一个HRegion是不会拆分到多个server上的(即一个HRegion不会跨server调度和存储)。
  • HRegion虽然是分布式存储的最小单元,但并不是存储的最小单元(Data Block是HBase的最小存储单元,一个Data Block由一个魔数和一系列的KeyValue(Cell)组成,魔数是一个随机的数字,用于表示这是一个Data Block类型,以快速监测这个Data Block的格式,防止数据的破坏。)。

**注:**一张hbase表在刚刚创建的时候,默认只有一个region。所以多有关于这张表的请求都被路由到同一个region server,不管集群中有多少region server。这也就是为什么hbase表在刚刚创建的阶段不能充分利用整个集群的吞吐量的原因。如果想开始就用到集群的吞吐量,可以再创建表时候,启用预分区方式实现。

2、架构模型图

在这里插入图片描述

  • Client
    首先当一个请求产生时,HBase Client使用RPC(远程过程调用)机制与HMaster和HRegionServer进行通信,对于管理类操作,Client与HMaster进行RPC;对于数据读写操作,Client与HRegionServer进行RPC。

  • Zookeeper
    HBase Client使用RPC(远程过程调用)机制与HMaster和HRegionServer进行通信,但如何寻址呢?由于Zookeeper中存储了-ROOT-表的地址和HMaster的地址,所以需要先到Zookeeper上进行寻址。
    HRegionServer也会把自己以Ephemeral方式注册到Zookeeper中,使HMaster可以随时感知到各个HRegionServer的健康状态。此外,Zookeeper也避免了HMaster的单点故障。

  • HMaster
    当用户需要进行Table和Region的管理工作时,就需要和HMaster进行通信。HBase中可以启动多个HMaster,通过Zookeeper的Master Eletion机制保证总有一个Master运行。
    管理用户对Table的增删改查操作
    管理HRegionServer的负载均衡,调整Region的分布
    在Region Split后,负责新Region的分配
    在HRegionServer停机后,负责失效HRegionServer上的Regions迁移

  • HRegionServer
    当用户需要对数据进行读写操作时,需要访问HRegionServer。HRegionServer存取一个子表时,会创建一个HRegion对象,然后对表的每个列族创建一个Store实例,每个Store都会有一个 MemStore和0个或多个StoreFile与之对应,每个StoreFile都会对应一个HFile, HFile就是实际的存储文件。因此,一个HRegion有多少个列族就有多少个Store。 一个HRegionServer会有多个HRegion和一个HLog。

**注:**HStore存储由两部分组成:MemStore和StoreFiles。 MemStore是Sorted Memory Buffer,用户 写入数据首先 会放在MemStore,当MemStore满了以后会Flush成一个 StoreFile(实际存储在HDHS上的是HFile),当StoreFile文件数量增长到一定阀值,就会触发Compact合并操作,并将多个StoreFile合并成一个StoreFile,合并过程中会进行版本合并和数据删除,因此可以看出HBase其实只有增加数据,所有的更新和删除操作都是在后续的compact过程中进行的,这使得用户的 读写操作*只要进入内存中就可以立即返回,保证了HBase I/O的高性能。

3、ROOT表和META表

HBase的用-ROOT-表来记录.META.的Region信息,就和.META.记录用户表的Region信息一模一样。-ROOT-只会有一个Region。
这么一来Client端就需要先去访问-ROOT-表。所以需要知道管理-ROOT-表的RegionServer的地址。这个地址被存在ZooKeeper中。默认的路径是: /hbase/root-region-server
-ROOT-行记录结构
在这里插入图片描述
.META.表结构
在这里插入图片描述
HBase的所有Region元数据被存储在.META.表中,随着Region的增多,.META.表中的数据也会增大,并分裂成多个新的Region。为了定位.META.表中各个Region的位置,把.META.表中所有Region的元数据保存在-ROOT-表中,最后由Zookeeper记录-ROOT-表的位置信息。所有客户端访问用户数据前,需要首先访问Zookeeper获得-ROOT-的位置,然后访问-ROOT-表获得.META.表的位置,最后根据.META.表中的信息确定用户数据存放的位置,如下图所示。
在这里插入图片描述
-ROOT-表永远不会被分割,它只有一个Region,这样可以保证最多只需要三次跳转就可以定位任意一个Region。为了加快访问速度,.META.表的所有Region全部保存在内存中。客户端会将查询过的位置信息缓存起来,且缓存不会主动失效。如果客户端根据缓存信息还访问不到数据,则询问相关.META.表的Region服务器,试图获取数据的位置,如果还是失败,则询问-ROOT-表相关的.META.表在哪里。最后,如果前面的信息全部失效,则通过ZooKeeper重新定位Region的信息。所以如果客户端上的缓存全部是失效,则需要进行6次网络来回,才能定位到正确的Region。
最后要提醒大家注意两件事情:

  1. 在整个路由过程中并没有涉及到MasterServer,也就是说HBase日常的数据操作并不需要MasterServer,不会造成MasterServer的负担。
  2. Client端并不会每次数据操作都做这整个路由过程,很多数据都会被Cache起来。至于如何Cache,则不在本文的讨论范围之内。

参考:
http://www.javashuo.com/article/p-pmaokxal-hh.html
https://blog.csdn.net/ldds_520/article/details/51674315
http://www.javashuo.com/article/p-qmoiefxq-hs.html

4、Region拆分原理

参考:https://blog.csdn.net/jiangmingzhi23/article/details/78609864
写请求被region server处理,region server会先将数据写到称为“memstore”的内存存储系统中。一旦memstore写满,就会被写到磁盘上的存储文件中,这个过程称为“memstore flush”。随着存储文件的积累,region server会将它们合并成一个更大文件。随着每一次写磁盘和合并操作完成,如果region拆分策略任务这个region需要被拆分,则会发起region拆分请求。由于hbase所有的数据文件是不可变的,进行region拆分时,一个region被拆分为两个region时,两个新创建的region不会重写所有的数据到新的文件,而是创建小的sym-link文件,sym-link文件也叫reference文件。reference文件根据分割点,指向父存储文件的顶部或底部。reference文件的使用和常规数据文件一样,但它只有一半的记录。如果reference文件不再有对父region的不可变数据文件的引用,则这个region不可以再拆分。这些reference文件通过合并逐渐清理,以便该region停止引用其父文件,并可以进一步拆分。
尽管region拆分是由region server本地进行决策的,但是拆分过程却有很多参与者。region server在region拆分前后都会通知Master进程更新.META.表, 以便client可以找到拆分出来的新的两个region。还需要重新排列目录结构以及HDFS中的数据文件。region拆分过程有多个任务完成。为了在发生错误时可以回滚,regionserver会在内存中保存执行状态日志。图1显示了Region Server执行拆分的步骤。每个步骤都标有其步骤编号。来自Region Servers或Master的操作显示为红色,而来自客户端的操作显示为绿色。
在这里插入图片描述

  • 1.region server自身决定region拆分,并准备发起拆分。作为第一步,它将在zookeeper的分区/hbase/region-in-transition/region-name下中创建一个znode。
  • 2.因为Master是父region-in-transition的znode节点的观察者,所以它知晓这个znode的建立。
  • 3.region server在HDFS的父region目录下创建一个名为“.splits”的子目录。
  • 4.region server关闭父region,强制cache刷盘并在本地数据结构中将这个region标记为offline。此时,父region的client请求将抛出NotServingRegionException,client将重试。
  • 5.region server为子region A和B分别在.splits目录下的region目录,并创建必要的数据结构。然后拆分存储文件,即先在父region中创建每个存储文件两个reference文件。这两个reference文件将指向父region文件。
  • 6.region server在HDFS中创建实际的region目录,并为每个子region更新相应的reference文件。
  • 7.region server发起Put请求到.META.表,并在.META.表中将父region设置为offline,表并添加有关子region的信息。此时,.META.表中不会有每个子region的单独的条目。client可以通过scan .META.表来知晓父region正在拆分,但是除非子region信息记录到.META.表,否则client是看不到子region的。如果前面的Put操作成功写入到.META.表,则标志父region拆分完成。如果region server在put操作前返回失败,则Master和打开这个region的region server将会清除region拆分的错误状态,如果.META.表成功更新,则region拆分状态会被Master向前翻。
  • 8.region server打开子region并行地接受写入请求。
  • 9.region server将子region A和B,以及它们的承载者信息分别添加到.META.表。之后,client就可以发现新的region,并访问之。client本地缓存.META.表信息,但是当它们访问region server或者.MET.表时,本地缓存失效,client从.META.表获取新的region信息。
  • 10.region server更新zookeeper的/hbase/region-in-transition/region-name节点中的region状态到SPLIT,以便master感知其状态变化。如果需要的话,负载器可以将子region自由地指定到其它region。
  • 11.region拆分完成后,其元数据和HDFS仍将包含对父region的引用。这些引用将在子region压缩重写数据文件时被删除。Master的GC任务会定期检查子region是否仍然引用父文件,如果没有,父region将被删除。

5、HBase在HDFS中存储

  • /hbase/.tmp:临时目录,当对表做创建和删除的时候,会将表move到该目录,然后进行操作
  • /hbase/data:核心目录,存储HBase表的数据 默认情况下,目录下有两个目录
  • /hbase/data/default: 在用户创建表的时候,没有指定namespace时,表就创建在此目录下
  • /hbase/data/hbase:系统内部创建的表,.META.表(region的详细信息)和namespace表(namespace信息)
  • /hbase/hbase.id:存储的是集群的唯一cluster id(uuid)
  • /hbase/hbase.version:集群的版本号
  • /hbase/oldWALs: 对应0.94.x版本中.oldlogs目录 当/hbase/WALs目录中的logs没有之后,会将这些logs移动到此目录下,HMaster会定期清理

参考:
http://dxer.github.io/2016/03/19/hbase%E7%AE%80%E4%BB%8B/

6、读写操作流程

写操作流程

(1) Client通过Zookeeper的调度,向RegionServer发出写数据请求,在Region中写数据。
(2) 数据被写入Region的MemStore,直到MemStore达到预设阈值。
(3) MemStore中的数据被Flush成一个StoreFile。
(4) 随着StoreFile文件的不断增多,当其数量增长到一定阈值后,触发Compact合并操作,将多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除。
(5) StoreFiles通过不断的Compact合并操作,逐步形成越来越大的StoreFile。
(6) 单个StoreFile大小超过一定阈值后,触发Split操作,把当前Region Split成2个新的Region。父Region会下线,新Split出的2个子Region会被HMaster分配到相应的RegionServer上,使得原先1个Region的压力得以分流到2个Region上。
可以看出HBase只有增添数据,所有的更新和删除操作都是在后续的Compact历程中举行的,使得用户的写操作只要进入内存就可以立刻返回,实现了HBase I/O的高性能。

读操作流程

(1) Client访问Zookeeper,查找-ROOT-表,获取.META.表信息。
(2) 从.META.表查找,获取存放目标数据的Region信息,从而找到对应的RegionServer。
(3) 通过RegionServer获取需要查找的数据。
(4) Regionserver的内存分为MemStore和BlockCache两部分,MemStore主要用于写数据,BlockCache主要用于读数据。读请求先到MemStore中查数据,查不到就到BlockCache中查,再查不到就会到StoreFile上读,并把读的结果放入BlockCache。
寻址过程:client–>Zookeeper–>-ROOT-表–>META表–>RegionServer–>Region–>client

7、Hbase的容错性

  • Master容错:Zookeeper重新选择一个新的Master
    (1)无Master过程中,数据读取仍照常进行;
    (2)无master过程中,region切分、负载均衡等无法进行;
  • RegionServer容错:定时向Zookeeper汇报心跳,如果一旦时间内未出现心跳,Master将该RegionServer上的Region重新分配到其他RegionServer上,失效服务器上“预写”日志由主服务器进行分割并派送给新的RegionServer
  • Zookeeper容错:Zookeeper是一个可靠地服务,一般配置3或5个Zookeeper实例