Chinaunix首页 | 论坛 | 博客
  • 博客访问: 244509
  • 博文数量: 63
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 750
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-10 21:59
  • 认证徽章:
个人简介

付出,终有回报!

文章分类

全部博文(63)

文章存档

2018年(6)

2017年(24)

2016年(6)

2015年(27)

分类: HADOOP

2017-02-27 11:02:07

HBase采用Master/Slave架构搭建集群,它隶属于Hadoop生态系统,由一下类型节点组成:HMaster节点、HRegionServer节点、ZooKeeper集群,而在底层,它将数据存储于HDFS中,因而涉及到HDFSNameNodeDataNode等,总体结构如下:




1、Client

包含访问HBase的接口,并维护cache来加快对HBase的访问,比如region的位置信息。HBase Client使用HBaseRPC机制与HMasterHRegionServer进行通信,对于管理类操作,ClientHMaster进行RPC;对于数据读写类操作,ClientHRegionServer进行RPC


2、Zookeeper

ZooKeeper为HBase集群提供协调服务,它管理着HMasterHRegionServer的状态(available/alive),并且会在它们宕机时通知给HMaster,从而HMaster可以实现HMaster之间的failover,或对宕机的HRegionServer中的HRegion集合的修复(将它们分配给其他的HRegionServer)ZooKeeper集群本身使用一致性协议(PAXOS协议)保证每个节点状态的一致性。Zookeeper Quorum中除了存储了-ROOT-表的地址和HMaster的地址,HRegionServer也会把自己以Ephemeral方式注册到 Zookeeper中,使得HMaster可以随时感知到各个HRegionServer的健康状态。此外,Zookeeper也避免了HMaster的 单点问题。

1、通过选举,保证任何时候,集群中只有一个masterMasterRegionServers 启动时会向ZooKeeper注册

2、存贮所有Region的寻址入口

3、实时监控Region server的上线和下线信息。并实时通知给Master

4、存储HBaseschematable元数据

5、默认情况下,HBase 管理ZooKeeper 实例,比如, 启动或者停止ZooKeeper

6、Zookeeper的引入使得Master不再是单点故障

3、HMaster

HMaster是主服务器的实现。主服务器负责监控集群中的所有RegionServer实例,并且是所有元数据更改的接口。在分布式集群中,主服务器通常在NameNode上运行。HMaster没有单点故障问题,可以启动多个HMaster,通过ZooKeeperMaster Election机制保证同时只有一个HMaster处于Active状态,其他的HMaster则处于热备份状态。一般情况下会启动两个HMaster,非ActiveHMaster会定期的和Active HMaster通信以获取其最新状态,从而保证它是实时更新的,因而如果启动了多个HMaster反而增加了Active HMaster的负担。HMaster在功能上主要负责TableRegion的管理工作:

1、管理用户对Table的增、删、改、查操作

2、管理HRegionServer的负载均衡,调整Region分布

3、在Region Split后,负责新Region的分配

4、在HRegionServer停机后,负责失效HRegionServer 上的Regions迁移

4、HRegionServer

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



HRegionServer一般和DataNode在同一台机器上运行,实现数据的本地性。HRegionServer包含多个HRegion,由WAL(HLog)BlockCacheMemStoreHFile组成。

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

1HRegion

Table随着记录数不断增加而变大后,会逐渐分裂成多份splits,成为regionsHBase使用RowKey将表水平切割成多个HRegion,从HMaster的角度,每个HRegion都纪录了它的StartKeyEndKey(第一个HRegionStartKey为空,最后一个HRegionEndKey为空),由于RowKey是排序的,因而Client可以通过HMaster快速的定位每个RowKey在哪个HRegion中。HRegionHMaster分配到相应的HRegionServer中,然后由HRegionServer负责HRegion的启动和管理,和Client的通信,负责数据的读(使用HDFS)。每个HRegionServer可以同时管理1000个左右的HRegion


2MemStore

HStore存储是HBase存储的核心了,其中由两部分组成,一部分是MemStore,一部分是StoreFiles

MemStore是一个In Memory Sorted Buffer,在每个HStore中都有一个MemStore,即它是一个HRegion的一个Column Family对应一个实例。它的排列顺序以RowKeyColumn FamilyColumn的顺序以及Timestamp的倒序,如下所示:



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

有以下三种情况可以触发MemStoreFlush动作,需要注意的是MemStore的最小Flush单元是HRegion而不是单个MemStore

  1. 当一个HRegion中的所有MemStore的大小总和超过了hbase.hregion. memstore.flush.size的大小,默认128MB。此时当前的HRegion中所有的MemStoreFlushHDFS中。
  2. 当全局MemStore的大小超过了hbase.regionserver.global.memstore. upperLimit的大小,默认40%的内存使用量。此时当前HRegionServer中所有HRegion中的MemStore都会FlushHDFS中,Flush顺序是MemStore大小的倒序,直到总体的MemStore使用量低于hbase. region server.global.memstore.lowerLimit,默认38%的内存使用量。
  3. 当前HRegionServerWAL的大小超过了hbase.regionserver.hlog. blocksize * hbase.regionserver.max.logs的数量,当前HRegionServer中所有HRegion中的MemStore都会FlushHDFS中,Flush使用时间顺序,最早的MemStoreFlush直到WAL的数量少于hbase.regionserver.

hlog. hlog.blocksize * hbase.regionserver. max.logs。

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


3WAL

在理解了上述MemStore的基本原理后,还必须了解一下WAL的功能,WALWrite Ahead Log,在早期版本中称为HLog,它是HDFS上的一个文件,如其名字所表示的,所有写操作都会先保证将数据写入这个Log文件后,才会真正更新MemStore,最后写入HFile中。HStore在系统正常工作的前提下是没有问题的,但是在分布式系统环境中,无法避免系统出错或者宕机,因此一旦HRegionServer意外退出,MemStore中的内存数据将会丢失,这就需要引入WAL了。采用WAL这种模式,可以保证HRegionServer宕机后,我们依然可以从该Log文件中读取数据,Replay所有的操作,而不至于数据丢失。

每个HRegionServer中都有一个WAL对象,WAL是一个实现Write Ahead Log的类,在每次用户操作写入MemStore的同时,也会写一份数据到HLog文件中,WAL文件定期会滚动出新的,并删除旧的文件(已持久化到StoreFile中的数据)。当HRegionServer意外终止后,HMaster会通过Zookeeper感知到,HMaster首先会处理遗留的WAL文件,将其中不同RegionLog数据进行拆分,分别放到相应region的目录下,然后再将失效的region重新分配,领取到这些regionHRegionServerLoad Region的过程中,会发现有历史HLog需要处理,因此会替换WAL中的数据到MemStore中,然后flushStoreFiles,完成数据恢复。

WAL文件存储在/hbase/WALs/${HRegionServer_Name}的目录中(0.94之前,存储在/hbase/.logs/目录中),一般一个HRegionServer只有一个WAL实例,也就是说一个HRegionServer的所有WAL写都是串行的(就像log4j的日志写也是串行的),这当然会引起性能问题,因而在HBase 1.0之后,通过HBASE-5699实现了多个WAL并行写(MultiWAL),该实现采用HDFS的多个管道写,以单个HRegion为单位。

4BlockCache

BlockCache是一个读缓存,即引用局部性原理(也应用于CPU,分为空间局部性和时间局部性,空间局部性是指CPU在某一时刻需要某个数据,那么有很大的概率在一下时刻它需要的数据在其附近;时间局部性是指某个数据在被访问过一次后,它有很大的概率在不久的将来会被再次的访问),将数据预读取到内存中,以提升读的性能。HBase中提供两种BlockCache的实现:默认on-heap LruBlockCacheBucketCache(通常是off-heap)。通常BucketCache的性能要差于LruBlockCache,然而由于GC的影响,LruBlockCache的延迟会变的不稳定,而BucketCache由于是自己管理BlockCache,而不需要GC,因而它的延迟通常比较稳定,这也是有些时候需要选用BucketCache的原因。

5HFile

HBase的数据以KeyValue(Cell)的形式顺序的存储在HFile中,在MemStoreFlush过程中生成HFile,由于MemStore中存储的Cell遵循相同的排列顺序,因而Flush过程是顺序写,我们知道磁盘的顺序写性能很高,因为不需要不停的移动磁盘指针。

HFile参考BigTableSSTableHadoopTFile实现,从HBase开始到现在,HFile经历了三个版本,其中V20.92引入,V30.98引入。首先我们来看一下V1的格式:


V1的HFile由多个Data BlockMeta BlockFileInfoData IndexMeta IndexTrailer组成,其中Data BlockHBase的最小存储单元,在前文中提到的BlockCache就是基于Data Block的缓存的。一个Data Block由一个魔数和一系列的KeyValue(Cell)组成,魔数是一个随机的数字,用于表示这是一个Data Block类型,以快速监测这个Data Block的格式,防止数据的破坏。Data Block的大小可以在创建Column Family时设置(HColumnDescriptor.setBlockSize()),默认值是64KB,大号的Block有利于顺序Scan,小号Block利于随机查询,因而需要权衡。Meta块是可选的,FileInfo是固定长度的块,它纪录了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等。Data IndexMeta Index纪录了每个Data块和Meta块的其实点、未压缩时大小、Key(起始RowKey)等。Trailer纪录了FileInfoData IndexMeta Index块的起始位置,Data IndexMeta Index索引的数量等。其中FileInfoTrailer是固定长度的。

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


开始是两个固定长度的数值,分别表示Key的长度和Value的长度。紧接着是Key,开始是固定长度的数值,表示RowKey的长度,紧接着是 RowKey,然后是固定长度的数值,表示Family的长度,然后是Family,接着是Qualifier,然后是两个固定长度的数值,表示Time StampKey TypePut/Delete)。Value部分没有这么复杂的结构,就是纯粹的二进制数据了。随着HFile版本迁移,KeyValue(Cell)的格式并未发生太多变化,只是在V3版本,尾部添加了一个可选的Tag数组

HFileV1版本的在实际使用过程中发现它占用内存多,并且Bloom FileBlock Index会变的很大,而引起启动时间变长。其中每个HFileBloom Filter可以增长到100MB,这在查询时会引起性能问题,因为每次查询时需要加载并查询Bloom Filter100MBBloom Filer会引起很大的延迟;另一个,Block Index在一个HRegionServer可能会增长到总共6GBHRegionServer在启动时需要先加载所有这些Block Index,因而增加了启动时间。为了解决这些问题,在0.92版本中引入HFileV2版本:


在这个版本中,Block IndexBloom Filter添加到了Data Block中间,而这种设计同时也减少了写的内存使用量;另外,为了提升启动速度,在这个版本中还引入了延迟读的功能,即在HFile真正被使用时才对其进行解析。

FileV3版本基本和V2版本相比,并没有太大的改变,它在KeyValue(Cell)层面上添加了Tag数组的支持;并在FileInfo结构中添加了和Tag相关的两个字段。

HFileV2格式具体分析,它是一个多层的类B+树索引,采用这种设计,可以实现查找不需要读取整个文件:



Data Block中的Cell都是升序排列,每个block都有它自己的Leaf-Index,每个Block的最后一个Key被放入Intermediate-Index中,Root-Index指向Intermediate-Index。在HFile的末尾还有Bloom Filter用于快速定位那么没有在某个Data Block中的RowTimeRange信息用于给那些使用时间查询的参考。在HFile打开时,这些索引信息都被加载并保存在内存中,以增加以后的读取性能。


阅读(377) | 评论(0) | 转发(0) |
0

上一篇:HBase数据模型(二)

下一篇:HBase的读写

给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册