HBase Region劃分策略總結
1.Region切分觸發策略
在最新穩定版(1.2.6)中,HBase已經有多達6種切分觸發策略。當然,每種觸發策略都有各自的適用場景,使用者可以根據業務在表級別選擇不同的切分觸發策略。常見的切分策略如下圖
- ConstantSizeRegionSplitPolicy:0.94版本前預設切分策略
這是最容易理解但也最容易產生誤解的切分策略,從字面意思來看,當region大小大於某個閾值(hbase.hregion.max.filesize)之後就會觸發切分,實際上並不是這樣,真正實現中這個閾值是對於某個store來說的,即一個region中最大store的大小大於設定閾值之後才會觸發切分。另外一個大家比較關心的問題是這裡所說的store大小是壓縮後的檔案總大小還是未壓縮檔案總大小,實際實現中store大小為壓縮後的檔案大小(採用壓縮的場景)。ConstantSizeRegionSplitPolicy相對來來說最容易想到,但是在生產線上這種切分策略卻有相當大的弊端:切分策略對於大表和小表沒有明顯的區分。閾值(hbase.hregion.max.filesize)設定較大對大表比較友好,但是小表就有可能不會觸發分裂,極端情況下可能就1個,這對業務來說並不是什麼好事。如果設定較小則對小表友好,但一個大表就會在整個叢集產生大量的region,這對於叢集的管理、資源使用、failover來說都不是一件好事。
- IncreasingToUpperBoundRegionSplitPolicy : 0.94版本~2.0版本預設切分策略
切分策略稍微有點複雜,總體看和ConstantSizeRegionSplitPolicy思路相同,一個region中最大store大小大於設定閾值就會觸發切分。但是這個閾值並不像ConstantSizeRegionSplitPolicy是一個固定的值,而是會在一定條件下不斷調整,調整規則和region所屬表在當前regionserver上的region個數有關係 :(#regions) * (#regions) * (#regions) * flush size * 2,當然閾值並不會無限增大,最大值為使用者設定的MaxRegionFileSize。這種切分策略很好的彌補了ConstantSizeRegionSplitPolicy的短板,能夠自適應大表和小表。而且在大叢集條件下對於很多大表來說表現很優秀,但並不完美,這種策略下很多小表會在大叢集中產生大量小region,分散在整個叢集中。而且在發生region遷移時也可能會觸發region分裂。
- SteppingSplitPolicy: 2.0版本預設切分策略
這種切分策略的切分閾值又發生了變化,相比 IncreasingToUpperBoundRegionSplitPolicy 簡單了一些,依然和待分裂region所屬表在當前regionserver上的region個數有關係,如果region個數等於1,切分閾值為flush size * 2,否則為MaxRegionFileSize。這種切分策略對於大叢集中的大表、小表會比 IncreasingToUpperBoundRegionSplitPolicy 更加友好,小表不會再產生大量的小region,而是適可而止。
另外,還有一些其他分裂策略,比如使用DisableSplitPolicy:可以禁止region發生分裂;而KeyPrefixRegionSplitPolicy,DelimitedKeyPrefixRegionSplitPolicy對於切分策略依然依據預設切分策略,但對於切分點有自己的看法,比如KeyPrefixRegionSplitPolicy要求必須讓相同的PrefixKey待在一個region中。
在用法上,一般情況下使用預設切分策略即可,也可以在cf級別設定region切分策略,命令為:
create ’table’, {NAME => ‘cf’, SPLIT_POLICY => ‘org.apache.hadoop.hbase.regionserver. ConstantSizeRegionSplitPolicy'}
Region切分準備工作-尋找SplitPoint
region切分策略會觸發region切分,切分開始之後的第一件事是尋找切分點-splitpoint。所有預設切分策略,無論是ConstantSizeRegionSplitPolicy、 IncreasingToUpperBoundRegionSplitPolicy 抑或是SteppingSplitPolicy,對於切分點的定義都是一致的。當然,使用者手動執行切分時是可以指定切分點進行切分的,這裡並不討論這種情況
那切分點是如何定位的呢? 整個region中最大store中的最大檔案中最中心的一個block的首個rowkey 。這是一句比較消耗腦力的語句,需要細細品味。另外,HBase還規定,如果定位到的rowkey是整個檔案的首個rowkey或者最後一個rowkey的話,就認為沒有切分點。
什麼情況下會出現沒有切分點的場景呢?最常見的就是一個檔案只有一個block,執行split的時候就會發現無法切分。很多新同學在測試split的時候往往都是新建一張新表,然後往新表中插入幾條資料並執行一下flush,再執行split,奇蹟般地發現數據表並沒有真正執行切分。
Region核心切分流程
HBase將整個切分過程包裝成了一個事務,意圖能夠保證切分事務的原子性。整個分裂事務過程分為三個階段:prepare – execute – (rollback)
- prepare階段:在記憶體中初始化兩個子region,具體是生成兩個HRegionInfo物件,包含tableName、regionName、startkey、endkey等。同時會生成一個transaction journal,這個物件用來記錄切分的進展,具體見rollback階段。
- execute階段:切分的核心操作。見下圖(來自 Hortonworks ):
- regionserver 更改ZK節點 /region-in-transition 中該region的狀態為SPLITING。
- master通過watch節點/region-in-transition檢測到region狀態改變,並修改記憶體中region的狀態,在master頁面RIT模組就可以看到region執行split的狀態資訊。
- 在父儲存目錄下新建臨時資料夾.split儲存split後的daughter region資訊。
- 關閉parent region:parent region關閉資料寫入並觸發flush操作,將寫入region的資料全部持久化到磁碟。此後短時間內客戶端落在父region上的請求都會丟擲異常NotServingRegionException。
- 核心分裂步驟:在.split資料夾下新建兩個子資料夾,稱之為daughter A、daughter B,並在資料夾中生成reference檔案,分別指向父region中對應檔案。這個步驟是所有步驟中最核心的一個環節,生成reference檔案日誌如下所示:
除此之外,還需要關注reference檔案的檔案內容,reference檔案是一個引用檔案(並非linux連結檔案),檔案內容很顯然不是使用者資料。檔案內容其實非常簡單,主要有兩部分構成:其一是切分點 splitkey,其二是一個boolean型別的變數(true或者false),true表示該reference檔案引用的是父檔案的上半部分(top),而false表示引用的是下半部分 (bottom)。
6.父region分裂為兩個子region後, 將daughter A、daughter B拷貝到HBase根目錄下,形成兩個新的region。
7.parent region通知修改 hbase.meta 表後下線,不再提供服務。下線後parent region在meta表中的資訊並不會馬上刪除,而是標註split列、offline列為true,並記錄兩個子region
8.開啟daughter A、daughter B兩個子region。通知修改 hbase.meta 表,正式對外提供服務
rollback階段:如果execute階段出現異常,則執行rollback操作。為了實現回滾,整個切分過程被分為很多子階段,回滾程式會根據當前進展到哪個子階段清理對應的垃圾資料。程式碼中使用 JournalEntryType 來表徵各個子階段,具體見下圖: