HBase——HFile存储结构解析

前言

HFile是HBase存储数据的文件组织形式,参考BigTable的SSTable和Hadoop的TFile实现。从HBase开始到现在,HFile经历了三个版本,其中V2在0.92引入,V3在0.98引入。HFileV1版本的在实际使用过程中发现它占用内存多,HFile V2版本针对此进行了优化,HFile V3版本基本和V2版本相同,只是在cell层面添加了Tag数组的支持。鉴于此,本文主要针对V2版本进行分析,对V1和V3版本感兴趣的同学可以参考其他信息。

一、HFile V1

  • Data Block:保存表中的数据,这部分可以被压缩

  • Meta Block:(可选)保存用户自定义的kv对,可以被压缩。

  • File Info:Hfile的meta元信息,不被压缩,定长。

  • Data Block Index:Data Block的索引。每个Data块的起始点。

  • Meta Block Index:(可选的)Meta Block的索引,Meta块的起始点。

  • Trailer: 定长。保存了每一段的偏移量,读取一个HFile时,会首先读取Trailer,Trailer有指针指向其他数据块的起始点,保存了每个段的起始位置(段的Magic Number用来做安全check),然后,DataBlock Index会被读取到内存中,这样,当检索某个key时,不需要扫描整个HFile,而只需从内存中找到key所在的block,通过一次磁盘io将整个block读取到内存中,再找到需要的key。DataBlock Index采用LRU机制淘汰。

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

注意,block是hfile的最小压缩和编码数据块,默认128KB

第一版的block index是非常简单的,注意只有两个block index,一个是data block index,一个是meta block index,block index包括了以下几点:

    1. offset
    1. Uncompressed size
    1. Key (a serialized byte array written using Bytes.writeByteArray)
    • 3.1 Key length as a variable-length integer (VInt)
    • 3.2 Key bytes

block index的数量会存放在trailer,这样才能读取到block index数据。

这个版本的block index有1个缺点

    1. 无法知道block压缩后的数据大小,这在之后的解压过程是必要的

所以在版本2,解决了这个问题,在block index中增加了实际存储block大小的数据。

二、HFile V2

在接口层面做了兼容,在读hfile的时候,支持v1和v2,在写hfile的时候,只会写v2版本的hfile。

hfile升级的原因:

    1. v1 的设计导致了region server启动时间很长,需要加载很大的数据量,比如大量的bloom filter,大量的block index

为了解决这个问题,v2使得hfile增加了新特性,把bloom filter和block index打散,写到多个block中去,这样就减少了hfile 写入时候的内存offset。并且这些打散的block index会有预定的长度。

另一方面,v2还用到了 load-on-open 这个概念,意思是说,在打开hfile的时候,加载那些必要的信息,包括trailer,trailer里记录了hfile的必要信息。而其他数据就可以再用到的时候,通过trailer再解析出来。

2.1 HFile逻辑结构

HFile V2的逻辑结构如下图所示:


文件主要分为四个部分:Scanned block section,Non-scanned block section,Opening-time data section和Trailer。

Scanned block section:顾名思义,表示顺序扫描HFile时所有的数据块将会被读取,包括Leaf Index Block和Bloom Block。

Non-scanned block section:表示在HFile顺序扫描的时候数据不会被读取,主要包括Meta Block和Intermediate Level Data Index Blocks两部分。

Load-on-open-section:这部分数据在HBase的region server启动时,需要加载到内存中。包括FileInfo、Bloom filter block、data block index和meta block index。

Trailer:这部分主要记录了HFile的基本信息、各个部分的偏移值和寻址信息。

2.2 HFile物理结构

如上图所示, HFile会被切分为多个大小相等的block块,每个block的大小可以在创建表列簇的时候通过参数blocksize => ‘65535’进行指定,默认为64k,大号的Block有利于顺序Scan,小号Block利于随机查询,因而需要权衡。而且所有block块都拥有相同的数据结构,如图左侧所示,HBase将block块抽象为一个统一的HFileBlock。HFileBlock支持两种类型,一种类型不支持checksum,一种不支持。为方便讲解,下图选用不支持checksum的HFileBlock内部结构:


上图所示HFileBlock主要包括两部分:BlockHeader和BlockData。其中BlockHeader主要存储block元数据,BlockData用来存储具体数据。block元数据中最核心的字段是BlockType字段,用来标示该block块的类型,HBase中定义了8种BlockType,每种BlockType对应的block都存储不同的数据内容,有的存储用户数据,有的存储索引数据,有的存储meta元数据。对于任意一种类型的HFileBlock,都拥有相同结构的BlockHeader,但是BlockData结构却不相同。下面通过一张表简单罗列最核心的几种BlockType,下文会详细针对每种BlockType进行详细的讲解:

2.3 HFile中Block块解析

上文从HFile的层面将文件切分成了多种类型的block,接下来针对几种重要block进行详细的介绍,因为篇幅的原因,索引相关的block不会在本文进行介绍,接下来会写一篇单独的文章对其进行分析和讲解。首先会介绍记录HFile基本信息的TrailerBlock,再介绍用户数据的实际存储块DataBlock,最后简单介绍布隆过滤器相关的block。

2.4 Trailer Block

主要记录了HFile的基本信息、各个部分的偏移值和寻址信息,下图为Trailer内存和磁盘中的数据结构,其中只显示了部分核心字段:


HFile在读取的时候首先会解析Trailer Block并加载到内存,然后再进一步加载LoadOnOpen区的数据,具体步骤如下:

    1. 首先加载version版本信息,HBase中version包含majorVersion和minorVersion两部分,前者决定了HFile的主版本: V1、V2 还是V3;后者在主版本确定的基础上决定是否支持一些微小修正,比如是否支持checksum等。不同的版本决定了使用不同的Reader对象对HFile进行读取解析
    1. 根据Version信息获取trailer的长度(不同version的trailer长度不同),再根据trailer长度加载整个HFileTrailer Block
    1. 最后加载load-on-open部分到内存中,起始偏移地址是trailer中的LoadOnOpenDataOffset字段,load-on-open部分的结束偏移量为HFile长度减去Trailer长度,load-on-open部分主要包括索引树的根节点以及FileInfo两个重要模块,FileInfo是固定长度的块,它纪录了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等;

2.5 Data Block

DataBlock是HBase中数据存储的最小单元。DataBlock中主要存储用户的KeyValue数据(KeyValue后面一般会跟一个timestamp,图中未标出),而KeyValue结构是HBase存储的核心,每个数据都是以KeyValue结构在HBase中进行存储。KeyValue结构在内存和磁盘中可以表示为:


每个KeyValue都由4个部分构成,分别为key length,value length,key和value。其中key value和value length是两个固定长度的数值,而key是一个复杂的结构,首先是rowkey的长度,接着是rowkey,然后是ColumnFamily的长度,再是ColumnFamily,之后是ColumnQualifier,最后是时间戳和KeyType(keytype有四种类型,分别是Put、Delete、 DeleteColumn和DeleteFamily),value就没有那么复杂,就是一串纯粹的二进制数据。

2.6 BloomFilter Meta Block & Bloom Block

BloomFilter对于HBase的随机读性能至关重要,对于get操作以及部分scan操作可以剔除掉不会用到的HFile文件,减少实际IO次数,提高随机读性能。在此简单地介绍一下Bloom Filter的工作原理,Bloom Filter使用位数组来实现过滤,初始状态下位数组每一位都为0,如下图所示:


假如此时有一个集合S = {x1, x2, … xn},Bloom Filter使用k个独立的hash函数,分别将集合中的每一个元素映射到{1,…,m}的范围。对于任何一个元素,被映射到的数字作为对应的位数组的索引,该位会被置为1。比如元素x1被hash函数映射到数字8,那么位数组的第8位就会被置为1。下图中集合S只有两个元素x和y,分别被3个hash函数进行映射,映射到的位置分别为(0,3,6)和(4,7,10),对应的位会被置为1:


现在假如要判断另一个元素是否是在此集合中,只需要被这3个hash函数进行映射,查看对应的位置是否有0存在,如果有的话,表示此元素肯定不存在于这个集合,否则有可能存在。下图所示就表示z肯定不在集合{x,y}中:


HBase中每个HFile都有对应的位数组,KeyValue在写入HFile时会先经过几个hash函数的映射,映射后将对应的数组位改为1,get请求进来之后再进行hash映射,如果在对应数组位上存在0,说明该get请求查询的数据不在该HFile中。

HFile中的位数组就是上述Bloom Block中存储的值,可以想象,一个HFile文件越大,里面存储的KeyValue值越多,位数组就会相应越大。一旦太大就不适合直接加载到内存了,因此HFile V2在设计上将位数组进行了拆分,拆成了多个独立的位数组(根据Key进行拆分,一部分连续的Key使用一个位数组)。这样一个HFile中就会包含多个位数组,根据Key进行查询,首先会定位到具体的某个位数组,只需要加载此位数组到内存进行过滤即可,减少了内存开支。

在结构上每个位数组对应HFile中一个Bloom Block,为了方便根据Key定位具体需要加载哪个位数组,HFile V2又设计了对应的索引Bloom Index Block,对应的内存和逻辑结构图如下:


Bloom Index Block结构中totalByteSize表示位数组的bit数,numChunks表示Bloom Block的个数,hashCount表示hash函数的个数,hashType表示hash函数的类型,totalKeyCount表示bloom filter当前已经包含的key的数目,totalMaxKeys表示bloom filter当前最多包含的key的数目, Bloom Index Entry对应每一个bloom filter block的索引条目,作为索引分别指向’scanned block section’部分的Bloom Block,Bloom Block中就存储了对应的位数组。

Bloom Index Entry的结构见上图左边所示,BlockOffset表示对应Bloom Block在HFile中的偏移量,FirstKey表示对应BloomBlock的第一个Key。根据上文所说,一次get请求进来,首先会根据key在所有的索引条目中进行二分查找,查找到对应的Bloom Index Entry,就可以定位到该key对应的位数组,加载到内存进行过滤判断。

三、HFile V3

增加安全方面特性,为cell级别增加ACL

HFile V3不和HFile V1和HFile V2兼容,因为在存储keyvalue的时候,会额外的存储tags,用于控制ACL。

四、总结

hbase的三个版本的hfile在文件结构层面逐渐完善,每个hfile文件都有自己独立的索引。理解这些结构对实际应用业务优化也是很有好处的。例如有很多场景需要多hbase的resion进行遍历而内存资源又有限的情况,假设计算引擎用的是spark,那么每个分区遍历一个region是不可能的,因为数据量太多,资源吃不下,而根据timestamp过滤分批遍历的话,性能有影响,相当于每个region都要被重复遍历。这种场景下,可以对region按照rowkey分割,每个spark分区只遍历一个region的部分rowkey,这样就可以无限拆分下去,再不也不用担心资源的问题。不过这就又出现了一个问题,如果用rowkey切割region,首先,rowkey如果设计的好的话,不同rowkey段的数据很均匀,那么可以直接根据业务切分。而如果无法评估出不同rowkey段和数据的对应关系,那么这个时候,可以利用到hfile。通过查看region下hfile文件中每个datablock的第一个rowkey,(由于datablock的index都是被保存在hfile的,所以datablock可被直接定位),用该rowkey切割region,并且可以以最小block的粒度来控制每批遍历region的数据量,这个粒度相信资源是完全够用的。

参考:
https://hbase.apache.org/book.html#_hfile_format_2

http://wangneng-168.iteye.com/blog/2164299

http://hbasefly.com/2016/03/25/hbase-hfile/

https://blog.csdn.net/BlackArmand/article/details/119933879

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容