HBase 软件架构

By timebusker on January 13, 2019

架构图

HBase体系结构借鉴了BigTable论文,是典型的Master-Slave模型。系统中有一个管理集群的Master节点以及大量实际服务用户读写的RegionServer节点。 除此之外,HBase中所有数据最终都存储在HDFS系统中,这与BigTable实际数据存储在GFS中相对应;系统中还有一个ZooKeeper节点,协助Master对集群进行管理。

  • 0.9x.x版本

架构图

  • 1.4.x及2.x版本

架构图

每一个 RegionServer 就只有一个 HLog,而不是一个 Region 有一个 HLog

Client

HBase客户端(Client)提供了Shell命令行接口、原生Java API编程接口、Thrift/REST API编程接口以及MapReduce编程接口。 HBase客户端支持所有常见的DML操作以及DDL操作,即数据的增删改查和表的日常维护等。 其中Thrift/REST API主要用于支持非Java的上层业务需求,MapReduce接口主要用于批量数据导入以及批量数据读取。

  • 0.98.x及更低版本 HBase有两张特殊表:
    -ROOT-:记录了.META.表的Region信息,-ROOT-只有一个Region,无论如何不会分裂

.META.:记录了用户所有表拆分出来的的Region映射信息,.META.可以有多个Regoin

Client访问用户数据前需要首先访问ZooKeeper,找到-ROOT-表的Region所在的位置,然后访问-ROOT-表, 接着访问.META.表,最后才能找到用户数据的位置去访问,中间需要多次网络操作,不过client端会做cache缓存。

  • 1.4.x及更高版本 HBase客户端访问数据行之前,首先需要通过元数据表定位目标数据所在RegionServer,之后才会发送请求到该RegionServer。 同时这些元数据会被缓存在客户端本地,以方便之后的请求访问。如果集群RegionServer发生宕机或者执行了负载均衡等, 从而导致数据分片发生迁移,客户端需要重新请求最新的元数据并缓存在本地。
Zookeeper

主要用于协调管理分布式应用程序。在HBase系统中,ZooKeeper扮演着非常重要的角色。

  • 实现Master高可用:通常情况下系统中只有一个Master工作,一旦Active Master由于异常宕机,ZooKeeper会检测到该宕机事件,并通过一定机制选举出新的Master,保证系统正常运转。

  • 管理系统核心元数据:比如,管理当前系统中正常工作的RegionServer集合,保存系统元数据表hbase:meta所在的RegionServer地址等。

  • 参与RegionServer宕机恢复:ZooKeeper通过心跳可以感知到RegionServer是否宕机,并在宕机后通知Master进行宕机处理。

  • 实现分布式表锁:HBase中对一张表进行各种管理操作(比如alter操作)需要先加表锁,防止其他用户对同一张表进行管理操作,造成表状态不一致。 和其他RDBMS表不同,HBase中的表通常都是分布式存储,ZooKeeper可以通过特定机制实现分布式表锁。

Master

Master主要负责HBase系统的各种管理工作:

  • 处理用户的各种管理请求,包括建表、修改表、权限操作、切分表、合并数据分片以及Compaction等。

  • 管理集群中所有RegionServer,包括RegionServer中Region的负载均衡、RegionServer的宕机恢复以及Region的迁移等。

  • 清理过期日志以及文件,Master会每隔一段时间检查HDFS中HLog是否过期、HFile是否已经被删除,并在过期之后将其删除。

HRegionServer

RegionServer主要用来响应用户的IO请求,是HBase中最核心的模块,由WAL(HLog)、BlockCache以及多个Region构成。

  • WAL(HLog):HLog在HBase中有两个核心作用——其一,用于实现数据的高可靠性,HBase数据随机写入时,并非直接写入HFile数据文件,而是先写入缓存,再异步刷新落盘。 为了防止缓存数据丢失,数据写入缓存之前需要首先顺序写入HLog,这样,即使缓存数据丢失,仍然可以通过HLog日志恢复;其二,用于实现HBase集群间主从复制, 通过回放主集群推送过来的HLog日志实现主从复制。

  • BlockCache:HBase系统中的读缓存。客户端从磁盘读取数据之后通常会将数据缓存到系统内存中,后续访问同一行数据可以直接从内存中获取而不需要访问磁盘。 对于带有大量热点读的业务请求来说,缓存机制会带来极大的性能提升。

BlockCache缓存对象是一系列Block块,一个Block默认为64K,由物理上相邻的多个KV数据组成。BlockCache同时利用了空间局部性时间局部性原理, 前者表示最近将读取的KV数据很可能与当前读取到的KV数据在地址上是邻近的,缓存单位是Block(块)而不是单个KV就可以实现空间局部性; 后者表示一个KV数据正在被访问,那么近期它还可能再次被访问。当前BlockCache主要有两种实现—LRUBlockCacheBucketCache, 前者实现相对简单,而后者在GC优化方面有明显的提升。

  • Region:数据表的一个分片,当数据表大小超过一定阈值就会“水平切分”,分裂为两个RegionRegion是集群负载均衡的基本单位。 通常一张表的Region会分布在整个集群的多台RegionServer上,一个RegionServer上会管理多个Region,当然,这些Region一般来自不同的数据表。

一个Region由一个或者多个Store构成,Store的个数取决于表中列簇(column family)的个数,多少个列簇就有多少个Store。 HBase中,每个列簇的数据都集中存放在一起形成一个存储单元Store,因此建议将具有相同IO特性的数据设置在同一个列簇中。

每个Store由一个MemStore和一个或多个HFile组成。MemStore称为写缓存,用户写入数据时首先会写到MemStore, 当MemStore写满之后(缓存数据超过阈值,默认128M)系统会异步地将数据flush成一个HFile文件。显然,随着数据不断写入, HFile文件会越来越多,当HFile文件数超过一定阈值之后系统将会执行Compact操作,将这些小文件通过一定策略合并成一个或多个大文件。

HRegionServer主要负责响应用户I/O请求,向HDFS文件系统中读写数据,是HBase中最核心的模块。 架构图

HRegionServer内部管理了一系列HRegion对象,每个HRegion对应了Table中的一个 Region,HRegion中由多个HStore组成。 每个HStore对应了Table中的一个Column Family的存储, 可以看出每个Column Family其实就是一个集中的存储单元,因此最好将具备共同IO特性的column放在一个Column Family中,这样最高效。

HStore存储是HBase存储的核心了,其中由两部分组成,一部分是MemStore,一部分是StoreFiles。 MemStore是Sorted Memory Buffer, 用户写入的数据首先会放入MemStore,当MemStore满了以后会Flush成一个StoreFile(底层实现是HFile), 当StoreFile文件数量增长到一定阈值, 会触发Compact合并操作,将多个StoreFiles合并成一个StoreFile,合并过程中会进 行版本合并和数据删除,因此可以看出HBase其实只有增加数据, 所有的更新和删除操作都是在后续的compact过程中进行的,这使得用户的写操作只要 进入内存中就可以立即返回,保证了HBase I/O的高性能。 当StoreFiles Compact后,会逐步形成越来越大的StoreFile,当单个StoreFile大小超过一定阈值后,会触发Split操作,同时把当前 Region Split成2个Region, 父Region会下线,新Split出的2个孩子Region会被HMaster分配到相应的HRegionServer 上,使得原先1个Region的压力得以分流到2个Region上。 下图描述了Compaction和Split的过程: 架构图

  • HLog(预写日志) 在理解了上述HStore的基本原理后,还必须了解一下HLog的功能,因为上述的HStore在系统正常工作的前提下是没有问题的, 但是在分布式系统环境中,无法避免系统出错或者宕机,因此一旦HRegionServer意外退出,MemStore中的内存数据将会丢失, 这就需要引入HLog了。每个HRegionServer中都有一个HLog对象,HLog是一个实现Write Ahead Log的类,在每次用户操作写入MemStore的同时, 也会写一份数据到HLog文件中(HLog文件格式见后续),HLog文件定期会滚动出新的,并删除旧的文件(已持久化到StoreFile中的数据)。 当HRegionServer意外终止后,HMaster会通过Zookeeper感知到,HMaster首先会处理遗留的HLog文件,将其中不同RegionLog数据进行拆分, 分别放到相应region的目录下,然后再将失效的region重新分配,领取到这些regionHRegionServerLoad Region的过程中,会发现有历史HLog需要处理, 因此会Replay HLog中的数据到MemStore中,然后flushStoreFiles,完成数据恢复。

HLog文件就是一个普通的Hadoop Sequence FileSequence FilevaluekeyHLogKey对象,其中记录了写入数据的归属信息,除了tableregion名字外, 还同时包括sequence numbertimestamptimestamp是写入时间,sequence number的起始值为0,或者是最近一次存入文件系统中的sequence numberSequence FilevalueHBaseKeyValue对象,即对应HFile中的KeyValue

  • HRegion:当表的大小超过预设值的时候,HRegion会自动按行对rowkey对应的数据进行拆分

  • HLog:HLog 是一个实现了WAL(Write Ahead Log)的预写日志类,其内部是一个简单的顺序日志。每个HRegionServer上都有一个HLog,类似于mysql中的binlog。该日志只会追加内容,用于记录操作日志,能用来做灾难恢复(数据回滚到初始状态)。

  • HStore:它是HBase的存储核心,存储的最小单元,由MemStore或0至多个StoreFile组成。

  • MemStore:它是内存缓冲区,用户写入的数据首先会放入MemStore,当MemStore满了以后会Flush成一个StoreFile(底层实现是HFile,是HBase存储数据的文件组织形式),当StoreFile的文件数量增长到一定阈值后,会触发Compact合并操作,将多个StoreFiles合并成一个StoreFile,合并过程中会进行版本合并和数据删除操作。

因此,可以看出HBase其实只有增加数据所有的更新和删除操作都是在后续的Compact过程中进行的,这样使得用户的写操作只要进入内存就可以立即返回,保证了HBaseI/O的高性能。

  • 数据flush过程如下:

当memstore数据达到阈值(默认是128M),将数据刷到硬盘,将内存中的数据删除,同时删除Hlog中的历史数据。

并将数据存储到hdfs中。

在hlog中做标记点。

数据Compact合并过程如下:

当数据块达到4块,hmaster将数据块加载到本地,进行合并

当合并的数据超过256M,进行拆分,将拆分后的region分配给不同的hregionserver管理

当hregionser宕机后,将hregionserver上的hlog拆分,然后分配给不同的hregionserver加载,修改habse:meta(HBase的内置表,存储HBase内部的系统信息——Region的分布情况以及每个Region的详细信息)

hlog会同步到hdfs

  • HBase 容错

HMaster 容错:通过Zookeeper实现,如果HMaster挂掉,通过Zookeeper的选举机制重新选择一个新的HMaster

HRegionServer容错:RegionServer会定时向Zookeeper发送心跳,如果一段时间内未出现心跳,Master将该RegionServer上的Region重新分配到其他RegionServer上。

Zookeeper容错:zookeeper是为其它分布式框架提供分布式协调服务的,本身并不是单点,一般都是奇数个zk组成的集群,如果某一个挂掉后,zk会通过选举机制重新选择一个leader。

HBase存储格式

HBase中的所有数据文件都存储在Hadoop HDFS文件系统上,主要包括上述提出的两种文件类型:

  • HFile,HBase中KeyValue数据的存储格式,HFile是Hadoop的二进制格式文件,实际上StoreFile就是对HFile做了轻量级包装,即StoreFile底层就是HFile

  • HLog File,HBase中WAL(Write Ahead Log)的存储格式,物理上是Hadoop的Sequence File

HFile

架构图

首先HFile文件是不定长的,长度固定的只有其中的两块:Trailer和FileInfo。正如图中所示的,Trailer 中有指针指向其他数据块的起始点。 File Info中记录了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等。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,然后是两个固定长度的数 值,表示Time Stamp和Key Type(Put/Delete)。 Value部分没有这么复杂的结构,就是纯粹的二进制数据了。 

HLogFile

架构图

HLog文件就是一个普通的Hadoop Sequence File,Sequence File 的Key是HLogKey对象,HLogKey中记录了写入数据的归属信息, 除了table和region名字外,同时还包括 sequence number和timestamp,timestamp是“写入时间”,sequence number的起始值为0,或者是最近一次存入文件系统中sequence number。