Hbase体系结构

139 阅读9分钟

前序

HBase体系结构借鉴了BigTable论文,是典型的Master-Slave模型
hbase核心架构由五部分组成:分别是Hbase client、HMaster、Region Server、Zookeeper以及HDFS组成。

系统中有一个管理集群的Master节点以及大量实例服务用户的RegionServer节点,除此之外,Hbase中所有数据最终都存储在HDFS系统中,这与BigTable实际数据存储在GFS中相对应;系统中还有一个Zookeeper集群,协助Master对集群进行管理。

其中

  • Zookeeper:存放整个集群的元数据以及状态信息,实现HMaster主从节点的failover,保存系统正常RegionServer集合,保存系统hbase:meta表所在RegionServer地址。
  • HMaster:主要负责DDL(表创建、删除)操作,管理HRegionServer,实现其负载均衡,管理和分配Region等
  • HRegionServer:主要用来响应用户的IO请求,存放和管理本地HRegion

Zookeeper

Zookeeper为Hbae集群提供协调服务,它管理着HMaster和HRegionServer的状态(available和alive等),并且会在他们宕机时通知HMaster,从而HMaster可以实现HMaster之前的failover,或对宕机的HRegionServer中的HRegion集合进行修复(将他们分配给其他的HRegionServer)。Zookeeper集群本身使用一致性协议(PAXOS协议)保证每个节点状态的一致性。

HMaster

HMaster没有单点故障问题,可以启动多个HMaster,通过ZK的Master Election机制保证同时只有一个HMaster处于Active状态,其他的HMaster则处于备份状态。一般情况下会启动两个HMaster,非Active的HMaster会定期和Active HMaster通信以获取最新状态,从而保证它时实时更新的,因而如果启动了多个HMaster反而增加了Active HMaster的负担。前文已经介绍过了HMaster的主要用于HRegion的分配和管理,DDL(Data Definition Lanaguage),即Table的新建、删除、修改等实现,主要有两方面的职责:

  • 管理HRegionServer:启动时HRegion的分配,以及在负载均衡和修复时HRegion的重新分配,监控集群中所有HRegionServer的状态(通过Heartbeat和监听Zookeeper的状态)。
  • 元数据管理职能:创建、删除、修改表的定义

HRegionServer/HRegion

Hbase使用RowKey将表水平切割成多个HRegion,从HMaster角度上看,每个HRegion都记录了它的StartKey和EndKey(第一个HRegion的StartKey为空,最后一个HRegion的EndKey为空),由于RowKey是排序的,因而Client可以通过HMaster快速定位每个RowKey在哪个HRegion中,HRegion由HMaster分配到相应的HRegionServer中,然后由HRegionServer负责HRegion的启动和管理,Client通信、负责数据的读(使用HDFS),每个HRegionServer大约管理1000个左右的HRegion。

组件如何协同工作

Zookeeper用于协调分布式系统成员的共享状态信息。RegionServer与Active HMaster通过会话与Zookeeper进行连接,Zookeeper通过心跳为活跃会话维护一个临时节点。

另外,HMaster通过监听Zookeeper中的临时节点(默认:/hbase/rs/*)来监听HRegionServer的加入和宕机。在第一个HMaster连接到Zookeeper时会创建临时节点(默认:/hbase/master)来表示Active的HMaster,其后来加进来的HMaster则监听该临时节点,如果当前Activity的HMaster宕机,则该节点消失,其他HMaster得到通知,会将其自身转换为Activity的HMaster,在变为Active的HMaster之前,它会在/hbase/back-masters创建自己的临时节点。

Hbase首次读写

Hbase中有一个特殊的目录表(META表),保存了集群中所有Region的位置。META表的位置存储在Zookeeper中

如下是客户端第一次读写时发生的情况:

  • 客户端从Zookeeper中获取负责管理META表的RegionServer。
  • 客户端查询META服务来获取我们要访问的RowKey所对应的RegionServer。客户端会将该信息与META表位置进行缓存
  • 客户端查询RowKey所在的RegionServer并从中获取行。

为了以后的读请求,客户端会缓存检索的META表位置以及之前读取的RowKey。在后面,我们一般不再需要查询META表,除非由于Region迁移导致缓存失效,然后会重新查询并更新缓存

Hbase:meta表

META表也是一个HBase表,保存了系统中所有的Region信息。它的RowKey就是RegionName,Value就是RegionServer位置。

META表结构如下所示:

RowKey:TableName(业务表名),StartKey(该Region起始rowKey),Timestamp该Region创建时间戳,也是RegionId),EncodedName(上面3个字段的MD5 Hex值)

Value:RegionServer

RegionServer组成

Region Server运行在HDFS DataNode上,由以下几个组件组成:

  • WAL:Write Ahead Log 是分布式文件系统上的一个文件,用于存储新的还未持久化存储的数据,它被用来做故障恢复。
  • BlockCache:这是读缓存,在内存中存储了最常访问的块数据,内存不足时删除最近最少使用的数据。
  • MemStore:这是写缓存,在内存中存储了新的还未持久化到硬盘的数据。当被写入硬盘时,数据会先进行排序,注意每个Region的每个列簇都会有一个MemStore。
  • HFile:将数据以有序的KeyValue形式存储在磁盘上。

Hbase写数据步骤

当客户端发出put请求时,第一步将数据写入预写日志WAL中:

  • 新数据会被追加到WAL文件尾部。
  • WAL用于在故障恢复还未被持久化的数据

数据被写入WAL后,会被加入到MemStore即写缓存。然后服务端就会像客户端返回ack表示写数据完成。

MemStore

MemStore在内存中缓存Hbase的数据更新,以有序KeyValues的形式,这和HFile中的存储形式一样,每个列簇都有一个MemStore,所有的更新都以列簇为单位进行排序

Region Flush

每一次Put/Delete请求都是先写入MemStore中,当MemStore满后会Flush成一个新的StoreFile(底层实现是HFile),即一个HStore(Column Family)可以有0个或多个StoreFile(HFile)。满足一定条件下的可以触发MemStore的Flush动作,需要注意的是MemStore的最小Flusher单元是HRegion而不是MemStore。

在MemStore Flusher过程中,还会在尾部追加一些meta数据,其中包括Flush时最大的WAL sequence值,以告诉HBase这个StoreFile写入的最新数据的序列,那么在故障恢复时就知道从哪里开始。在HRegion启动时,这个sequence会被读取,并取最大的作为下一次更新时的起始sequence。

HFile

数据以有序的KeyValue的形式存储在HFile中。当MemStore累积足够多的数据时,就会将整个有序的KeyValue数据集顺序写入到一个新的HFile中,顺序写入的方式非常快,因为它不需要移动磁盘磁头。

HFile索引

HFile包含多层索引,从而使得HBase无需读取整个文件即可查找数据,多级索引类似一个B+树:

  • 键值对以升序存储
  • Rowkey对应索引指向64KB大小的数据块
  • 每个数据块都有自己的叶子索引
  • 每个数据块的最后一个键放在中间索引中
  • 根索引指向中间索引

三种索引类型: (1)Root Index:根索引 (2)Intermedidate Index: 中间索引 (3) Leaf Index: 叶子索引

trailer指向原信息数据块,它是在数据持久化为HFile时被写在HFile文件尾部。trailer还包含例如布隆过滤器和时间范围等信息。布隆过滤器用来跳过那些不包含指定rowkey的文件,时间范围信息则是根据时间来过滤,跳过那些不在请求的时间范围之内的文件。

上面讲到的索引,在HFile被打开时会被载入内存,这样数据查询只要一次磁盘查询

读取合并

我们已经看到,对于每行的keyValue可以存储多个位置,已经持久化的行单元位于HFile中,最近最新的行单元位于MemStore中,而最近读取的单元位于BlockCache中。因此,当我们读取一行时,系统如何获取对应的KeyValuefan返回?读取需要通过以下步骤来合并BlockCache、MemStore以及HFile中的键值:

  • 首选,扫描程序在BlockCache(读缓存)中查找行单元。最近读取的键值存储在这里,并且当内存不足时需要删除最近最少使用的数据
  • 接下来,扫描程序在MemStore(写缓存)中查找,这里包含最近的写入
  • 如果扫描程序在BlockCache中没有找到对应的数据,那么Hbase将使用HFile索引树和布隆过滤器将对应的HFile加载到内存中,这里可能包含目标行单元

如前所述,每个HStore可能有多个HFile,这意味着对于读而言,可能必须查找对多个文件,不管磁盘IO或者是多路归并,都会影响读性能;所以Hbase会尽可能的通过压缩(Compaction)来建山文件树或通过布隆过滤器过滤掉一些不符合条件的HFIle。

HBase压缩

HBase Minor Compaction

HBase会自动合并一些小的HFile,重写成少量更大的HFile。这个过程被称为minor compaction。它使用归并排序算法,将小文件合并成大文件,有效减少HFile的数量

HBase Major Compaction

Major 压缩会将一个Region的一个Store中的所有HFile合并为每一个列簇的一个HFile,在此过程会删除已删除或已过期的单元,这样可以提高读性能,但是由于Major压缩会重写所有文件,因此这个过程会有大声大量磁盘I/O和网络开销。Major压缩还可以使由于服务器故障或负载均衡而变成非本地数据重新回到RegionServer本地数据(Region 及其HFIle在同一节点)

Region拆分

我们再来回顾下region的概念:

  • 一个Table可以水平拆分为一个或者多个Region。每个region包含了连续的、有序的一组KeyValue,以startKey和endKey为边界
  • 每个Region的默认大小为1GB
  • Region里的数据由RegionServer负责读写,和Client交互
  • 每个Region Server大约可以管理1000个Region(来自同一张表或多张表)

最初,每个表只有一个Region。当Region过大时,会裂开为两个子Region(代表原始Region的一半)可以在同一个RegionServer上并行打开,拆开时会报告给HMaster。出于负载均衡考虑,HMaster可能会将Region迁移到其他RegionServer上。

Region负载均衡

拆开最初发生在同一个RegionServer上,但是出于负载均衡的考虑,HMaster可能会将新的Region迁移到其他RegionServer。这会导致新的RegionServer从远程HDFS节点上访问数据,需要等到Major压缩时才将数据文件移动到新的RegionServer的本地节点上。Hbase数据在写入时是在本地节点的,但是在迁移Region时(用于负载均衡或故障恢复),会丢失本地性。