1. 程式人生 > >HBase篇(4)-你不知道的HFile

HBase篇(4)-你不知道的HFile

很多 tao 類型 記錄 key 讀寫 meta rst 範圍

【每日五分鐘搞定大數據】系列,HBase第四篇

技術分享圖片

這一篇你可以知道,

HFile的內部結構

HBase讀文件細粒度的過程

HBase隨機讀寫快除了MemStore之外的原因

上一篇中提到了Hbase的數據以HFile的形式存在HDFS, 物理存儲路徑是:

NameSpace->Table->Region->CF->HFile

這一篇我們來說下這個HFile,把路徑從HFile開始再補充一下

HFile->Block->KeyValue.

順便科普一下,HFile具體存儲路徑為:

/hbase/data/<nameSpace>/<tableName>/<encoded-regionname>/<column-family>/<filename>

如何讀取HFile的內容:

hbase org.apache.hadoop.hbase.io.hfile.HFile -f  /上面的路徑指定某個HFile   -p

這是我做的一個思維導圖,這裏面的內容就是我這章要講的東西,有點多,大家慢慢消化。
技術分享圖片

HFile的邏輯分類

Scanned block section:掃描HFile時這個部分裏面的所有block都會被讀取到。

Non-scanned block section:和相面的相反,掃描HFile時不會被讀取到。

Load-on-open-section:regionServer啟動時就會加載這個部分的數據,不過不是最先加載。

Trailer:這個部分才是最先加載到內存的,記錄了各種偏移量和版本信息。

HFile的物理分類

物理分類和邏輯分類對應的關系,可以在上面的圖中看到

HFile有多個大小相等的block組成,Block分為四種類型:Data Block,Index Block,Bloom Block和Meta Block。

  • Data Block
    用於存儲實際數據,通常情況下每個Data Block可以存放多條KeyValue數據對;
  • Index Block和Bloom Block
    都用於優化隨機讀的查找路徑,其中Index Block通過存儲索引數據加快數據查找,而Bloom Block通過一定算法可以過濾掉部分一定不存在待查KeyValue的數據文件,減少不必要的IO操作;
  • Meta Block
    主要存儲整個HFile的元數據。

Data Block

保存了實際的數據,由多個KeyValue 組成,塊大小默認為64K(由建表時創建cf時指定或者HColumnDescriptor.setBlockSize(size)),在查詢數據時,以block為單位加載數據到內存。

KeyValue 的結構

  • key
    由這些內容組成:rowkey長度、rowkeyColumnFamily的長度、ColumnFamily、ColumnQualifier、KeyType(put、Delete、 DeleteColumn和DeleteFamily)
  • key length
    固定長度的數值
  • value
    二進制數據
  • value length
    固定長度的數值

Index Block

  • data block index(Root Index Block )
    Data Block第一層索引
  • Intermediate Level Data Index Block
    Data Block第二層索引
  • Leaf Index Block
    Data Block第三層索引

這三層索引我舉個栗子放在一起說,第一層是必須要的,也是最快的,因為它會被加載到內存中。二三根據數據量決定,如果有的話在找的時候也會加載到內存。實際上就是一步步的縮小範圍,類似B+樹的結構:

a,b,c,d,e    
f,g,h,i,j   
k,l,m,n,o

Root Index Block 第一層:a,g,l
Intermediate Level Data Index Block 第二層:a,c,e  ||  f,h,j  ||  k ,m,o
Leaf Index Block 第三層(部分):a,b  || c,d  || e,f
  1. 假設要搜索的rowkey為bb,root index block(常駐內存)中有三個索引a,g,l,b在a和g之間,因此會去找索引 a 指向的二層索引
  2. 將索引 a 指向的中間節點索引塊加載到內存,然後通過二分查找定位到 b 在 index a 和 c 之間,接下來訪問索引 a 指向的葉子節點。
  3. 將索引 a 指向的中間節點索引塊加載到內存,通過二分查找定位找到 b 在 index a 和 b 之間,最後需要訪問索引b指向的數據塊節點。
  4. 將索引 b 指向的數據塊加載到內存,通過遍歷的方式找到對應的 keyvalue 。

上面的流程一共IO了三次,HBase提供了一個BlockCache,是用在第4步緩存數據塊,可以有一定概率免去隨後一次IO。

相關配置:

hfile.data.block.size(默認64K):同樣的數據量,數據塊越小,數據塊越多,索引塊相應的也就越多,索引層級就越深

hfile.index.block.max.size(默認128K):控制索引塊的大小,索引塊越小,需要的索引塊越多,索引的層級越深

Meta Block (可選的)

保存用戶自定義的kv對,可以被壓縮。比如booleam filter就是存在元數據塊中的,該塊只保留value值,key值保存在元數據索引塊中。每一個元數據塊由塊頭和value值組成。可以快速判斷key是都在這個HFile中。

meta block index (可選的)

Meta Block的索引。

File Info ,Hfile的元信息

不被壓縮,用戶也可以在這一部分添加自己的元信息。

Trailer (記錄起始位置)

記錄了HFile的基本信息、偏移值和尋址信息
Trailer Block

  • version
    最先加載到內存的部分,根據version確定Trailer長度,再加載整個Trailer block
  • LoadOnOpenDataOffset
    load-on-open區的偏移量(便於將其加載到內存)
  • FirstDataBlockOffset:HFile中第一個Block的偏移量
  • LastDataBlockOffset:HFile中最後一個Block的偏移量
  • numEntries:HFile中kv總數

另外:Bloom filter相關的Block我準備專門寫一篇文章,因為Bloom filter這個東西在分布式系統中非常常見而且有用,現在需要知道的是它是用來快速判斷你需要查找的rowKey是否存在於HFile中(一堆的rowKey中)

重點來了!

看完上面的內容我們就可以解決文章開始提出的問題了:

HBase讀文件細粒度的過程?

HBase隨機讀寫快除了MemStore之外的原因?

這兩個問題我一起回答。

0.這裏從找到對應的Region開始說起,前面的過程可以看上一篇文章。

1.首先用MemStoreScanner搜索MemStore裏是否有所查的rowKey(這一步在內存中,很快),

2.同時也會用Bloom Block通過一定算法過濾掉大部分一定不包含所查rowKey的HFile,

3.上面提到在RegionServer啟動的時候就會把Trailer,和Load-on-open-section裏的block先後加載到內存,

所以接下來會查Trailer,因為它記錄了每個HFile的偏移量,可以快速排除掉剩下的部分HFile。

4.經過上面兩步,剩下的就是很少一部分的HFile了,就需要根據Index Block索引數據(這部分的Block已經在內存)快速查找rowkey所在的block的位置;

5.找到block的位置後,檢查這個block是否在blockCache中,在則直接去取,如果不在的話把這個block加載到blockCache進行緩存,

當下一次再定位到這個Block的時候就不需要再進行一次IO將整個block讀取到內存中。

6.最後掃描這些讀到內存中的Block(可能有多個,因為有多版本),找到對應rowKey返回需要的版本。

另外,關於blockCache很多人都理解錯了,這裏要註意的是:

blockCache並沒有省去掃描定位block這一步,只是省去了最後將Block加載到內存的這一步而已。

這裏又引出一個問題,如果BlockCache中有需要查找的rowKey,但是版本不是最新的,那會不會讀到臟數據?

HBase是多版本共存的,有多個版本的rowKey那說明這個rowKey會存在多個Block中,其中一個已經在BlockCache中,則省去了一次IO,但是其他Block的IO是無法省去的,它們也需要加載到BlockCache,然後多版本合並,獲得需要的版本返回。解決多版本的問題,也是rowKey需要先定位Block然後才去讀BlockCache的原因。

技術分享圖片

HBase篇(4)-你不知道的HFile