Hbase Split 是一個很重要的功能,HBase 通過把數(shù)據(jù)分配到一定數(shù)量的 Region 來達(dá)到負(fù)載均衡的。當(dāng) Region 管理的數(shù)據(jù)過多時,可以通過手動或自動的方式觸發(fā) HBase Split 將一個 Region 分裂成兩個新的子 Region,并對父 Region 進(jìn)行清除處理(不會立即清除)。
HBase 為什么需要 Split?
相比于傳統(tǒng) RDBMS 對大數(shù)據(jù)表的擴(kuò)展方式,HBase 天然支持自動分庫分表,實(shí)現(xiàn)的基礎(chǔ)就是 Split 和 Rebalance。
Region 是管理一段連續(xù)的 Rowkey 的核心單元,當(dāng) Region 管理的 Rowkey 數(shù)量多時,或 HFile 文件較大時,都會影響到性能。
Pre-splitting
一個表剛被創(chuàng)建的時候,Hbase 默認(rèn)分配一個 Region 給表,所有的讀寫請求都會訪問到一個 RegionServer 上的唯一一個 Region。這樣就達(dá)不到負(fù)載均衡的效果了,集群中的其他 RegionServer 就會處于空閑的狀態(tài)(考慮只有一個表的情況)。解決這個問題可以有兩種方法:
如果能很好的預(yù)計業(yè)務(wù)的 Rowkey 分布和數(shù)據(jù)增長情況,可以在建表時分配好 Region 的數(shù)量和每個 Region Rowkey 范圍,同時禁用表的自動分區(qū),因?yàn)?Split 操作會有額外的開銷和維護(hù)成本。
另一種方法只需要指定分配的 Region 數(shù)量,利用 pre-splitting,生成每個 Region 的 Rowkey 范圍
Hbase 自帶了兩種 pre-split 的算法,分別是 HexStringSplit 和 UniformSplit 。HexStringSplit 適用于十六進(jìn)制字符的 Rowkey(MD5)。UniformSplit 適用于隨機(jī)字節(jié)組成的 Rowkey(Hash)。
Split 觸發(fā)時機(jī)
HBase 中共有3種情況會觸發(fā) HBase Split:
- 當(dāng) Memstore flush 操作后,HRegion 寫入新的 HFile,有可能產(chǎn)生較大的 HFile,會判斷是否需要執(zhí)行 Split。
- HStore 執(zhí)行完成 Compact 操作后可能產(chǎn)生較大的 HFile,會判斷是否需要執(zhí)行 Split。
- HBaseAdmin 手動執(zhí)行 split 命令時,會觸發(fā) Split。
Split 觸發(fā)策略
目前已經(jīng)的支持觸發(fā)策略多達(dá)6種,每種觸發(fā)策略都有各自的適用場景,可以根據(jù)業(yè)務(wù)在表級別(Column family 級別)選擇不同的切分觸發(fā)策略。一般情況下使用默認(rèn)切分策略即可。
ConstantSizeRegionSplitPolicy:0.94版本前默認(rèn)切分策略。
一個 Region 中最大 Store 的大小大于設(shè)置閾值之后才會觸發(fā)切分,Store 大小為壓縮后的文件大?。▎⒂脡嚎s的場景)
切分策略對于大表和小表沒有明顯的區(qū)分IncreasingToUpperBoundRegionSplitPolicy:0.94版本~2.0版本默認(rèn)切分策略。
和 ConstantSizeRegionSplitPolicy 思路相同,一個 Region 中最大 Store 大小大于設(shè)置閾值就會觸發(fā)切分,區(qū)別是這個閾值并不像 ConstantSizeRegionSplitPolicy 是一個固定的值,而是會在不斷調(diào)整。
調(diào)整規(guī)則和 Region 所屬表在當(dāng)前 RegionServer 上的 Region 個數(shù)有關(guān)系 :(#regions) * (#regions) * (#regions) * flush_size * 2,最大值為用戶設(shè)置的 MaxRegionFileSize
能夠自適應(yīng)大表和小表,這種策略下很多小表會在大集群中產(chǎn)生大量小 Region,分散在整個集群中SteppingSplitPolicy:2.0版本默認(rèn)切分策略。
相比 IncreasingToUpperBoundRegionSplitPolicy 簡單了一些,依然和待分裂 Region 所屬表在當(dāng)前 RegionServer 上的 Region 個數(shù)有關(guān)系:如果 Region 個數(shù)等于1,切分閾值為 flush_size * 2,否則為 MaxRegionFileSizeDisableSplitPolicy:禁止 Region split
KeyPrefixRegionSplitPolicy:切分策略依然依據(jù)默認(rèn)切分策略,根據(jù) Rowkey 指定長度的前綴來切分 Region,保證相同的前綴的行保存在同一個 Region 中。由 KeyPrefixRegionSplitPolicy.prefix_length 屬性指定 Rowkey 前綴長度。按此長度對splitPoint進(jìn)行截取。
此種策略比較適合有固定前綴的 Rowkey。當(dāng)沒有設(shè)置前綴長度,切分效果等同與 IncreasingToUpperBoundRegionSplitPolicy。DelimitedKeyPrefixRegionSplitPolicy:切分策略依然依據(jù)默認(rèn)切分策略,同樣是保證相同 RowKey 前綴的數(shù)據(jù)在一個Region中,但是是以指定分隔符前面的前綴為來切分 Region。
Split 流程
Region split 的過程可以簡單的理解為:在 Region 中找到一個合適的 split point,在這個 split point 上將該 Region 的數(shù)據(jù)劃分為兩個新的 Region。當(dāng)然這個過程的實(shí)現(xiàn)起來很復(fù)雜,Split 發(fā)生時,新創(chuàng)建的子 Region 不會立即將所有數(shù)據(jù)重新寫入新文件,而是會創(chuàng)建類似于符號鏈接文件的小文件,稱為引用文件(reference files),根據(jù) split point,指向父存儲文件的頂部或底部(父 Region 的一半數(shù)據(jù))。
下文引用官方博客對 HBase split 流程的解釋:

HBase 將整個切分過程包裝成了一個事務(wù),為了保證切分事務(wù)的原子性。整個分裂事務(wù)過程分為三個階段:prepare – execute – (rollback)
prepare階段:在內(nèi)存中初始化兩個子 Region,具體是生成兩個 HRegionInfo 對象,包含 TableName、RegionName、Startkey、Endkey等。同時會生成一個 transaction journal,這個對象用來記錄切分的進(jìn)展
execute階段:切分的核心操作
(1)在ZK節(jié)點(diǎn) /hbase/region-in-transition/region-name 下創(chuàng)建一個 znode,并設(shè)置狀態(tài)為SPLITTING
(2)Master 通過監(jiān)聽ZK節(jié)點(diǎn),檢測到 Region 狀態(tài)的變化
(3)RegionServer 在父 Region 的數(shù)據(jù)目錄(HDFS)下創(chuàng)建一個名稱為 .splits 的子目錄
(4)RegionServer 關(guān)閉父 Region,強(qiáng)制將數(shù)據(jù) flush 到磁盤,并這個 Region 標(biāo)記為 offline 的狀態(tài)。此時,落到這個 Region 的請求都會返回 NotServingRegionException 這個錯誤,客戶端需要進(jìn)行一些重試,直到新的 Region 上線。
(5)RegionServer 在 .splits 目錄(HDFS)下創(chuàng)建 daughterA 和 daughterB 子目錄,并在文件夾中創(chuàng)建對應(yīng)的 reference 文件,分別指向父 Region 的數(shù)據(jù)文件中的一部分。
(6)RegionServer 創(chuàng)建子 Region 的目錄(HDFS),并將 daughterA 和 daughterB 目錄下的文件拷貝到 Region 目錄。
(7)在 .META. 表中設(shè)置父 Region 為 offline 狀態(tài),不再提供服務(wù)。并將子 Region 的信息添加到 .META. 表中父 Region 的信息中(splitA 和 splitB 兩列)。這個時候如果掃描 hbase:meta 表,會發(fā)現(xiàn)父 Region 正在執(zhí)行 split,并不能看到子 Region 的信息。如果 RegionServer 執(zhí)行這個過程失敗,Master 和下一個分配了這個 Region 的 Regionserver 會清除 split 相關(guān)的狀態(tài)數(shù)據(jù)。
(8)RegionServer 并行啟用兩個子 Region,并正式提供對外服務(wù)
(9)RegionSever 將 daughterA 和 daughterB 添加到 .META. 表中,并設(shè)置為 online 狀態(tài),這樣就可以從 .META. 找到子 Region,并可以對子 Region 進(jìn)行訪問了。
(10)RegionServr 修改ZK節(jié)點(diǎn) /hbase/region-in-transition/region-name 的狀態(tài)為SPLIT,Master 就可以監(jiān)聽到 Region 狀態(tài)的更新。Split 事務(wù)就此結(jié)束。
再次強(qiáng)調(diào),為了減少對業(yè)務(wù)的影響,Region 的 Split 并不涉及到數(shù)據(jù)遷移的操作,而只是創(chuàng)建了對父Region的指向。只有在做大合并的時候,才會將數(shù)據(jù)進(jìn)行遷移。
- rollback階段:如果 execute 階段出現(xiàn)異常,則執(zhí)行 rollback 操作。為了實(shí)現(xiàn)回滾,整個切分過程被分為很多子階段,回滾程序會根據(jù)當(dāng)前進(jìn)展到哪個子階段清理對應(yīng)的垃圾數(shù)據(jù)。JournalEntryType 類定義了各個子階段。
Region 事務(wù)性保證
整個region切分是一個比較復(fù)雜的過程,涉及子步驟,因此必須保證整個 Split 過程的事務(wù)性,即要么完全成功,要么完全未開始,在任何情況下也不能出現(xiàn) Split 只完成一半的情況。為了實(shí)現(xiàn)事務(wù)性,Hbase 設(shè)計了使用狀態(tài)機(jī)(見 SplitTransaction 類)的方式保存切分過程中的每個子步驟狀態(tài),這樣一旦出現(xiàn)異常,系統(tǒng)可以根據(jù)當(dāng)前所處的狀態(tài)決定是否回滾,以及如何回滾。
目前實(shí)現(xiàn)中這些中間狀態(tài)都只存儲在內(nèi)存中,因此一旦在切分過程中出現(xiàn) RegionServer 宕機(jī)的情況,有可能會出現(xiàn)切分處于中間狀態(tài)的情況,也就是RIT狀態(tài)。這種情況下可使用 hbck 工具,根據(jù)實(shí)際情況查看并分析解決方案。
在2.0版本 HBase 實(shí)現(xiàn)了新的分布式事務(wù)框架 Procedure V2(HBASE-12439),使用 HLog 存儲這種單機(jī)事務(wù)(DDL、Split、Move 等操作)的中間狀態(tài)。保證即使在事務(wù)執(zhí)行過程中參與者發(fā)生了宕機(jī),依然可以使用 HLog 作為協(xié)調(diào)者對事務(wù)進(jìn)行回滾操作或重新提交。
通過 reference 文件如何查找到對應(yīng)的數(shù)據(jù)
根據(jù)文件名來判斷是否是 reference 文件:
- reference 文件的命名規(guī)則為前半部分為父 Region 對應(yīng)的 HFile 的文件名,后半部分是父 Region 的名稱,因此讀取的時候也根據(jù)前半部分和后半部分來定位文件。
- 根據(jù) reference 文件的內(nèi)容來確定掃描的范圍,reference 的內(nèi)容包含兩部分:一部分是切分點(diǎn) splitkey,另一部分是 boolean 類型的變量,如果為 true 則掃描文件的上半部分,反之則掃描文件的下半部分
- 接下來確定了掃描的文件,以及文件的掃描范圍,那就按照正常的文件檢索了
Split 對其他模塊的影響
執(zhí)行 Region Split 過程不涉及數(shù)據(jù)的移動,所以可以很快完成。新生成的子 Region 文件中沒有任何用戶數(shù)據(jù),而是一個 reference 文件,文件中存儲的是一些元數(shù)據(jù)信息,包括切分點(diǎn)的 Rowkey 等。引入了以下問題:
父 Region 的數(shù)據(jù)什么時候會遷移到子 Region 目錄
子 Region 發(fā)生 major_compaction 時。將父 Region 目錄中屬于該子 Region 的所有數(shù)據(jù)讀出來并寫入子 Region 數(shù)據(jù)文件目錄中,這一操作符合 compaction 本身的處理邏輯,因此在 compaction 中操作。父 Region 什么時候會被刪除
HMaster 會啟動一個線程定期檢查所有處于 splitting 狀態(tài)的父 Region,確定其是否可以被清理。檢測線程首先會在 .META. 表中找到 splitting region,并找出其生成的兩個子 Region(.META. 表中 splitA 和 splitB 列)。然后檢查兩個子 Region 是否保留引用文件,如果都不存在就認(rèn)為該 splitting region 可以被刪除和下線。.META. 表中的信息參考下圖:
注意事項(xiàng)
使用 Split 時有以下需要注意的:
- 不能對元數(shù)據(jù)表進(jìn)行 Split
- 不能對正在恢復(fù)的 Region 進(jìn)行 Split
- 如果某個 Region 存在引用文件的不能 Split
- 當(dāng) Split 執(zhí)行到將父 Region 下線之后,子 Region還未創(chuàng)建之前,如果此刻正訪問的是父 Region,在客戶端沒有更新 Region 地址緩存的情況下,會報 NotServingRegionException 異常,因此客戶端需要做好重試機(jī)制。
- Split 的完整過程中有個關(guān)鍵的時間點(diǎn)是無法回退的點(diǎn)(PONR: point of no return),發(fā)生在將 .META. 表中的父 Region 下線之前,如果進(jìn)入 PONR 之后,由于種種原因更新 .META. 失敗,需要重啟所在的 RegionServer。
Reference:
https://zh.hortonworks.com/blog/apache-hbase-region-splitting-and-merging/
http://hbase.apache.org/book.html