1 LRU Cache
LRUBlockCache是目前hbase默認的BlockCache機制裸影,實現(xiàn)機制也比較簡單膝宁,是使用一個ConcurrentHashMap管理BlockKey到Block的映射關(guān)系宛徊,緩存Block只需要將BlockKey和對應(yīng)的Block放到該HashMap中呆细,查詢緩存就根據(jù)BlockKey從HashMap中獲取即可。同時該方案采用嚴格的LRU淘汰算法外盯,當BlockCache總量達到一定閾值之后就會啟動淘汰機制麸澜,最近最少使用的Block會被置換出來挺尿。在具體的實現(xiàn)細節(jié)方面,需要關(guān)注三點:
- 1.緩存分層策略
Hbase在LRU緩存基礎(chǔ)上炊邦,采用了緩存分層設(shè)計编矾,將整個BlockCache分為三個部分:single-access、mutil-access和inMemory馁害。需要注意的是窄俏,Hbase系統(tǒng)的元數(shù)據(jù)放在InMemory區(qū),因此設(shè)置數(shù)據(jù)屬性InMemory=true需要非常謹慎碘菜,確保此列簇數(shù)據(jù)量很小且訪問頻繁凹蜈,否則可能將hbase.meta元數(shù)據(jù)基礎(chǔ)內(nèi)存怒详,嚴重影響所有業(yè)務(wù)性能。 - 2.LRU淘汰算法實現(xiàn)
系統(tǒng)在每次cache block時將BlockKey和Block放入hashmap后會檢查BlockCache總量是都達到閾值踪区,如果達到閾值,就會緩解淘汰線程對Map中的Block進行淘汰吊骤。系統(tǒng)設(shè)置了三個MinMaxPiorityQueue隊列缎岗,分別對應(yīng)上述三個分層,每個隊列中的原始按照最近最少使用排列白粉,系統(tǒng)會優(yōu)先poll出最近最少使用的元素传泊,將其對應(yīng)的內(nèi)存釋放.可見,三個分層中的Block會分別執(zhí)行LRU淘汰算法進行淘汰鸭巴。 - 3.LRU方案優(yōu)缺點
LRU方案使用JVM提供的HashMap管理緩存眷细,簡單有效。但隨著數(shù)據(jù)從single-access區(qū)晉升到mutil-access區(qū)鹃祖,基本上就伴隨著對應(yīng)的內(nèi)存對象從young區(qū)到old區(qū)溪椎,晉升到old區(qū)的Block被淘汰后會變?yōu)閮?nèi)存垃圾,最終由CMS回收掉,會造成性能問題恬口。由于此校读,BucketCache方案才會橫空出世。
2 Bucket Cache
相比LRUBlockCache祖能,BucketCache實現(xiàn)相對比較復(fù)雜歉秫。它沒有使用jvm內(nèi)存管理算法來管理緩存,而是自己對內(nèi)存進行管理养铸,因此不會出現(xiàn)因為出現(xiàn)大量碎片導(dǎo)致Full GC的情況發(fā)生雁芙。本節(jié)主要介紹BucketCache的具體實現(xiàn)方式(包括BucketCache的內(nèi)存組織形式、緩存寫入讀取流程等)以及如何配置使用BucketCache钞螟。
2.1內(nèi)存組織形式
下圖是BucketCache的內(nèi)存組織形式圖兔甘,其中上面部分是邏輯組織結(jié)構(gòu),下面部分是對應(yīng)的物理組織結(jié)構(gòu)鳞滨。Hbase啟動之后會在內(nèi)存中申請大量的bucket裂明,如下圖中黃色舉行所示,每個bucket的大小默認都為2MB太援。每個bucket會有一個baseoffset變量和一個size標簽闽晦,其中baseoffset變量表示這個bucket在實際物理空間中的起始地址,因此block的物理地址就可以通過baseoffset和該block在bucket的偏移量唯一確定提岔;而size標簽表示這個bucket可以存放的block塊的大小仙蛉,比如圖中左側(cè)bucket的size標簽為65KB,表示可以存放64KB的block碱蒙,右側(cè)bucket的size標簽為129KB荠瘪,表示可以存放128KB的block夯巷。
Hbase中使用BucketAllocator類實現(xiàn)對Bucket的組織管理:
Hbase會根據(jù)每個bucket的size標簽對bucket進行分類,相同size標簽的bucket由同一個BucketSizeInfo管理哀墓,如上圖趁餐,左側(cè)存放64KB block的bucket有65KB BucketSizeInfo管理,右側(cè)存放128KB block有129KB BucketSizeInfo管理篮绰。
-
hbase在啟動的時候就決定了size標簽分類后雷,默認標簽有(4+1)K、(8+1)K吠各、(16+1)K … (48+1)K臀突、(56+1)K、(64+1)K贾漏、(96+1)K … (512+1)K候学。而且系統(tǒng)會首先從小到大遍歷一次所有size標簽,為每種size標簽分配一個bucket纵散,最后所有剩余的bucket都分配最大的size標簽梳码,默認分配 (512+1)K,如下圖所示:
Bucket的size標簽可以動態(tài)調(diào)整伍掀,比如64K的block數(shù)目比較多边翁,65K的bucket被用完以后,其他size標簽的完全空閑的bucket可以轉(zhuǎn)換為65K的bucket硕盹,但是至少保留一個該size的bucket符匾。
3 Block緩存寫入、讀取流程
下圖是block寫入緩存以及從緩存中讀取block的流程示意圖瘩例,圖中主要包括5個模塊啊胶,其中RAMCache是一個存儲blockkey和block對應(yīng)關(guān)系的hashmap;WriteThread是整個block寫入的中心樞紐垛贤,主要負責一異步寫入block到內(nèi)存空間焰坪;BucketAllocator在上一節(jié)詳細介紹過,主要實現(xiàn)對bucket的組織管理聘惦,為block分配內(nèi)存空間某饰;IOEngine是具體的內(nèi)存管理模塊,主要實現(xiàn)將block數(shù)據(jù)寫入對應(yīng)地址的內(nèi)存空間中善绎;BackingMap也是一個HashMap黔漂,用來存儲blockKey與對應(yīng)物理內(nèi)存偏移量的映射關(guān)系,用來根據(jù)blockkey定位具體的block禀酱;其中紫線表示cache block流程炬守,綠線表示get block流程。
Block緩存寫入流程
- 將block寫入RAMCache剂跟。實際實現(xiàn)中减途,HBase設(shè)置了多個RAMCache酣藻,系統(tǒng)首先會根據(jù)blockkey進行hash,根據(jù)hash結(jié)果將block分配到對應(yīng)的RAMCache中鳍置;
- WriteThead從RAMCache中取出所有的block辽剧。和RAMCache相同,HBase會同時啟動多個WriteThead并發(fā)的執(zhí)行異步寫入税产,每個WriteThead對應(yīng)一個RAMCache;
- BucketAllocator會選擇與block大小對應(yīng)的bucket進行存放(具體細節(jié)可以參考上節(jié)‘內(nèi)存組織形式’所述)怕轿,并且返回對應(yīng)的物理地址偏移量offset;
- WriteThead將block以及分配好的物理地址偏移量傳給IOEngine模塊砖第,執(zhí)行具體的內(nèi)存寫入操作;
- 寫入成功后环凿,將類似<blockkey,offset>這樣的映射關(guān)系寫入BackingMap中梧兼,方便后續(xù)查找時根據(jù)blockkey可以直接定位;
Block緩存讀取流程 - 首先從RAMCache中查找智听。對于還沒有來得及寫入到bucket的緩存block羽杰,一定存儲在RAMCache中;
- 如果在RAMCache中沒有找到到推,再在BackingMap中根據(jù)blockKey找到對應(yīng)物理偏移地址offset考赛;
- 根據(jù)物理偏移地址offset可以直接從內(nèi)存中查找對應(yīng)的block數(shù)據(jù)。