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

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

這一篇你能夠知道,apache

HFile的內部結構緩存

HBase讀文件細粒度的過程分佈式

HBase隨機讀寫快除了MemStore以外的緣由oop

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

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

這一篇咱們來講下這個HFile,把路徑從HFile開始再補充一下優化

HFile->Block->KeyValue.

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

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

如何讀取HFile的內容:blog

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的緣由。

相關文章
相關標籤/搜索