HBase读链路分析
簡介:HBase的存儲引擎是基于LSM-Like樹實現(xiàn)的,更新操作不會直接去更新數(shù)據(jù),而是使用各種type字段(put,delete)來標記一個新的多版本數(shù)據(jù),采用定期compaction的形式來歸檔合并數(shù)據(jù)。這種數(shù)據(jù)結(jié)構(gòu)將寫操作變得非常簡單且高效,但是卻給讀造成了很大的困擾。讀取過程需要根據(jù)列族讀取不同HFile中的數(shù)據(jù);還需要根據(jù)版本進行過濾,同時對已經(jīng)標記刪除的數(shù)據(jù)也要進行過濾;硬盤中的數(shù)據(jù)與MemStore中的數(shù)據(jù)重合時,還需要執(zhí)行合并,最后在內(nèi)存中拼接成一行完整的數(shù)據(jù)再向上返回。 本文粗粒度地展示了HBase的讀取鏈路,歡迎一起探討交流~
正文之前
在講HBase的讀路徑時,我們先來看幾個簡單的類圖。
InternalScanner是一個Interface主要提供了兩個方法,next(List<Cell> result)方法——獲取下一行的數(shù)據(jù)。而next(List<Cell> result, ScannerContext scannerContext)提供功能相同,只不過允許傳入一個ScannerContext用以記錄當前scan任務(wù)的上下文,判斷是否可以提前結(jié)束、是否要去讀下一列、是否要去讀下一行等。并且發(fā)生在InternalScanner中的數(shù)據(jù)比較等操作,都是基于byte[](而不用先轉(zhuǎn)化為RowResults),更加接近于數(shù)據(jù)在物理上的存儲形式,可以獲得更高的性能。
KeyValueScanner也是一個接口,換成CellScanner可能更容易理解。對,它主要提供在一個“可讀取的對象上”,獲取cell的能力。這里使用“可讀取的對象”這個詞,主要是因為它可以是一個物理概念上的HFile,但也可以是邏輯意義上有迭代讀取能力的scanner。
最后一個關(guān)鍵的類就是KeyValueHeap,該類實現(xiàn)了KeyValueScanner與InternalScanner接口,具備了獲取cell及獲取行的能力。KeyValueHeap中還有一個關(guān)鍵的屬性,為heap,它是一個PriorityQueue<KeyValueScanner>對象,comparator = CellComparatorImp(即按照key的格式:rowkey:family:qualifier:timestamp)。即KeyValueHeap允許傳入多個KeyValueScanner,通過PriorityQueue的形式將這些scanner管理起來,向上提供獲取cell及獲取行數(shù)據(jù)的能力!
有了InternalScanner,KeyValueScanner和KeyValueHeap其實已經(jīng)可以做很多事情了。
我們知道,HBase的查詢抽象地來看的話,是表現(xiàn)為下面這個流程的:
即從不同的HFile中進行數(shù)據(jù)讀取,在內(nèi)存中進行一個MergeSort,拼接成一行數(shù)據(jù)向上返回。
你們看KeyValueScanner、InternalScanner是不是就像其負責中HFile的讀取Scanner,而KeyValueHeap負責的其實就是圖中的MergeSort的任務(wù)。KeyValueHeap控制著下層KeyValueScanner、InternalScanner的數(shù)據(jù)讀取,KeyValueScanner、InternalScanner是真正讀取數(shù)據(jù)的Scanner。
好,大體的流程思路已經(jīng)講清楚了。其實HBase的讀取流程遠比這復(fù)雜,涉及的對象也更多,但有了上面的基礎(chǔ)相信可以理解得很容易,接下來我們來仔細看看HBase的讀取流程。
正文
我們從RegionScanner出發(fā),仔細看看HBase的讀取流程。
上圖中的RegionScanner主要靠成員變量storeHeap,joinedHeap(KeyValueHeap)進行數(shù)據(jù)讀取迭代。而StoreScanner也不是一個單純的Scanner,而是扮演了跟RegionScanner類似的角色,它也擁有自己的heap,以此來進行數(shù)據(jù)的讀取。跟【正文之前】說的一樣,KeyValueHeap控制著下層KeyValueScanner、InternalScanner的數(shù)據(jù)讀取,KeyValueScanner、InternalScanner是真正讀取數(shù)據(jù)的Scanner。只不過RegionScanner中多嵌了一層StoreScanner(KeyValueHeap),變成了這樣的調(diào)用鏈路:KeyValueHeap(RegionScanner)->KeyValueHeap(StoreScanner)
->KeyValueScanner,InternalScanner(StoreFileScanner及SegmentScanner)。
為什么HBase要這樣封裝?
其實是為了抽象不同的功能。
簡單來說,
1)StoreScanner是為了聯(lián)合StoreFileScanner與SegmentScanner向上提供整行的數(shù)據(jù)迭代讀取功能。
2)而RegionScanner,一方面是對獲取的數(shù)據(jù)做了過濾功能,另一方面是為了將全部數(shù)據(jù)分為兩段獲取形式(storeHeap和joinedHeap),用以優(yōu)化性能。因為從storeHeap中獲取的數(shù)據(jù)如果會被過濾,那么就沒有必要再獲取joinedHeap中的數(shù)據(jù)了。
詳細內(nèi)容我們見下文。
HBase的讀取任務(wù)開始之前需要構(gòu)建初始的Scanner體系,涉及RegionScanner與StoreScanner的對象初始化,我們詳細來看:
1)RegionScanner對象的初始化:
1.建立RegionScanner對象,準備開始Scan任務(wù)涉及的所有Scanner的生成。
2.根據(jù)scan任務(wù)涉及的所有column family,在本region上分別會為其中的每個column family生成一個StoreScanner。如果開啟了on-demand column family loading,那么會根據(jù)傳入FilterList的isFamilyEssential方法進行判斷,如果isFamilyEssential,那么會將該StoreScanner放入storeHeap中,否則放入joinedHeap中。
3.storeHeap和joinedHeap中存放StoreScanner的形式為PriorityQueue,優(yōu)先級為CellComparatorImp。
2)StoreScanner對象的初始化
接下來我們介紹RegionScanner對象的初始化中,我們一筆帶過的StoreScanner的生成過程:
1.根據(jù)scan.isReversed()控制StoreScanner中的Scanner的優(yōu)先級順序。
2.根據(jù)傳入的scan信息,生成matcher內(nèi)置對象,該對象在查詢過程中會對StoreScanner讀取的數(shù)據(jù)進行一個篩選。
3.根據(jù)scan信息startRow,stopRow在storeEngine中查詢出涉及的HStoreFile,對這些HStoreFile分別建立StoreFileScanner,組成scannerList,并且以StoreFileComparators.SEQ_ID為優(yōu)先級(maxSequenceId升序,FileSize降序,BulkTime升序,PathName升序)。
4.對scannerList根據(jù)timestamp range, row key range, bloomFilter做一個過濾。
5.scannerList中剩余的scanner根據(jù)startRow,stopRow將指針seek到正確的位置。
6.將scanners以PriorityQueue的形式組織,優(yōu)先級同樣為CellComparatorImp。
PS:StoreFileComparators.SEQ_ID —— Comparator.comparingLong(HStoreFile::getMaxSequenceId) ?.thenComparing(Comparator.comparingLong(new GetFileSize()).reversed()) ?.thenComparingLong(new GetBulkTime()).thenComparing(new GetPathName())
組建好需要Scanner體系之后,后續(xù)就是讀取流程了。
讀取流程如下圖所示:
RegionScanner主要負責以下功能:
其包含storeHeap與joinedHeap都為KeyValueHeap的對象實例,heap底層是包含了多個StoreScanner組成的PriorityQueue,comparator = CellComparatorImp。向上提供符合條件的整行數(shù)據(jù)的迭代查詢。
1.循環(huán)從storeHeap上獲取cell數(shù)據(jù),以此判斷是否還存在待獲取數(shù)據(jù)。如果沒有,return false。如果有:
2.那么先從storeHeap上獲取family essential相關(guān)的數(shù)據(jù),使用filter進行過濾。如果被過濾,continue loop。如果沒有:
3.那么從joinedHeap上獲取剩余數(shù)據(jù),返回。
StoreScanner主要負責以下功能:
StoreScanner雖然是實現(xiàn)了KeyValueScanner和InternalScanner的類,但主要靠其成員變量heap(KeyValueHeap)來完成必要的操作。heap由多個StoreFileScanner實例按照PriorityQueue組成,comparator = CellComparatorImp。
1.循環(huán)從heap中獲取cell。
2.通過matcher匹配cell獲得返回的MatchCode,不同MatchCode會觸發(fā)不同的操作,見下表。
?3.不停循環(huán),直到數(shù)據(jù)組成整行,向上返回。
StoreScanner中KeyValueHeap的next功能:
storeScanner中的heap.next()究竟做了什么?簡單來說,做了以下兩件事情:1)從current(當前的StoreFileScanner,不在heap中)獲取cell返回。2)更新當前current,把current放回heap重新排序,再獲取當前最優(yōu)先的StoreFileScanner作為current。
具體做法如下:
1.從當前的StoreFileScanner current中獲取下一個cell(kvReturn)。再獲取kvReturn往后的第一個cell(kvNext)
2.判斷kvNext是否為空。為空代表當前current讀取完畢,需要從heap中獲取下一個scanner記為current。不為空則
3.從當前heap中獲取第一個scanner,與current 進行對比。判斷它們誰通過peek()獲得的cell key最小,如果scanner更小,那么把current放回heap。重新heap.poll()獲得最新current。
4.返回kvReturn cell。
至此整個HBase的讀路徑分析結(jié)束,留待補充的點:
1.Matcher的實現(xiàn)邏輯分析。
2.BloomFilter的過濾分析。
3.StoreFileScanner以下直到HDFS之間的鏈路分析,中間涉及一個BlockCache。
原文鏈接
本文為阿里云原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。?
總結(jié)
以上是生活随笔為你收集整理的HBase读链路分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 双11专刊|云原生数据仓库Analyti
- 下一篇: 一文搞懂物联网Modbus通讯协议