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