原创

HBase学习—HBase 相关原理

系统架构

架构   

从HBase的架构图上可以看出,HBase中的组件包括Client、Zookeeper、HMaster、HRegionServer、HRegion、BlockCache、Store、MemStore、StoreFile、HFile、HLog等,接下来介绍他们的作用。

相关组件

Client

HBase客户端提供了shell命令行接口,原生java api,rest风格接口,以及MapReduce API,HBase客户端支持所有常见的DML操作以及DDL操作,既数据的增删改查以及表的维护。

其中REST api主要针对非java的调用,而MapReduce主要针对的是HBase的批量数据导入导出。

在客户端访问数据之前,先要通过查找meta表,定位region所在的RegionServer然后再开始访问数据,同时客户端会将meta数据缓存在客户端本地,这样做解决的hbase:meta的热点问题,并且提高后续访问的效率,若在后边发生了region数据的移动,那么将会重新获取心得meta表数据。

ZooKeeper

  1. 为HBase Master提供选举机制:HBase高可用依赖zk完成,通常情况下Master只有一个,当Master下线时通过zk完成切换新得Master。
  2. 管理HBase的核心元数据:例如当前meta表的region所在的region server。管理集群中所有正在运行中的region Server集合
  3. 维护region server的状态:当有region server下线时,zk能够通过心跳及时的检测到,并通知Master进行处理。
  4. 实现分布式表锁:HBase作为分布式数据库,对于修改表结构等操作提供了表锁,也就是说在进行表的alter操作时,进行表锁定,而这一实现的基础就是依靠zk。

具体HBase在zk中的结构请参考下图:

IMAGE

Master

  1. 管理region,为region server分配region,负载均衡,宕机恢复以及宕机后region迁移等工作
  2. 处理客户端管理请求,比如相关表创建、删除、切分操作,以及compact等
  3. 清理过期文件,比如过期的HLog以及过期的HFile等文件。

RegionServer

RegionServer主要负责集群响应客户端请求,主要有几部分组成,WAL(HLog)、BlockCache、以及多个Region。

WAL

WAL(write ahead log)其实也就是HLog,一个普通的Hadoop Sequence File。

每个Region Server包含一个或者多个HLog(1.1版本后支持开启多HLog),Hlog是多个region共享的,结构如下图。

他的存在主要有两个作用,一是为了保证数据的可靠性,HBase数据的写入并非直接写如到磁盘,而是在缓存中,当满足一定的条件时才会刷写到磁盘,这也可能会导致RegionServer宕机,数据未及时写到磁盘产生丢失,所以规定,在写入换存前先写入HLog,在丢失时直接从HLog中恢复即可。二是为了在Hbase集群的主从复制时需要用到,从节点接收到Hlog将其重播就能够同步主集群的数据了。

Hlog并非永久存储在HDFS中的,当他的对应任务完成后将会被标记为无效,然后再由单独的线程进行清理。

BlockCache

blockcache作为hbase的读缓存,他能够将hbase磁盘中的数据缓存在内存中,在下一次读取时不在需要进行磁盘io,能够大量减少查询所需时间。

BlockCache由小的block组成,每一个小的block为64k,由物理上多个KV数据组成。当前HBase中的block cache有两种实现方式,LRUBlockCache和BucketCache,前者实现较为简单,后者对GC的优化较为明显。

Region

region是数据表的一个分片,一个表可以存在多个region,默认在表创建时只有一个region,当数据到达一定阀值时HBase会自动将region分裂为两个,关于region的分裂HBase在2.0版本之后与之前都提供了不同的分裂策略。

region是集群负载均衡的基本单位。通常一张表的region会均匀的分布在集群中的几台RegionServer上。一个regionServer管理着多个region,这些region通常来自不同的表。

为了避免表在写入时的region热点问题,通常在定义表时使用预分区,先将region根据rowkey拆分使其均匀的分布在集群中,以避免此问题的发生,关于表的预分区后边的文章再详细介绍。

Store

了解完region进入region内部,他是由一个或者多个Store构成,参看上边提供的HBase系统架构图。Store的个数取决于表中列族的个数,也就是说一张表有多少个列族就有多少个Store。

IMAGE

当前这张表的结构就决定了他的每一个表分片region中存在三个Store,每个列族中的数据都集中存放形成一个Store,因此建议再列族划分时,将具有相同IO操作的数据放在一个列族中,这样在读取数据时能够有效的避免磁盘寻址浪费的时间。

MemStore

Store的构成是由一个MemStore和多个StoreFile,先说MemStore,它是Hbase的缓存级别的存储组件,同时他也负责在数据刷写到hdfs前对HFile进行排序(HFile按照key进行的排序),保证了所有HFile在存储时已经是有序的了。

MemStore的作用不仅如此,他还会对HFile进行一些优化,例如数据的保留版本为1,在刚写入后进行了频繁的更新,那么MemStore就会根据版本进行删除,只保留一个最新的版本,这样在刷写HFile时能够避免无效的数据写入。

每个Store中只有一个MemStore,写操作时在成功写入到WAL后才会将数据写入到MemStore中,保证了数据的可靠,当MemStore达到了一定的阀值(默认128m)时,将其刷写到磁盘,形成StoreFile(HFile)永久保存。

StoreFile(HFile)

关于StoreFile其就是对HFile进行了简单的包装,我们可以认为在MemStore达到了一定的阀值时刷写的就是HFile,一个Store中包含了多个HFile,并且HFile的数量随着数据的增大会越来越多,HBase对HFile的数量也有限制,在达到一定数量后就会进行所谓的compact,将其合并为一个大文件。具体HBase的compact再详细分析。

HFile是有序的,他是根据他的key进行的排序,这样做的目的是为了在Hbase中根据有序的数据文件建立索引,提升HBase整体的查询效率。关于他是如何保证有序的,这就是刚刚提到的MemStore提供的功能。

HDFS

HBase底层依赖于HDFS存储实际数据,包括用户数据文件、Hlog文件等最终都会存储到HDFS上。也正是如此HBase不用考虑数据的可靠性,应为HDFS默认的三副本策略就已经能够满足了。

HBase内部封装了DFSClient用来与Hdfs交互,负责与HDFS进行数据存储查询的Io操作。

Region 寻址机制

  既然读写都在 RegionServer 上发生,我们前面有讲到,每个 RegionSever 为一定数量的 Region 服务,那么 Client 要对某一行数据做读写的时候如何能知道具体要去访问哪个 RegionServer 呢?那就是接下来我们要讨论的问题

老的 Region 寻址方式

  在 HBase-0.96 版本以前,HBase 有两个特殊的表,分别是-ROOT-表和.META.表,其中-ROOT的位置存储在 ZooKeeper 中,-ROOT-本身存储了.META. Table 的 RegionInfo 信息,并且-ROOT不会分裂,只有一个 Region。而.META.表可以被切分成多个 Region。读取的流程如下图所示:

详细步骤:

第 1 步:Client 请求 ZooKeeper 获得-ROOT-所在的 RegionServer 地址

第 2 步:Client 请求-ROOT-所在的 RS 地址,获取.META.表的地址,Client 会将-ROOT-的相关 信息 cache 下来,以便下一次快速访问

第 3 步:Client 请求.META.表的 RegionServer 地址,获取访问数据所在 RegionServer 的地址, Client 会将.META.的相关信息 cache 下来,以便下一次快速访问

第 4 步:Client 请求访问数据所在 RegionServer 的地址,获取对应的数据

  从上面的路径我们可以看出,用户需要 3 次请求才能直到用户 Table 真正的位置,这在一定 程序带来了性能的下降。在 0.96 之前使用 3 层设计的主要原因是考虑到元数据可能需要很 大。但是真正集群运行,元数据的大小其实很容易计算出来。在 BigTable 的论文中,每行 METADATA 数据存储大小为 1KB 左右,如果按照一个 Region 为 128M 的计算,3 层设计可以支持的 Region 个数为 2^34 个,采用 2 层设计可以支持 2^17(131072)。那么 2 层设计的情 况下一个集群可以存储 4P 的数据。这仅仅是一个 Region 只有 128M 的情况下。如果是 10G 呢? 因此,通过计算,其实 2 层设计就可以满足集群的需求。因此在 0.96 版本以后就去掉 了-ROOT-表了。

新的 Region 寻址方式

如上面的计算,2 层结构其实完全能满足业务的需求,因此 0.96 版本以后将-ROOT-表去掉了。 如下图所示:

访问路径变成了 3 步:

第 1 步:Client 请求 ZooKeeper 获取.META.所在的 RegionServer 的地址。

第 2 步:Client 请求.META.所在的 RegionServer 获取访问数据所在的 RegionServer 地址,Client 会将.META.的相关信息 cache 下来,以便下一次快速访问。

第 3 步:Client 请求数据所在的 RegionServer,获取所需要的数据。

总结去掉-ROOT-的原因

  1. 提高性能

  2. 层结构已经足以满足集群的需求

这里还有一个问题需要说明,那就是 Client 会缓存hbase:meta的数据,用来加快访问,既然有缓存,那它什么时候更新?如果hbase:meta更新了,比如 Region1 不在 RerverServer2 上了,被转移到了 RerverServer3 上。Client 的缓存没有更新会有什么情况?

其实,Client 的元数据缓存不更新,当hbase:meta的数据发生更新。如上面的例子,由于 Region1 的位置发生了变化,Client 再次根据缓存去访问的时候,会出现错误,当出现异常达到重试次数后就会去hbase:meta所在的 RegionServer 获取最新的数据,如果hbase:meta所在的 RegionServer也变了,Client 就会去 ZooKeeper 上获取hbase:meta所在的 RegionServer 的最新地址,zk上一定保存的是最新的region列表。

读写流程

写流程

  1. 客户端向zookeeper请求 hbase:meta 表region所在region server位置。

  2. 客户端向该RegionServer请求查找要查询的数据所对应的所有region所在的RegionServer。

  3. 找到RegionServer后向其发送写⼊请求,RegionServer将要写⼊的数据分别写⼊到WAL和memstore各⼀份。

  4. 在memstore达到阀值后再把数据flush到磁盘,⽣成storefile⽂件

  5. 删除hlog中的历史数据。

读流程

  1. 客户端向zookeeper请求 hbase:meta 表region所在region server位置。

  2. 客户端向该RegionServer请求查找要查询的数据所对应的所有region所在的RegionServer。

  3. 找到RegionServer后开始请求,先从memstore查找数据,如果没有,再从BlockCache上读取

  4. 若BlockCache也没有找到再去StoreFile中查找,此时会将结果写⼊BlockCache再返回结果

RegionServer工作机制

Region 分配

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

RegionServer 上线

Master 使用 zookeeper 来跟踪 RegionServer 状态。当某个 RegionServer 启动时,会首先在 ZooKeeper 上的 server 目录下建立代表自己的 znode。由于 Master 订阅了 server 目录上的变 更消息,当 server 目录下的文件出现新增或删除操作时,Master 可以得到来自 ZooKeeper 的实时通知。因此一旦 RegionServer 上线,Master 能马上得到消息。

RegionServer 下线

当 RegionServer 下线时,它和 zookeeper 的会话断开,ZooKeeper 而自动释放代表这台 server 的文件上的独占锁。Master 就可以确定:

  1. RegionServer 和 ZooKeeper 之间的网络断开了。

  2. RegionServer 挂了。

无论哪种情况,RegionServer都无法继续为它的Region提供服务了,此时Master会删除server 目录下代表这台 RegionServer 的 znode 数据,并将这台 RegionServer 的 Region 分配给其它还 活着的机器。

Master 工作机制

Master 上线

Master 启动进行以下步骤:

  1. 从 ZooKeeper 上获取唯一一个代表 Active Master 的锁,用来阻止其它 Master 成为 Master。

  2. 扫描 ZooKeeper 上的 server 父节点,获得当前可用的 RegionServer 列表。

  3. 和每个 RegionServer 通信,获得当前已分配的 Region 和 RegionServer 的对应关系。

  4. 扫描hbase:meta Region 的集合,计算得到当前还未分配的 Region,将他们放入待分配 Region 列表。

Master 下线

由于 Master 只维护表和 Region 的元数据,而不参与表数据 IO 的过程,Master 下线仅 导致所有元数据的修改被冻结(无法创建删除表,无法修改表的 schema,无法进行 Region 的负载均衡,无法处理 Region 上下线,无法进行 Region 的合并,唯一例外的是 Region 的 split 可以正常进行,因为只有 RegionServer 参与),表的数据读写还可以正常进行。因此 Master 下线短时间内对整个 hbase 集群没有影响。

从上线过程可以看到,Master 保存的信息全是可以冗余信息(都可以从系统其它地方 收集到或者计算出来)

因此,一般 HBase 集群中总是有一个 Master 在提供服务,还有一个以上的 Master 在等待时机抢占它的位置吗(通过zk来实现的,其实就是抢占他的节点)。

sev7e0
Write by sev7e0
end
本文目录