hbase(二)hfile結構
HFile結構
截止hbase 1.0.2版本,hfile已經有3個版本,要深入了解hfile的話,還是要從第一個版本開始看起。
hfile v1
Data Block:保存表中的數據,這部分可以被壓縮
Meta Block:(可選)保存用戶自定義的kv對,可以被壓縮。
File Info :Hfile的meta元信息,不被壓縮,定長。
Data Block Index :Data Block的索引。每個Data塊的起始點。
Meta Block Index:(可選的)Meta Block的索引,Meta塊的起始點。
Trailer: 定長。保存了每一段的偏移量,讀取一個HFile時,會首先讀取Trailer,Trailer有指針指向其他數據塊的起始點,保存了每個段的起始位置(段的Magic Number用來做安全check),然後,DataBlock Index會被讀取到內存中,這樣,當檢索某個key時,不需要掃描整個HFile,而只需從內存中找到key所在的block,通過一次磁盤io將整個block讀取到內存中,再找到需要的key。DataBlock Index采用LRU機制淘汰。
HFile的Data Block,Meta Block通常采用壓縮方式存儲。Data Block是HBase I/O的基本單元,為了提高效率,HRegionServer中有基於LRU的Block Cache機制。每個Data塊的大小可以在創建一個Table的時候通過參數指定,大號的Block有利於順序Scan,小號Block利於隨機查詢。每個Data塊除了開頭的Magic以外就是一個個KeyValue對拼接而成, Magic內容就是一些隨機數字,目的是防止數據損壞。
註意,block是hfile的最小壓縮和編碼數據塊,默認128KB
第一版的block index是非常簡單的,註意只有兩個block index,一個是data block index,一個是meta block index,block index包括了以下幾點:
1. offset
2. Uncompressed size
3. Key (a serialized byte array written using Bytes.writeByteArray)
3.1 Key length as a variable-length integer (VInt)
3.2 Key bytes
block index的數量會存放在trailer,這樣才能讀取到block index數據。
這個版本的block index有1個缺點
1. 無法知道block壓縮後的數據大小,這在之後的解壓過程是必要的
所以在版本2,解決了這個問題,在block index中增加了實際存儲block大小的數據
hfile v2
在接口層面做了兼容,在讀hfile的時候,支持v1和v2,在寫hfile的時候,只會寫v2版本的hfile。
hfile升級的原因:
1. v1 的設計導致了region server啟動時間很長,需要加載很大的數據量,比如大量的bloom filter,大量的block index
為了解決這個問題,v2使得hfile增加了新特性,把bloom filter和block index打散,寫到多個block中去,這樣就減少了hfile 寫入時候的內存offset。並且這些打散的block index會有預定的長度。
另一方面,v2還用到了 load-on-open 這個概念,意思是說,在打開hfile的時候,加載那些必要的信息,包括trailer,trailer裏記錄了hfile的必要信息。而其他數據就可以再用到的時候,通過trailer再解析出來
下圖為hfile v2版本的結構
與V1版本的相比,它的區別在於
1)文件分為三部分:Scanned block
section,Non-scanned block section,以及load-on-open
2) multi block index,為DataBlockIndex建立多層索引。DataBlockIndex分為Leaf Index Block、Root Data Index(或者multi Root Data index(紫色的Meta Index區域)),Leaf index block具體存儲了DataBlock的offset、length、以及firstkey的信息。RootDataIndex 存儲的是每個Leaf index block的offset、length、Leaf index Block記錄的第一個key,以及截至到該Leaf Index Block記錄的DataBlock的個數。假定DataBlock的個數足夠多,HFile文件又足夠大的情況下,默認的128KB的長度的ROOTDataIndex仍然存在超過chunk大小的情況時,會分成更多的層次。這樣最終的可能是ROOT INDEX –> IntermediateLevel ROOT INDEX(可以是多層) -> Leaf index block,由此形成多級索引,在提高hfile的初始化加載速度的同時不影響對數據的查找性能,另外在ROOT INDEX中會記錄Mid Key所對應的信息,幫助在做File Split或者折半查詢時快速定位中間Row的信息。
簡單來說,多級索引的目的是為了解決hfile過大導致block index過大。所以將block index分為root block index和non-root block index,分開存儲。
關於midkey
hfile在存midkey的時候采用了shortKey的思路,比如上個block最後一個rowkey為"the quick brown fox", 當前block第一個rowkey為"the who", 那麽我們可以用"the r"來作為midkey,和hbase rowkey的scan規則保持一致
存儲短一點的虛擬midkey有兩個好處:
1. 減少index部分的存儲空間,因為自定義的rowkey可以會出現幾KB這樣極端的長度,精簡過後,只需要幾個字節
2. 采用與上一個block的最後一個rowkey更接近的虛擬key作為midkey,可以避免潛在的io浪費。如果midkey采用當前block的第一個rowkey,那麽當查詢的rowkey比midkey小但是比上一個block的最後一個rowkey大時,會去遍歷上一個block,這就出現了無用功。而midkey更接近上一個block的最後一個rowkey時,可以在很大程度上避免這個問題,即直接返回該rowkey不在此hfile中。
// 很詳細的中文翻譯及解析
http://wangneng-168.iteye.com/blog/2164299
// 官方對hfile的介紹
https://hbase.apache.org/book.html#_hfile_format_2
hfile v3
增加安全方面特性,為cell級別增加ACL
hifle v3不和hfilev1和v2兼容,因為在存儲keyvalue的時候,會額外的存儲tags,用於控制ACL
總結
hbase的三個版本的hfile在文件結構層面逐漸完善,每個hfile文件都有自己獨立的索引。理解這些結構對實際應用業務優化也是很有好處的。例如有很多場景需要多hbase的resion進行遍歷而內存資源又有限的情況,假設計算引擎用的是spark,那麽每個分區遍歷一個region是不可能的,因為數據量太多,資源吃不下,而根據timestamp過濾分批遍歷的話,性能有影響,相當於每個region都要被重復遍歷。這種場景下,可以對region按照rowkey分割,每個spark分區只遍歷一個region的部分rowkey,這樣就可以無限拆分下去,再不也不用擔心資源的問題。不過這就又出現了一個問題,如果用rowkey切割region,首先,rowkey如果設計的好的話,不同rowkey段的數據很均勻,那麽可以直接根據業務切分。而如果無法評估出不同rowkey段和數據的對應關系,那麽這個時候,可以利用到hfile。通過查看region下hfile文件中每個datablock的第一個rowkey,(由於datablock的index都是被保存在hfile的,所以datablock可被直接定位),用該rowkey切割region,並且可以以最小block的粒度來控制每批遍歷region的數據量,這個粒度相信資源是完全夠用的。
hbase(二)hfile結構