本文由 網易雲 發布。
作者:范欣欣
本篇文章僅限內部分享,如需轉載,請聯系網易獲取授權。
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 版本感興趣的同學可以參考其他信息。
01 HFile邏輯結構
HFile V2的邏輯結構如下圖所示:
文件主要分為四個部分:Scanned block section,Non-scanned block section,Load-on-open-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的基本信息、各個部分的偏移值和尋址信息。
02 HFile物理結構
如上圖所示,HFlie會被切分為多個大小相等的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進行詳細的講解:
03 HFile中Block塊解析
上文從HFile的層面將文件切分成了多種類型的block,接下來針對幾種重要block進行詳細的介紹,因為篇幅的原因,索引相關的block不會在本文進行介紹,接下來會寫一篇單獨的文章對其進行分析和講解。首先會介紹記錄HFile基本信息的TrailerBlock,再介紹用戶數據的實際存儲塊DataBlock,最后簡單介紹布隆過濾器相關的block。
Trailer Block
TrailerBlock主要記錄了HFile的基本信息、各個部分的偏移值和尋址信息,下圖為Trailer內存和磁盤中的數據結構,其中只顯示了部分核心字段:
HFile在讀取的時候首先會解析Trailer Block並加載到內存,然后再進一步加載LoadOnOpen區的數據,具體步驟如下:
1. 首先加載version版本信息,HBase中version包含majorVersion和minorVersion兩部分,前者決定了HFile的主版本:V1、V2 還是V3;后者在主版本確定的基礎上決定是否支持一些微小修正,比如是否支持checksum等。不同的版本決定了使用不同的Reader對象對HFile進行讀取解析
2. 根據Version信息獲取trailer的長度(不同version的trailer長度不同),再根據trailer長度加載整個HFileTrailer Block
3.最后加載load-on-open部分到內存中,起始偏移地址是trailer中的LoadOnOpenDataOffset字段,load-on-open部分的結束偏移量為HFile長度減去Trailer長度,load-on-open部分主要包括索引樹的根節點以及Filelnfo兩個重要模塊,Filelnfo是固定長度的 塊 ,它紀錄了文的一些Meta信息,例 如 : AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等;索引樹根節點放到下一篇文章進行介紹。
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, 最后是時間戳和KeyType(keytype有四種類型,分別是Put、Delete、 DeleteColumn和DeleteFamily),value就沒有那么復雜,就是一串純粹的二進制數據。
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表示bloomfilter當前最多包含的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對應的位數組,加載到內存進行過濾判斷。
04 總 結
這篇小文首先從宏觀的層面對HFile的邏輯結構和物理存儲結構進行了講解,並且將HFile從邏輯上分解為各種類型的Block,再接着從微觀的視角分別對Trailer Block、Data Block在結構上進行了解析:通過對Trailer Block的解析,可以獲取HFile的版本以及HFile中其他幾個部分的偏移量,在讀取的時候可以直接通過偏移量對其進行加載;而對Data Block的解析可以知道用戶數據在HDFS中是如何實際存儲的;最后通過介紹Bloom Filter的工作原理以及相關的Block塊了解HFile中Bloom Filter的存儲結構。接下來會以本文為基礎,再寫一篇文章分析HFile中索引塊的結構以及相應的索引機制。
網易有數:企業級大數據可視化分析平台。面向業務人員的自助式敏捷分析平台,采用PPT模式的報告制作,更加易學易用,具備強大的探索分析功能,真正幫助用戶洞察數據發現價值。可點擊這里免費試用。
了解 網易雲 :
網易雲官網:https://www.163yun.com/
新用戶大禮包:https://www.163yun.com/gift
網易雲社區:https://sq.163yun.com/