Hfile結構

文件主要分為四個部分:Scanned block section,Non-scanned block section,Opening-time data section和Trailer。
- Scanned block section:顧名思義,表示順序掃描HFile時所有的數(shù)據(jù)塊將會被讀取,包括Leaf Index Block和Bloom Block。
- Non-scanned block section:表示在HFile順序掃描的時候數(shù)據(jù)不會被讀取,主要包括Meta Block和Intermediate Level Data Index Blocks兩部分。
- Load-on-open-section:這部分數(shù)據(jù)在HBase的region server啟動時,需要加載到內存中。包括FileInfo、Bloom filter block、data block index和meta block index。
- Trailer:這部分主要記錄了HFile的基本信息、各個部分的偏移值和尋址信息。
分層索引
無論是Data Block Index還是Bloom Filter,都采用了分層索引的設計。
Data Block的索引,在HFile V2中做多可支持三層索引:最底層的Data Block Index稱之為Leaf Index Block,可直接索引到Data Block;中間層稱之為Intermediate Index Block,最上層稱之為Root Data Index,Root Data index存放在一個稱之為”Load-on-open Section“區(qū)域,Region Open時會被加載到內存中?;镜乃饕壿嫗椋河蒖oot Data Index索引到Intermediate Block Index,再由Intermediate Block Index索引到Leaf Index Block,最后由Leaf Index Block查找到對應的Data Block。在實際場景中,Intermediate Block Index基本上不會存在,文末部分會通過詳細的計算闡述它基本不存在的原因,因此,索引邏輯被簡化為:由Root Data Index直接索引到Leaf Index Block,再由Leaf Index Block查找到的對應的Data Block。
交叉存放
在”Scanned Block Section“區(qū)域,Data Block(存放用戶數(shù)據(jù)KeyValue)、存放Data Block索引的Leaf Index Block(存放Data Block的索引)與Bloom Block(Bloom Filter數(shù)據(jù))交叉存在。
按需讀取
無論是Data Block的索引數(shù)據(jù),還是Bloom Filter數(shù)據(jù),都被拆成了多個Block,基于這樣的設計,無論是索引數(shù)據(jù),還是Bloom Filter,都可以按需讀取,避免在Region Open階段或讀取階段一次讀入大量的數(shù)據(jù),有效降低時延。
我們先假設沒有Bloom Filter數(shù)據(jù)。當MemStore中所有的KeyValues全部寫完以后,HFile Writer開始在close方法中處理最后的”收尾”工作:
- 寫入最后一個Data Block。
- 寫入最后一個Leaf Index Block。如上屬于Scanned Block Section部分的”收尾”工作。
- 如果有MetaData則寫入位于Non-Scanned Block Section區(qū)域的Meta Blocks,事實上這部分為空。
- 寫Root Block Index Chunk部分數(shù)據(jù):如果Root Block Index Chunk超出了預設大小,則輸出位于Non-Scanned Block Section區(qū)域的Intermediate Index Block數(shù)據(jù),以及生成并輸出Root Index Block(記錄Intermediate Index Block索引)到Load-On-Open Section部分。如果未超出大小,則直接輸出為Load-On-Open Section部分的Root Index Block。
- 寫入用來索引Meta Blocks的Meta Index數(shù)據(jù)(事實上這部分只是寫入一個空的Block)。
- 寫入FileInfo信息,F(xiàn)ileInfo中包含:Max SequenceID, MajorCompaction標記,TimeRanage信息,最早的Timestamp, Data BlockEncoding類型,BloomFilter配置,最大的Timestamp,KeyValue版本,最后一個RowKey,平均的Key長度,平均Value長度,Key比較器等。
- 寫入Bloom Filter元數(shù)據(jù)與索引數(shù)據(jù)。注:前面每一部分信息的寫入,都以Block形式寫入,都包含Header與Data兩部分,Header中的結構也是相同的,只是都有不同的Block Type,在Data部分,每一種類型的Block可以有自己的定義。
- 寫入Trailer部分信息, Trailer中包含:Root Index Block的Offset,F(xiàn)ileInfo部分Offset,Data Block Index的層級,Data Block Index數(shù)據(jù)總大小,第一個Data Block的Offset,最后一個Data Block的Offset,Comparator信息,Root Index Block的Entries數(shù)量,加密算法類型,Meta Index Block的Entries數(shù)量,整個HFile文件未壓縮大小,整個HFile中所包含的KeyValue總個數(shù),壓縮算法類型等。
至此,一個完整的HFile已生成。我們可以通過下圖再簡單回顧一下Root Index Block、Leaf Index Block、Data Block所處的位置以及索引關系:

Bloom Filter包含Bloom元數(shù)據(jù)(Hash函數(shù)類型,Hash函數(shù)個數(shù)等)與位圖數(shù)據(jù)(BloomData),為了避免每一次讀取時加載所有的Bloom Data,HFile V2中將BloomData部分分成了多個小的Bloom Block。BloomData數(shù)據(jù)也被當成一類Inline Block,與Data Block、Leaf Index Block交叉存在,而關于Bloom Filter的元數(shù)據(jù)與多個Bloom Block的索引信息,被存放在Load-On-Open Section部分。但需要注意的是,在FileInfo部分,保存了關于BloomFilter配置類型信息,共包含三種類型:不啟用,基于Row構建BloomFilter,基于Row+Column構建Bloom Filter?;旌狭薆loomFilter Block以后的HFile構成如下圖所示:

為啥這么快
再來看hbase如何在hdfs上去檢索一行數(shù)據(jù)。首先要只要hbase的檢索都是以rowkey值或者rowkey值范圍來檢索數(shù)據(jù)的,現(xiàn)在root表中檢索mata表的的hregion位置,root表只會有一個region而且永遠不會
被拆分以保證能夠一次獲取到mata表的hregion的位置,在mata表中保存所有的用戶表的region的信息,region的rowkey有該region對應的表和第一行的rowkey等組成,因為一個表的rowkey在所有的
region上都是有序的字典排序,所有要檢索一個rowkey只要通過對比mata表中region的rowkey就可以知道包含改rowkey的數(shù)據(jù)在那個region上,meta中還包含了region所咋的hregionserver的信息,通過
mata中的region的信息可以直接定位到包含改rowkey數(shù)據(jù)的所在的region在哪臺hregionserver上。
知道region在哪臺hregionserver上對已快速定位rowkey的數(shù)據(jù)還是不夠的,region會根據(jù)families把數(shù)據(jù)才分成store,一個store只能包含一個family,在保存到hdfs的時候store其實就是一個目錄而已,真正存數(shù)據(jù)的是filestroe也就是hfile,每一個hfile當達到一定大小的時候就會拆分成兩個hfile所以一個store目錄中會包含多個hfile。
因為table是按照rowkey來劃分region的,region默認的大小為256M,通常會設置得更高1G,2G,4G等,所以hfile不可能比region的的值要大。但是hfile有可能還是很大,在hdfs上會拆分成不同的block放在不同的datanode上,這樣子仍然無法做到精確定位。
hfile 繼續(xù)劃分,有data block,block index,trailler等組成,已經定位到rowkey所在的hfile時,會先讀取hfile的trailer的信息以獲取block index的位置,block index的key就是data block中的第一個rowkey,所以通過block index 的key就能精確的定位到要檢索的rowkey在那個data block上,然后直接將該data block讀取到內存,需要注意的是這里的data block已經很小了(默認是64k,不同于hdfs上的block默認為64M,hbase的hfile中的block要小的多)這樣子足以讀取該block到內存中,將該block進行遍歷就能獲取到需要的rowkey取出數(shù)據(jù),以為這里的block只有64k這樣的遍歷非常迅速。這就是為什么hfile的data block要設置的如此之小的原因。