分裂策略
不同的分裂策略的實現需要繼承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的目錄結構如下
/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
```
從最后的hfile文件名可以看到,子region引用了父親region的同名的hfile,這兩個特殊的文件里沒有真實的數據,而是一個索引數據,記着split row是什么,並且自己是split row的前半部分還是后半部分(Reference).
-
往兩個子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節點