0
点赞
收藏
分享

微信扫一扫

HBase架构设计和工作原理

A邱凌 2022-03-11 阅读 68

1 HBase系统架构

Client职责

Zookeeper职责

HBase的元数据有两种:

  1. HBase中的表的相关信息,主要指表名、列簇的定义等,Master来管理
  2. 所有用户表的region的位置信息,这种数据也称之为元数据;存储在meta表,meta表由多个region组成,这些region也会分散到各个RegionServer去存储

Master职责

RegionServer职责

总结

如果master宕机了,那些事情不能做:

那些操作能继续做呢?

2 物理存储

2.1 整体物理结构

 要点知识:

2.2 StoreFile和HFile结构

StoreFile 以 HFile 格式保存在 HDFS 上,请看下图 HFile 的数据组织格式:

 首先 HFile 文件是不定长的,长度固定的只有其中的两块:Trailer 和 FileInfo。
正如图中所示:
Trailer中有指针指向其他数据块的起始点。
FileInfo中记录了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY,COMPARATOR, MAX_SEQ_ID_KEY等。

HFile的Data Block,Meta Block通常采用压缩方式存储,压缩之后可以大大减少网络IO和磁盘IO,随之而来的开销是需要花费cpu进行压缩和解压缩;目标HFile的压缩支持两种方式:Gzip,LZO

Data Index和Meta Index块记录了每个Data块和Meta块的起始点。

Data Block是HBase I/O的基本单元,为了提高效率,HRegionServer中有基于LRU的Block Cache机制。每个Data块的大小可以在创建一个Table的时候通过参数指定,大号的Block有利于顺序Scan,小号Block利于随机查询。 每个Data块除了开头的Magic以外就是一个个KeyValue对拼接而成,Magic内容就是一些随机数字,目的是防止数据损坏。

HFile里面的每个KeyValue对就是一个简单的byte数组。但是这个byte数组里面包含了很多项,并且有固定的结构。我们来看看里面的具体结构

 开始是两个固定长度的数值,分别表示Key的长度和Value的长度。紧接着是Key,开始是固定长度的数值,表示RowKey的长度,紧接着是 RowKey,然后是固定长度的数值,表示Family的长度,然后是Family,接着是Qualifier,然后是两个固定长度的数值,表示TimeStamp和KeyType(Put/Delete)。Value部分没有这么复杂的结构,就是纯粹的二进制数据了。

2.3 MemStore和StoreFile

一个HRegion由多个Store组成,每个Store包含一个列簇的所有数据。

Store包括位于内存的一个MemStore和位于硬盘的多个StoreFile组成

写操作先写入MemStore,当MemStore中的数据量达到某个阈值,HRegionServer启动flushcache进程进入StoreFile,每次写入形成单独一个HFile。

当总StoreFile大小超过一定阈值后,会把当前的Region分割成两个,并由HMaster分配给相应的RegionServer,实现负载均衡。

Client检索数据时,先在MemStore找,找不到再找StoreFile。

2.4 HLog & WAL

WAL意为Write Ahead Log,类似MySQL中的binlog,用来做灾难恢复之用,HLog记录数据的所有变更,一旦数据修改,就可以从Log中进行恢复。

HBase采用类LSM的架构体系,数据写入并没有直接写入数据 文件 ,而是会先写入缓存(MemStore),在满足一定条件下缓存数据再会异步flush到硬盘。为了防止数据写入缓存之后不会因为RegionServer进程发生异常导致数据丢失,在写入缓存之前会首先将数据 顺序写入HLog中。如果不幸一旦发生RegionServer宕机或者其它异常,这种设计可以从HLog中进行日志回话进行数据补救,保证数据不丢失。HBase故障恢复的最大看点就在于如何通过HLog回话补救丢失的数据。

WAL(Write-Ahead Logging)是一种高效的日志算法,几乎是所有非内存数据库提升写性能的不二法门,基本原理是在数据写入之前首先顺序写入日志,然后再写入缓存,等到缓存写满之后统一落盘。之所以能够提升写性能,是因为WAL将一次随机写转化为了一次顺序写加一次内存写。提升写性能的同时,WAL可以保证数据的可靠性,即在任何情况下数据不丢失。假如一次写入完成之后发生了宕机,即使所有缓存中的数据丢失,也可以通过恢复日志还原出丢失的数据。

每个Region Server维护一个HLog,而不是每个Region一个。这样不同region(来自不同table)的日志会混在一起,这样做的目的是不断追加单个文件相对于同时写多个文件而言,可以减少磁盘寻址次数,因此可以提高对table的写性能。带来的麻烦是,如果一台region server下线,为了恢复其上的Region,需要将RegionServer上的log进行拆分,然后分发到其它RegionServer上进行恢复。

3 寻址机制

3.1 老的Region寻址机制

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

从上面的路径我们可以看出,用户需要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-表了。

3.2 新的Region寻址方式

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

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

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

4 读写过程

4.1 读请求过程

 详细步骤:

HBase的单个key的扫描速度和Table的数据规模没有关系

4.2 写请求过程

 写入数据的前提:需要保证该张表是按照RowKey有序的

Hbase在做数据插入操作时,首先要找到RowKey所对应的的Region,怎么找到的?其实这个简单,因为.META.表存储了每张表每个Region的起始RowKey了。

4.3 Region的Split和Compact

数据在更新时首先写入HLog(WAL Log),再写入内存(MemStore)中,MemStore(ConcurrentSkipListMap,优点就是增删改查key-value效率都很高)中的数据是排序的当MemStore累计到一定阈值(默认是128M,局部控制)时,就会创建一个新的MemStore,并且将老的MemStore添加到flush队列,由单独的线程flush到磁盘上,成为一个StoreFile。与此同时,系统会在ZooKeeper中记录一个redo point,表示这个时刻之前的变更已经持久化了。当系统出现意外时,可能导致内存(MemStore)中的数据丢失,此时使用HLog(WAL Log)来恢复checkpoint之后的数据。

MemStore执行flush操作的触发条件:

HBase的三种默认的Split策略:

StoreFile是只读的,一旦创建后就不可以再修改。因此HBase的更新/修改其实是不断追加的操作。当一个Store中的StoreFile达到一定的阈值后,就会进行一次合并(minor_compact, major_compact),将对同一个key的修改合并到一起,形成一个大的StoreFile,当StoreFile的大小达到一定阈值后,又会对StoreFile进行split,等分为两个StoreFile。由于对表的更新是不断追加的,compact时,需要访问Store中全部的StoreFile和MemStore,将它们按RowKey进行合并,由于StoreFile和MemStore都是经过排序的,并且StoreFile带有内存中索引,合并的过程还是比较快。

Minor_Compact 和 Major_Compact 的区别:

Client写入 -> 存入MemStore,一直到MemStore写满 -> Flush成一个StoreFile,直至增长到一定阈值 -> 触发Compact合并操作 -> 多个StoreFile合并成一个StoreFile,同时进行版本合并和数据删除 -> 当StoreFile Copact后,逐步形成越来越大的StoreFile -> 单个StoreFile大小超过一定阈值后,触发Split操作,把当前Region Split成2个Region,Region会下线,新Split出的2个孩子Region会被HMaster分配到相应的HRegionServer上,使原先1个Region的压力得以分流到2个Region上。由此过程可知,HBase只是增加数据,所有的更新和删除操作,都晨Compact阶段做的,所以,用户写操作只需要进入到内存即可立即返回,从而保证I/O高性能。

写入数据的过程补充:

举报

相关推荐

0 条评论