本文目录如下所示:
目录
- HFile在HBase架构中的位置
- 什么是HFile
- HFile逻辑结构
- HFile逻辑结构的优点
- HFile物理结构
- HFile生成流程
- HFile中Block块解析
- 多大的HFile文件才存在Intermiate Index Block
HFile在HBase架构中的位置
如上图所示,HFile是HBase最底层的文件组织形式。
Table
--N Region
--N Store
--N StoreFile
--HFile(StoreFile与HFile是一对一)
什么是HFile
HFile是HBase存储数据的文件组织形式,参考BigTable的SSTable和Hadoop的TFile实现。
从HBase开始到现在,HFile经历了三个版本,其中V2在0.92引入,V3在0.98引入。HFileV1版本的在实际使用过程中发现它占用内存多,HFile V2版本针对此进行了优化,HFileV3版本基本和V2版本相同,只是在cell层面添加了Tag数组的支持。鉴于此,本文主要针对V2版本进行分析。
HFile逻辑结构
最初的HFile格式(HFile V1)
Data Block默认大小为64k。
Data Index部分存储了每一个Data Block的索引信息{Offset,Size,FirstKey},这里只有1级索引,当HFile较大,索引信息过多,导致一个RegionServer启动时可能需要加载数GB的Data Block Index数据。这在一个大数据量的集群中,几乎无法忍受。另外,第一次读取时需要加载所有的Bloom Filter数据到内存中。一个HFile中的Bloom Filter的数据大小可达百MB级别。
Data Block Index究竟有多大?
一个Data Block在Data Block Index中的索引信息包含{Offset, Size, FirstKey},BlockOffset使用Long型数字表示,Size使用Int表示即可。假设用户数据RowKey的长度为50bytes,那么,一个64KB的Data Block在Data Block Index中的一条索引数据大小约为62字节。
假设一个RegionServer中有500个Region,每一个Region的数量为10GB(假设这是Data Blocks的总大小),在这个RegionServer上,约有81920000个Data Blocks,此时,Data Block Index所占用的大小为81920000*62bytes,约为4.7GB
HFile V2设计
作为V1的改进版,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的基本信息、各个部分的偏移值和寻址信息。
HFile逻辑结构的优点
- 分层索引
Data Block的索引,在HFile V2中最多可支持三层索引:
- Root Data Index
- Intermediate Index Block
- Leaf Index Block
- Data Block
- Leaf Index Block
- Intermediate Index Block
- 交叉存放
在”Scanned Block Section“区域,Data Block(存放用户数据KeyValue)、存放Data Block索引的Leaf Index Block(存放Data Block的索引)与Bloom Block(Bloom Filter数据)交叉存在。
- 按需读取
无论是Data Block的索引数据,还是Bloom Filter数据,都被拆成了多个Block,基于这样的设计,无论是索引数据,还是Bloom Filter,都可以按需读取,避免在Region Open阶段或读取阶段一次读入大量的数据,有效降低时延。
将索引分级后,RegionServer不需要将所有索引都加载,加载一级索引即可。
HFile物理结构
-
所有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进行详细的讲解:
HFile中Block块解析
HFile生成流程
基本思路:先填满内容,再生成header,最后生成checksum之类。
起初,HFile中并没有任何Block,数据还存在于MemStore中。
Flush发生时,创建HFile Writer,第一个空的Data Block出现,初始化后的Data Block中为Header部分预留了空间,Header部分用来存放一个Data Block的元数据信息。
而后,位于MemStore中的KeyValues被一个个append到位于内存中的第一个Data Block中:
- Header内容
为输出的Data Block生成一条索引记录,包含这个Data Block的{起始Key,偏移,大小}信息,这条索引记录被暂时记录到内存的Block Index Chunk中:
Leaf Index Block:
- Bloom Filter
多大的HFile文件才存在Intermiate Index Block
基于每一个Block中的FirstKey为50bytes的假设,一个128KB的Root Index Block可容纳的HFile文件总大小约为252GB。
如果实际的RowKey小于50 Bytes,或者将Data Block的Size调大,一个128KB的Root Index Chunk所关联的HFile文件将会更大。因此,在大多数场景中,Intermediate Index Block并不会存在。
总结
关于架构设计的一些基本思想在HFile中得到充分体现,分治(HFile本身属于分治的产物,Table的存储被分解成HFile的存储)、HFileBlock的抽象,分层(索引),架构演进(v1/v2/v3的演进)。
另外HFileBlock数据结构设计比较好,支持8种结构。