1. 程式人生 > >Hbase split的三種方式和split的過程

Hbase split的三種方式和split的過程

在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

在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 就會被刪除。

參考連線: