HBase源码分析6—HFile剖析

11. 三月 2018 hbase, 数据库 0

上一篇中我们介绍了一下BloomFilter的相关内容,这部分内容对于我们理解HFile的结构很重要(其实也没那么重要,跳过不看就是了= =||)。这一篇我们就来针对HFile下手,研究下他的结构究竟是什么样的。

历史

HFile一共经历了三个版本的变迁。我们有必要先了解下每次修改的缘由。

(在HFile出现前还有基于MapFile的解决方案,因为当时并没有HFile这个东西,就不提了。Compaction和标记删除就是那个时候出现的。)

V1

JIRA(HBASE-61)上看,HFile的出现似乎是为了挖掘潜在的性能优化能力。在V1中,data和index保存在同一个文件中,支持保存metadata(如BloomFilter),FileInfo用于保存合并等场景需要的文件信息。

图片来自hbase.apache.org

V2

随着数据量的变大,V1的缺点也逐渐暴露出来:每个HFile中的索引等信息必须全部加载到内存中,这些数据有时会很大。在V2中主要引入了多级索引,不需要把所有索引加载到内存、块级索引等。

在这个图中:

  • Scanned Block Section:顺序扫描时使用的块,包括了data block、leaf index block、bloom block
  • Non-Scanned Block Section:顺序扫描时不使用的块,包括了meta block、intermediate data block等
  • Load-on-Open Section:启动时需要被加载到内存的块,包括root data index、meta index、file info等
  • Trailer:记录HFile基本信息和上面三个块的偏移量等信息

Block

HFileBlock

所有的块(跟data block同级的这些)都继承自HFileBlock(位置在 main/java/org/apache/hadoop/hbase/io/hfile/HFileBlock.java ),结构如下图

BlockType 表示了这个块的类型。HBase提供了以下几种类型( MagicStr 就是实际填在 BlockType 位置的数据)

BlockType 这个类里还定义了一些基本的读写操作,比较简单不列举了。

HFile是如何被读的呢?可以看同一个包下 HFileReaderV2.java ,在构造的时候有一个循环

追踪 readBlock 这个函数可以追到 HFileBlock.java 中的 readBlockDataInternal ,比较简单,先读header,然后根据长度建一个新的buffer,把header拷贝过去,再把data读到buffer的后面。但是上面这个函数里block搞出来也没有下一步动作,其实是都整个被缓存起来了。代码在 readBlock函数中

 

下面我们挑几个block来分析下

HFileBlockIndex

HFileBlockIndex 是所有index的实现,里面提供了针对多层index和单层index的不同方法。比如在HFileReaderV2中

分析两个read的代码,总结出index block的data块格式大概如下,其中红色的中位信息只在data index中有,meta index里是没有的。具体有多少个blockEntry是从Trailer块里读出来的。

另外作为Index Block,这个类里面还提供了很多跟index相关的方法。比如我们挑这个类里面最长也是覆盖面最广的方法 loadDataBlockWithScanInfo ,这个方法的目的是搜索含有指定key的Data Block(和一些附加信息),代码如下(太长,折叠了)

首先从通过 rootBlockContainingKey 查他在root index中的位置,这个是通过在blockkeys数组中做二分查找实现的(blockkeys存储的是每个块的最小的key)。然后通过一个while循环一层一层往下找,如果当前层是data block就退出循环,否则(就是intermediate index block)调用 locateNonRootIndexEntry 查找下一层的block,这里面又是一个二分查找。最后组装一下data block的信息返回。

 

Data Block

HFileReaderV2 初始化代码中我们可以找到这个片段,是跟data block解析相关的

意思说是根据fileInfo的信息设置合适的encoder。在 createFromFileInfo 这个方法中又有

这个 NoOpDataBlockEncoder.INSTANCE 就是raw数据的encoder了,也就是不做任何的encoding和decoding。继续追进去在 startBlockEncoding 方法中我们可以发现实际的encoder是 NoneEncoder 。通过对这个类的分析,我们很容易得到data block的上层结构。

CellUtil.writeFlatKey(cell, out); 代码,可以看出key的结构

结构如下图

 

 


发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据