在Hbase中split是一個很重要的功能,Hbase是通過把數據分配到一定數量的region來達到負載均衡的。一個table會被分配到一個或多個region中,這些region會被分配到一個或者多個regionServer中。在自動split策略中,當一個region達到一定的大小就會自動split成兩個region。table在region中是按照row key來排序的,並且一個row key所對應的行只會存儲在一個region中,這一點保證了Hbase的強一致性 。
在一個region中有一個或多個stroe,每個stroe對應一個column families(列族)。一個store中包含一個memstore 和 0 或 多個store files。每個column family 是分開存放和分開訪問的。
Pre-splitting
當一個table剛被創建的時候,Hbase默認的分配一個region給table。也就是說這個時候,所有的讀寫請求都會訪問到同一個regionServer的同一個region中,這個時候就達不到負載均衡的效果了,集群中的其他regionServer就可能會處於比較空閑的狀態。解決這個問題可以用pre-splitting,在創建table的時候就配置好,生成多個region。
在table初始化的時候如果不配置的話,Hbase是不知道如何去split region的,因為Hbase不知道應該那個row key可以作為split的開始點。如果我們可以大概預測到row key的分布,我們可以使用pre-spliting來幫助我們提前split region。不過如果我們預測得不准確的話,還是可能導致某個region過熱,被集中訪問,不過還好我們還有auto-split。最好的辦法就是首先預測split的切分點,做pre-splitting,然后后面讓auto-split來處理后面的負載均衡。
Hbase自帶了兩種pre-split的算法,分別是 HexStringSplit 和 UniformSplit 。如果我們的row key是十六進制的字符串作為前綴的,就比較適合用HexStringSplit,作為pre-split的算法。例如,我們使用HexHash(prefix)作為row key的前綴,其中Hexhash為最終得到十六進制字符串的hash算法。我們也可以用我們自己的split算法。
在hbase shell 下:
hbase org.apache.hadoop.hbase.util.RegionSplitter pre_split_table HexStringSplit -c 10 -f f1
-c 10 的意思為,最終的region數目為10個;-f f1為創建一個那么為f1的 column family.
執行scan 'hbase:meta' 可以看到meta表中的,
只截取了meta表中的2個region的記錄(一共10個region),分別是rowkey范圍是 '' ''~19999999 和19999999~33333332的region。
我們也可以自定義切分點,例如在hbase shell下使用如下命令:
create 't1', 'f1', {SPLITS => ['10', '20', '30', '40']}
自動splitting
當一個reion達到一定的大小,他會自動split稱兩個region。如果我們的Hbase版本是0.94 ,那么默認的有三種自動split的策略,ConstantSizeRegionSplitPolicy,IncreasingToUpperBoundRegionSplitPolicy還有 KeyPrefixRegionSplitPolicy.
在0.94版本之前ConstantSizeRegionSplitPolicy 是默認和唯一的split策略。當某個store(對應一個column family)的大小大於配置值 ‘hbase.hregion.max.filesize’的時候(默認10G)region就會自動分裂。
而0.94版本中,IncreasingToUpperBoundRegionSplitPolicy 是默認的split策略。
這個策略中,最小的分裂大小和table的某個region server的region 個數有關,當store file的大小大於如下公式得出的值的時候就會split,公式如下
Min (R^2 * “hbase.hregion.memstore.flush.size”, “hbase.hregion.max.filesize”) R為同一個table中在同一個region server中region的個數。
例如:
hbase.hregion.memstore.flush.size 默認值 128MB。
hbase.hregion.max.filesize默認值為10GB 。
- 如果初始時R=1,那么Min(128MB,10GB)=128MB,也就是說在第一個flush的時候就會觸發分裂操作。
- 當R=2的時候Min(2*2*128MB,10GB)=512MB ,當某個store file大小達到512MB的時候,就會觸發分裂。
- 如此類推,當R=9的時候,store file 達到10GB的時候就會分裂,也就是說當R>=9的時候,store file 達到10GB的時候就會分裂。
split 點都位於region中row key的中間點。
KeyPrefixRegionSplitPolicy可以保證相同的前綴的row保存在同一個region中。
指定rowkey前綴位數划分region,通過讀取 KeyPrefixRegionSplitPolicy.prefix_length 屬性,該屬性為數字類型,表示前綴長度,在進行split時,按此長度對splitPoint進行截取。此種策略比較適合固定前綴的rowkey。當table中沒有設置該屬性,指定此策略效果等同與使用IncreasingToUpperBoundRegionSplitPolicy。
我們可以通過配置 hbase.regionserver.region.split.policy 來指定split策略,我們也可以寫我們自己的split策略。
強制split
Hbase 允許客戶端強制執行split,在hbase shell中執行以下命令:
split 'forced_table', 'b' //其中forced_table 為要split的table , ‘b’ 為split 點
region splits 執行過程:
region server處理寫請求的時候,會先寫入memstore,當memstore 達到一定大小的時候,會寫入磁盤成為一個store file。這個過程叫做 memstore flush。當store files 堆積到一定大小的時候,region server 會 執行‘compact’操作,把他們合成一個大的文件。 當每次執行完flush 或者compact操作,都會判斷是否需要split。當發生split的時候,會生成兩個region A 和 region B但是parent region數據file並不會發生復制等操作,而是region A 和region B 會有這些file的引用。這些引用文件會在下次發生compact操作的時候清理掉,並且當region中有引用文件的時候是不會再進行split操作的。這個地方需要注意一下,如果當region中存在引用文件的時候,而且寫操作很頻繁和集中,可能會出現region變得很大,但是卻不split。因為寫操作比較頻繁和集中,但是沒有均勻到每個引用文件上去,所以region一直存在引用文件,不能進行分裂,這篇文章講到了這個情況,總結得挺好的。http://koven2049.iteye.com/blog/1199519
雖然split region操作是region server單獨確定的,但是split過程必須和很多其他部件合作。region server 在split開始前和結束前通知master,並且需要更新.META.表,這樣,客戶端就能知道有新的region。在hdfs中重新排列目錄結構和數據文件。split是一個復雜的操作。在split region的時候會記錄當前執行的狀態,當出錯的時候,會根據狀態進行回滾。下圖表示split中,執行的過程。(紅色線表示region server 或者master的操作,綠色線表示client的操作。)
1.region server 決定split region,第一步,region server在zookeeper中創建在
/hbase/region-in-transition/region-name 目錄下,創建一個znode,狀態為SPLITTING.
2.因為master有對 region-in-transition 的znode做監聽,所以,mater的得知parent region需要split
3.region server 在hdfs的parent region的目錄下創建一個名為“.splits”的子目錄
4.region server 關閉parent region。強制flush緩存,並且在本地數據結構中標記region為下線狀態。如果這個時候客戶端剛好請求到parent region,會拋出NotServingRegionException。這時客戶端會進行補償性重試。
5.region server在.split 目錄下分別為兩個daughter region創建目錄和必要的數據結構。然后創建兩個引用文件指向parent regions的文件。
6.region server 在HDFS中,創建真正的region目錄,並且把引用文件移到對應的目錄下。
7.region server 發送一個put的請求到.META.表中,並且在.META.表中設置parent region為下線狀態,並且在parent region對應的row中兩個daughter region的信息。但是這個時候在.META.表中daughter region 還不是獨立的row。這個時候如果client scan .META.表,會發現parent region正在split,但是client還看不到daughter region的信息。當這個put 成功之后,parent region split會被正在的執行。如果在 RPC 成功之前 region server 就失敗了,master和下次打開parent region的region server 會清除關於這次split的臟狀態。但是當RPC返回結果給到parent region ,即.META.成功更新之后,,region split的流程還會繼續進行下去。相當於是個補償機制,下次在打開這個parent region的時候會進行相應的清理操作。
8.region server 打開兩個daughter region接受寫操作。
9.region server 在.META.表中增加daughters A 和 B region的相關信息,在這以后,client就能發現這兩個新的regions並且能發送請求到這兩個新的region了。client本地具體有.META.表的緩存,當他們訪問到parent region的時候,發現parent region下線了,就會重新訪問.META.表獲取最新的信息,並且更新本地緩存。
10.region server 更新 znode 的狀態為SPLIT。master就能知道狀態更新了,master的平衡機制會判斷是否需要把daughter regions 分配到其他region server 中。
11.在split之后,meta和HDFS依然會有引用指向parent region. 當compact 操作發生在daughter regions中,會重寫數據file,這個時候引用就會被逐漸的去掉。垃圾回收任務會定時檢測daughter regions是否還有引用指向parent files,如果沒有引用指向parent files的話,parent region 就會被刪除。
參考連接:
http://hortonworks.com/blog/apache-hbase-region-splitting-and-merging/ Hbase split
http://hbase.apache.org/book/regions.arch.html Hbase 官方文檔(region)
http://blog.javachen.com/2014/01/16/hbase-region-split-policy/ split策略
http://blackproof.iteye.com/blog/2037159 split源碼解析
ZOOM 雲視頻會議網站:http://www.zoomonline.cn/