轉: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節點