1.前言
HBase的數(shù)據(jù)最終是以HFile的形式存儲(chǔ)在HDFS中的,HBase中HFile有著自己的格式。一次memstore的flush會(huì)產(chǎn)生一個(gè)HFile,一次Compact會(huì)導(dǎo)致多個(gè)HFile合并成一個(gè)。本文主要講述一下HFile文件格式,并介紹一些HBase中是如何讀取,寫(xiě)出HFile的。通過(guò)HBase提供的讀/寫(xiě)HFile的reader和writer工廠類(lèi),使用者可以直接從HFile文件讀取數(shù)據(jù),從而繞過(guò)HBase提供的Scan、Get、Put等api。
2. HFile格式

如上圖所示,一個(gè)HFile內(nèi)容是由一個(gè)個(gè)block組成的,按照block類(lèi)型可分為:
- datablock 存放的key-value數(shù)據(jù),一般一個(gè)datablock大小默認(rèn)為64KB,
- data index block,其中存放的是datablock的index,索引可以是多級(jí)索引,中間索引,葉子索引一般會(huì)分布在HFile文件當(dāng)中。root 索引位置見(jiàn)圖中 root data-block-index
- bloom filter block, 保存了bloom過(guò)濾器的值
- meta data block,meta data block有多個(gè),且連續(xù)分布,見(jiàn)圖中meta data block
- meta data index,顧名思義
- file-info block, 其中記錄了關(guān)于文件的一些信息,比如:HFile中最大的key、平均Key長(zhǎng)度、HFile創(chuàng)建時(shí)間戳、data block使用的編碼方式等等
- trailer block,每個(gè)HFile文件都會(huì)有的,對(duì)于不同版本的HFile(有V1,V2,V3三個(gè)版本,V2和V3相差不大)來(lái)說(shuō)trailer長(zhǎng)度可能不一樣,但是同一個(gè)版本的所有HFile trailer的長(zhǎng)度都是一樣長(zhǎng)的,并且trailer的最后4B一定是版本信息
從上圖可以看出在meta data block之前,datablock、bloom filter block,葉子/中間層data block索引是相間分布的,meta block之后就不會(huì)再有data block了
2.1 HFileBlock
出了trailer以外,其他的數(shù)據(jù)都是一個(gè)個(gè)block,由類(lèi)HFileBlock表示,每一個(gè)block里都有一個(gè)header記錄了一些關(guān)于這個(gè)block的信息如下:
- BlockType, 8B, block所屬類(lèi)型
- onDiskSizeWithoutHeader,4B,當(dāng)前block中出了header以外占住的磁盤(pán)大?。ㄓ捎诖疟P(pán)上的數(shù)據(jù)是經(jīng)過(guò)編碼和壓縮的,所以和內(nèi)存中大小是不一樣的)
- uncompressedSizeWithoutHeader,4B,block數(shù)據(jù)未壓縮前在內(nèi)存中占據(jù)的大小,不包括header。
- prevBlockOffset, 8B,前一個(gè)block的offset
- checkSum type,1B,使用的校驗(yàn)和方法,CRC32之類(lèi)的
- num of data bytes per check sum, 4B,多少個(gè)字節(jié)計(jì)算一次校驗(yàn)和
7 .onDiskDataSizeWithHeader,4B,當(dāng)前block占據(jù)磁盤(pán)的總大小
以上每個(gè)block都會(huì)有固定的33Byte的header大小。
header接下來(lái)就是真實(shí)的數(shù)據(jù)了。
數(shù)據(jù)后面就是checksum,由于是每多少個(gè)字節(jié)計(jì)算一次checksum,所以有多個(gè)checksum,每個(gè)checksum占據(jù)4Byte。
3.讀寫(xiě)
無(wú)論讀寫(xiě)HFile,都可以通過(guò)org.apache.hadoop.hbase.io.hfile.HFile這類(lèi)類(lèi)提供的一些靜態(tài)方法來(lái)實(shí)現(xiàn)
3.1 寫(xiě)
寫(xiě)HFile的類(lèi)繼承結(jié)構(gòu)如下:

- HFileWriterV2和HFileWriterV3分別 負(fù)責(zé)v2和v3版本的HFile的寫(xiě).
它們的實(shí)例則是通過(guò)對(duì)應(yīng)的兩個(gè)工廠類(lèi)WriterFactoryV2和WriterFactoryV3。
類(lèi)HFile提供了創(chuàng)建WriterFactoryXX的方法如下:
public static final WriterFactory getWriterFactory(Configuration conf,
CacheConfig cacheConf) {
int version = getFormatVersion(conf);
switch (version) {
case 2:
return new HFileWriterV2.WriterFactoryV2(conf, cacheConf);
case 3:
return new HFileWriterV3.WriterFactoryV3(conf, cacheConf);
default:
throw new IllegalArgumentException("Cannot create writer for HFile " +
"format version " + version);
}
}
使用Writer至少需要在conf里面通過(guò)hfile.format.version指定version,且只能是2或3, 代表著v2和v3
3.2 讀
Reader繼承結(jié)構(gòu)如下:

同樣有V2和V3兩個(gè)版本的reader。
通過(guò)HFile的靜態(tài)方法獲取Reader如下:
public static Reader createReader(FileSystem fs, Path path,
FSDataInputStreamWrapper fsdis, long size, CacheConfig cacheConf, Configuration conf)
throws IOException
public static Reader createReader(
FileSystem fs, Path path, CacheConfig cacheConf, Configuration conf) throws IOException
獲取reader無(wú)需指定version,因?yàn)関ersion信息已經(jīng)在HFile的trailer里面了。