HBase Region 的分裂
轉:http://www.tuicool.com/articles/jQvEVvu
分裂策略
不同的分裂策略的實現需要繼承RegionSplitPolicy,主要實現兩個方法:
- shouldSplit()表示是否需要分裂
- getSplitPoint()得到分裂點rowkey
從 HBase 0.94之後,預設的分裂策略是IncreasingToUpperBoundRegionSplitPolicy ,思想就是當Region的大小超過某個閾值時,即進行分裂。
這個閾值主要由如下幾個因素決定:
- hbase.hregion.max.filesize
- hbase.increasing.policy.initial.size
- 當前Region所在RegionServer上和當前Region屬於同一張表的Region個數
根據以上三個因素算出一個閾值後,如果當前Region有某個Store的大小大於這個閾值,則認為該Region可分裂,這裡對於Store還有一個條件就是Store下不能存在reference型別的StoreFile,這種reference型別的StoreFile是一次分裂後產生的,後續會詳說。
對於IncreasingToUpperBoundRegionSplitPolicy來說,使用基類中預設的getSplitPoint()函式,即將Region中size最大的Store下最大的StoreFile拿出來,然後根據block index找出StoreFile中間的block,那麼這個block的startkey就是split point
分裂實現
在後臺flush執行緒flush完成一個region內部的memstore時,會去檢查這個region是否需要分裂,如果需要分裂,會提交一個SplitRequest任務給後臺的compactSplitThread執行緒內部的負責split的執行緒池,SplitRequest內部會建立一個SplitTransaction來完成split
- 根據待分裂region和split point生成兩個HRegionInfo物件,代表分裂後產生的兩個dautghter region
- 在zk上建立一個ephemeral node,路徑是 /hbase/region-in-transition/regionEncodedName,節點內容為了通知master某個region server想split
某個region,兩個子region的資訊,包括range等,需要通知master原因是防止master對這個
region進行遷移等等 - 等待master批准region server split
- 在hdfs上為這個region的split過程建立臨時工作目錄/hbase/data/namespace/tableName/regionEncodedName/.splits
-
關閉當前待分裂region
- 將region的writestate的writesEnabled置為false,告訴後臺的compact和flush執行緒不要再工作了
- 如果當前region內的memstore size大於hbase.hregion.preclose.flush.size,預設5MB,那麼先做一次pre flush。這裡最開始時已經進行了flush region的操作,在flush region完成到現在中間可能還有寫操作寫入當前region內的各個store的memstore中,由於關閉region期間region不能提供讀寫服務,並且關閉region期間需要將region中的memstore進行flush,所以為了讓region的不能提供讀寫服務時間變少,這裡做一個pre flush,後續再真正關閉region
- 置上region的closing標記,導致region停讀寫。
- flush當前region的所有memstore,並且將region的所有storefile關閉
- 置上region的closed標記
- 將region從region server的online region列表中刪除
-
開始split 當前region的store file(splitStoreFiles),為region下的每個storefile都建立一個StoreFileSplitter任務,交給執行緒池處理。StoreFileSplitter任務實際上沒有真正的劈開
storefile,生成兩個小的storefile,而是生成兩個型別為reference的storefile檔案,檔名和內容都比較特殊,比如:假設region encoded name為aaaa的region,分裂為兩個name為bbbb和cccc的region,aaaa下有一個column family叫做cfA,下面有一個名
為hfileA的storefile,那麼三個region的目錄結構如下
從最後的hfile檔名可以看到,子region引用了父親region的同名的hfile,這兩個特殊的檔案裡沒有真實的資料,而是一個索引資料,記著split row是什麼,並且自己是split row的前半部分還是後半部分(Reference)./hbase/data/namespace/tableName/aaaa/cfA/hfileA /hbase/data/namespace/tableName/bbbb/.splits/cfA/hfileA.aaaa /hbase/data/namespace/tableName/cccc/.splits/cfA/hfileA.aaaa
-
往兩個子region的目錄中寫入.regioninfo檔案,並且將臨時目錄改名,目錄結構如下
/hbase/data/namespace/tableName/bbbb/cfA/hfileA.aaaa /hbase/data/namespace/tableName/cccc/cfA/hfileA.aaaa
- 原子的修改meta table,在meta table裡面標記父親region下線,並且split為兩個region,
並且在meta table中加入兩個子region對應的項
- 開啟兩個子region,更新meta table,將location記錄其中 - 將兩個子region加入region server的online region列表中
- 請求一個compaction操作,後臺的compaction操作最終會清理掉這些reference檔案
- 更新zk上節點的狀態,告訴master已經split完成
-
等待master刪除zk節點