一條數(shù)據(jù)HBase之旅,簡明HBase入門教程14:Scan全流程

      網(wǎng)友投稿 1049 2022-05-29

      華為云上的NoSQL數(shù)據(jù)庫服務CloudTable,基于Apache HBase,提供全托管式集群服務,集成了時序數(shù)據(jù)庫OpenTSDB與時空數(shù)據(jù)庫GeoMesa,在TB/PB級別的海量數(shù)據(jù)背景下,可提供ms級查詢以及千萬級TPS,點我了解詳情。

      Client發(fā)送讀取請求到RegionServer

      無論是Get,還是Scan,Client在發(fā)送請求到RegionServer之前,也需要先獲取路由信息:

      1.定位該請求所關聯(lián)的Region

      因為Get請求僅關聯(lián)一個RowKey,所以,直接定位該RowKey所關聯(lián)的Region即可。

      對于Scan請求,先定位Scan的StartRow所關聯(lián)的Region。

      2.往該Region所關聯(lián)的RegionServer發(fā)送讀取請求

      該過程與《一條數(shù)據(jù)的HBase之旅,簡明HBase入門教程8 - 數(shù)據(jù)路由與分組打包》的"數(shù)據(jù)路由"章節(jié)所描述的流程類似,不再贅述。

      如果一次Scan涉及到跨Region的讀取,讀完一個Region的數(shù)據(jù)以后,需要繼續(xù)讀取下一個Region的數(shù)據(jù),這需要在Client側(cè)不斷記錄和刷新Scan的進展信息。如果一個Region中已無更多的數(shù)據(jù),在scan請求的響應結果中會帶有提示信息,這樣可以讓Client側(cè)切換到下一個Region繼續(xù)讀取。

      RegionServer如何處理讀取請求

      關于Read的命題

      通過前面的文章,我們已經(jīng)知道了如下這些信息:

      1.一個表可能包含一個或多個Region

      將HBase中擁有數(shù)億行的一個大表,橫向切割成一個個”子表“,這一個個”子表“就是Region。

      2.每一個Region中關聯(lián)一個或多個列族

      如果將Region看成是一個表的橫向切割,那么,一個Region中的數(shù)據(jù)列的縱向切割,稱之為一個Column Family。每一個列,都必須歸屬于一個Column Family,這個歸屬關系是在寫數(shù)據(jù)時指定的,而不是建表時預先定義。

      3.每一個列族關聯(lián)一個MemStore,以及一個或多個HFiles文件

      上面的關于“Region與多列族”的圖中,泛化了Column Family的內(nèi)部結構。下圖是包含MemStore與HFile的Column Family組成結構:

      HFile數(shù)據(jù)文件存在于底層的HDFS中,上圖中只是為了方便闡述HFile與Column Family之間的關系。

      一條數(shù)據(jù)的HBase之旅,簡明HBase入門教程14:Scan全流程

      在HBase的源碼實現(xiàn)中,將一個Column Family抽象成一個Store對象??梢赃@么簡單理解Column Family與Store的概念差異:Column Family更多的是面向用戶層可感知的邏輯概念,而Store則是源碼實現(xiàn)中的概念,是關于一個Column Family的抽象。

      4.每一個MemStore中可能涉及一個Active Segment,以及一個或多個Immutable Segments

      擴展到一個Region包含兩個Column Family的情形:

      5.HFile由Block構成,默認地,用戶數(shù)據(jù)被按序組織成一個個64KB的Block

      HFile V1的結構雖已過時,但非常有助于你理解HFile的核心設計思想:

      Data Block(上圖中左側(cè)的Data塊):保存了實際的KeyValue數(shù)據(jù)。

      Data Index:關于Data Block的索引信息。

      HFile V2只不過在HFile V1基礎上做的演進,將Data Index信息以及BloomFilter的數(shù)據(jù)也分成了多層。

      當前階段,你只需要了解到:基于一個給定的RowKey,HFile中提供的索引信息能夠快速查詢到對應的Data Block。

      在重新溫習了上述內(nèi)容以后,我們也大致了解了關于HBase讀取我們所面臨的問題是什么。關于HBase Read的命題可以定義為:如何從包含1個或多個列族(每個列族包含1個或多個MemStore Segments,以及1個或多個HFiles)的Region中讀取用戶所期望的數(shù)據(jù)?默認情況下,這些數(shù)據(jù)必須是未被標記刪除的、未過期的而且是最新版本的數(shù)據(jù)。

      將Get看作一類特殊的Scan

      無論是讀取一行數(shù)據(jù),還是讀取指定RowKey范圍的讀取一系列數(shù)據(jù),所面臨的問題其實是類似的,因此,可以將Get看作是一種特殊的Scan,只不過它的StartRow與StopRow重疊,事實上,RegionServer側(cè)處理Get請求時的確先將Get先轉(zhuǎn)換成了一個Scan操作。

      合理組織所有的KeyValue數(shù)據(jù)源

      在Store/Column Family內(nèi)部,KeyValue可能存在于MemStore的Segment中,也可能存在于HFile文件中,無論是Segment還是HFile,我們統(tǒng)稱為KeyValue數(shù)據(jù)源。

      在本文的第一部分介紹如何執(zhí)行Scan操作時,我們講到了Client側(cè)使用一個ResultScanner來抽象地描述一次Scan操作,ResultScanner屏蔽掉了往RegionServer發(fā)送請求以及一個Region讀取完成以后切換到下一個Region等細節(jié)信息。

      初次閱讀RegionServer/Region的讀取流程所涉及的源碼時,會被各色各樣的Scanner類整的暈頭轉(zhuǎn)向,HBase使用了各種Scanner來抽象每一層/每一類KeyValue數(shù)據(jù)源的Scan操作:

      關于一個Region的讀取,被封裝成一個RegionScanner對象。

      每一個Store/Column Family的讀取操作,被封裝在一個StoreScanner對象中。

      SegmentScanner與StoreFileScanner分別用來描述關于MemStore中的Segment以及HFile的讀取操作。

      StoreFileScanner中關于HFile的實際讀取操作,由HFileScanner完成。

      RegionScanner的構成如下圖所示:

      在StoreScanner內(nèi)部,多個SegmentScanner與多個StoreFileScanner被組織在一個稱之為KeyValueHeap的對象中:

      每一個Scanner內(nèi)部有一個指針指向當前要讀取的KeyValue,KeyValueHeap的核心是一個優(yōu)先級隊列(PriorityQueue),在這個PriorityQueue中,按照每一個Scanner當前指針所指向的KeyValue進行排序:

      // 用來組織所有的Scanner

      protected PriorityQueue heap = null;

      // PriorityQueue當前排在最前面的Scanner

      protected KeyValueScanner current = null;

      同樣的,RegionScanner中的多個StoreScanner,也被組織在一個KeyValueHeap對象中:

      KeyValueScanner接口

      KeyValueScanner定義了讀取KeyValue的基礎接口:

      /**

      * 查看當前Scanner中當前指針位置的KeyValue,該接口不會移動指針.

      */

      Cell peek();

      /**

      * 返回Scanner當前指針位置的KeyValue,而后移動指針到下一個KeyValue.

      */

      Cell next() throws IOException;

      /**

      * 將當前Scanner的指針定位到指定的KeyValue的位置,如果不存在,則定位到

      * 比該Cell大的下一個KeyValue位置.Seek操作會從當前的HFile Block的開

      * 始位置查找.

      */

      boolean seek(Cell key) throws IOException;

      /**

      * 與seek接口類似,也是將當前Scanner的指針定位到指定的KeyValue的位置,

      * 如果不存在,則定位到比該KeyValue大的下一個KeyValue位置.與seek接口不

      * 同點在于,該操作會從上一次讀到的HFile Block的位置開始查找.

      */

      boolean reseek(Cell key) throws IOException;

      /**

      * 與seek/reseek類似,但不同點在于采用了Lazy Seek機制來降低磁盤IO請求,

      * 其原理在于充分利用Bloom Filter的判斷結果,以及待Seek的KeyValue與該

      * Scanner中最大時間戳的對比,減少一些不必要的Seek操作

      */

      boolean requestSeek(Cell kv, boolean forward,??boolean useBloom) throws IOException;

      實現(xiàn)了KeyValueScanner接口類的主要Scanner包括:

      StoreFileScanner

      SegmentScanner

      StoreScanner

      RegionScanner初始化

      RegionScanner初始化過程,包括幾個關鍵操作:

      1.獲取ReadPoint

      ReadPoint決定了此次Scan操作能看到哪些數(shù)據(jù)。Scan過程中新寫入的數(shù)據(jù),對此次Scan是不可見的。

      2.按需選擇對應的Store,并初始化對應的StoreScanner

      StoreScanner在初始化的時候,也會按需選擇對應的SegmentScanner以及StoreFileScanner,篩選規(guī)則包括:

      如果一次Scan操作指定了Time Range,則只選擇與該Time Range有關的Scanners。

      對于Get操作,可以通過BloomFilter過濾掉不符合條件的Scanners。

      StoreScanner中篩選除了Scanner以后,會將每一個Scanner seek到Scan的StartRow位置:

      通過next請求讀取一行行數(shù)據(jù)

      如果將RegionScanner理解成一個內(nèi)部構造復雜的機器,而驅(qū)動這個機器運轉(zhuǎn)的動力源自Client側(cè)的一次次scan請求,scan請求通過調(diào)用RegionScanner的next方法來獲取一行行結果。

      為了簡單的解釋該流程,我們先假定一個RegionScanner中僅包含一個StoreScanner,那么,這個RegionScanner中的核心讀取操作,是由StoreScanner完成的,我們進一步假定StoreScanner由4個Scanners組成(我們泛化了SegmentScanner與StoreFileScanner的區(qū)別,統(tǒng)稱為Scanner),直觀起見,在下圖中我們使用了四種不同的顏色:

      每一個Scanner中都有一個current指針指向下一個即將要讀取的KeyValue,KeyValueHeap中的PriorityQueue正是按照每一個Scanner的current所指向的KeyValue進行排序。

      第一次next請求,將會返回ScannerA中的Row01:FamA:Col1,而后ScannerA的指針移動到下一個KeyValue?Row01:FamA:Col2,PriorityQueue中的Scanners排序依然不變:

      第二次next請求,依然返回ScannerA中的Row01:FamA:Col2,ScannerA的指針移動到下一個KeyValue Row02:FamA:Col1,此時,PriorityQueue中的Scanners排序發(fā)生了變化:

      下一次next請求,將會返回ScannerB中的KeyValue…..周而復始,直到某一個Scanner所讀取的數(shù)據(jù)耗盡,該Scanner將會被close,不再出現(xiàn)在上面的PriorityQueue中。

      SegmentScanner/StoreFileScanner中返回的KeyValue,包含了各種類型的KeyValue:

      已被更新過的舊KeyValue

      已被標記刪除但尚未被及時清理的KeyValue

      已過期的尚未被及時清理的KeyValue

      用來描述一次刪除操作的KeyValue(刪除還包含了多種類型)

      承載最新用戶數(shù)據(jù)的普通KeyValue

      因此,在StoreScanner層,需要對這些KeyValue做更復雜的邏輯校驗,這些校驗由ScanQueryMatcher完成。默認地,可作為返回數(shù)據(jù)的KeyValue,應該滿足如下條件:

      KeyValue類型為Put

      KeyValue所關聯(lián)的列為用戶Scan所涉及的列

      KeyValue的時間戳符合Scan的TimeRange要求

      版本最新

      未被標記刪除

      通過了Filter的過濾條件

      上述條件,只針對一些普通的Scan,不同的Scan參數(shù)配置,可能會導致條件集發(fā)生變化,如Scan啟用了Raw Scan模式時,Delete類型的KeyValue也會被返回。另外,上面的這些條件所羅列的順序,也未遵循實際的檢查順序,而實際的檢查順序也是嚴格的,如果顛倒就可能會導致Bug。小米的同學就曾發(fā)現(xiàn)了這樣的一個Bug:

      假設某一個列共有T1~T5五個版本, Column Family中設置的MaxVersions=3(即最大允許保留的版本)

      T5 -> Value=5

      T4 -> Value=4

      T3 -> Value=3

      T2 -> Value=2

      T1 -> Value=1

      如果Scan中采用了一個SingleColumnValueFilter,要求返回滿足Value<=3的所有結果。

      因為MaxVersions為3,我們所期望的返回結果應該為:

      T5 -> Value=5?(Value不滿足條件)

      T4 -> Value=4?(Value不滿足條件)

      T3 -> Value=3

      T2 -> Value=2?(Version不滿足條件)

      T1 -> Value=1?(Version不滿足條件)

      關于多版本檢查以及Filter檢查,這里有兩種可能的順序:

      Opt 1:先檢查Filter,再檢查多版本。這種情況下的返回結果為:

      T5 -> Value=5?(Value不滿足條件)

      T4 -> Value=4?(Value不滿足條件)

      T3 -> Value=3

      T2 -> Value=2

      T1 -> Value=1

      這種情況的返回結果就是錯誤的。

      Opt 2: 先檢查多版本,再檢查Filter。這種情況下的返回結果才是預期的。

      在Scanner中,如果允許讀取多個版本(由Scan#readVersions配置),那正常的讀取順序應該為:

      上面這種讀取的順序與實際存在的數(shù)據(jù)的邏輯順序也是相同的。

      由于不同的Scan所讀取的每一行中的數(shù)據(jù)不同,有的限定了列的數(shù)量,有的限定了版本的數(shù)量,這使得讀取時可以通過一些優(yōu)化,減少不必要的數(shù)據(jù)掃描。如某次Scan在允許讀多個版本的同時,限定了只讀取C1~C3,那么,讀取順序應該為:

      最普通的Scan,其實只需要讀取每一列的最新版本即可,那讀取的順序應該為:

      通過上面幾張圖,我們其實是想說明在Scanner內(nèi)部需要具備這樣的一些基礎能力:

      如果只需要當前列的最新版本,那么Scanner應該可以跳過當前列的其它版本,而且將指針移到下一列的開始位置。

      如果當前行的所要讀取的列都已讀完,那么,Scanner應該可以跳過該行剩余的列,將指針移動到下一行的開始位置。

      我們知道KeyValueScanner定義了基礎的seek/reseek/requestSeek等接口,可以將指針移動到指定KeyValue位置。但關于指針如何移動的決策信息,由誰來提供?

      這些信息也是由ScanQueryMatcher提供的。ScanQueryMatcher對每一個KeyValue的邏輯檢查結果稱之為MatchCode,MatchCode不僅包含了是否應該返回該KeyValue的結果,還可能給出了Scanner的下一步操作的提示信息。關于它的枚舉值,簡單舉例如下:

      INCLUDE_AND_SEEK_NEXT_ROW

      包含當前KeyValue,并提示Scanner當前行已無需繼續(xù)讀取,請Seek到下一行。

      INCLUDE_AND_SEEK_NEXT_COL

      包含當前KeyValue,并提示Scanner當前列已無需繼續(xù)讀取,請Seek到下一列。

      無論是StoreScanner還是RegionScanner,返回的都是符合條件的KeyValue列表。這些KeyValues在RSRpcServices層被進一步組裝成Results響應給Client側(cè)。

      總結

      Scan涉及了太多的細節(jié)內(nèi)容,本文只粗略介紹了Scan的一些核心思路,這與本系列文章最初的定位有關,當然也受限于本文的篇幅。 本文主要介紹了如下內(nèi)容:

      介紹HBase的兩種讀取模式:Get與Scan

      Client如何發(fā)起一次Get請求,Get的關鍵參數(shù)

      Client如何發(fā)起一次Scan請求,Scan的關鍵參數(shù)

      重點介紹了RegionServer側(cè)關于Scan的處理流程

      如何用Scanner來抽象描述關于Region的讀取操作

      關于讀取KeyValue的基礎Scanner接口定義

      RegionScanner初始化時的關鍵操作

      Client側(cè)的一次次scan請求如何驅(qū)動RegionScanner內(nèi)部的讀取操作

      從StoreFileScanner/SegmentScanner中讀取出來的原始KeyValue如何被合理的校驗

      Scanner讀取時如何跳過一些不必要的數(shù)據(jù)

      關于Scan的更多細節(jié),感興趣的同學可以自己去源碼中探尋答案:

      如果第一次scan請求不能取回所有的數(shù)據(jù),下一次scan如何快速有效繼承上一次的進度?

      Get/Small Scan/Large Scan在實現(xiàn)上有何不同?

      ScanQueryMatcher中校驗KeyValue的詳細邏輯

      關于Filter涉及多步校驗,每一步校驗是在什么地方完成的?

      MinVersion與MaxVersion的定義是什么?

      ScanQueryMatcher中關于多種刪除類型的語義是如何定義的?

      如何限制一次Scan所占用的內(nèi)存大小以及執(zhí)行的時間?

      BloomFilter在Get/Scan流程中是如何被應用的?

      Scan過程中如果正在讀取的HFile文件被Compaction合并了,如何處理?

      正在Scan的Region突然被遷移到其它的RegionServer中,如何繼續(xù)原來的進度繼續(xù)讀???

      Reverse Scan與普通的Scan在實現(xiàn)上有何不同?

      HFile的內(nèi)容在本文只粗略提及,在RegionServer側(cè)的處理流程中,關于BlockCache部分更是只字未提。本文將重點放在介紹Scan的核心思路上。

      下篇文章將單獨介紹HFile的核心原理。

      hbase

      版權聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權內(nèi)容。

      上一篇:十道海量數(shù)據(jù)處理面試題與十個方法大總結(轉(zhuǎn)載)
      下一篇:MongoDB 第3章 MongoDB快速入門
      相關文章
      久久国产成人亚洲精品影院 | 亚洲精品在线播放| 亚洲小说区图片区另类春色| 亚洲一级黄色视频| 亚洲美女高清一区二区三区| 国产国拍亚洲精品福利| 亚洲伊人久久大香线蕉综合图片 | 亚洲高清美女一区二区三区| 日韩精品亚洲人成在线观看 | 久久精品亚洲中文字幕无码麻豆| 亚洲国产高清在线| 亚洲精品天天影视综合网| 亚洲av永久无码精品秋霞电影影院 | 亚洲精品无码国产| 亚洲国产成人精品无码区在线观看| 国产精品亚洲一区二区三区在线| 国产亚洲成av人片在线观看| 亚洲av丰满熟妇在线播放| 亚洲一区二区在线视频| 亚洲美女免费视频| 亚洲日本人成中文字幕| 亚洲一卡2卡三卡4卡无卡下载| 亚洲自偷自偷在线成人网站传媒| 亚洲精品无码mⅴ在线观看| 色婷婷六月亚洲综合香蕉| www国产亚洲精品久久久| 亚洲天堂中文字幕在线| 国产av无码专区亚洲av果冻传媒| 亚洲级αV无码毛片久久精品| 亚洲av无码片在线播放| 亚洲精品美女久久久久| 亚洲校园春色另类激情| 亚洲αⅴ无码乱码在线观看性色| 日本中文一区二区三区亚洲| 最新精品亚洲成a人在线观看| 亚洲AV日韩AV永久无码免下载 | 亚洲免费日韩无码系列| 久久久久久a亚洲欧洲aⅴ| 久久精品国产亚洲AV高清热 | 亚洲色欲色欲www在线丝| 亚洲va在线va天堂va888www|