1. 程式人生 > >HBase基本架構及原理

HBase基本架構及原理

1. HBase框架簡單介紹

HBase是一個分散式的、面向列的開源資料庫,它不同於一般的關係資料庫,是一個適合於非結構化資料儲存的資料庫。另一個不同的是HBase基於列的而不是基於行的模式。HBase使用和 BigTable非常相同的資料模型。使用者儲存資料行在一個表裡。一個數據行擁有一個可選擇的鍵和任意數量的列,一個或多個列組成一個ColumnFamily,一個Fmaily下的列位於一個HFile中,易於快取資料。表是疏鬆的儲存的,因此使用者可以給行定義各種不同的列。在HBase中資料按主鍵排序,同時表按主鍵劃分為多個Region。

在分散式的生產環境中,HBase 需要執行在 HDFS 之上,以 HDFS 作為其基礎的儲存設施。HBase 上層提供了訪問的資料的 Java API 層,供應用訪問儲存在 HBase 的資料。在 HBase 的叢集中主要由 Master 和 Region Server 組成,以及 Zookeeper,具體模組如下圖所示:

簡單介紹一下 HBase 中相關模組的作用:

  • Master
    HBase Master用於協調多個Region Server,偵測各個RegionServer之間的狀態,並平衡RegionServer之間的負載。HBaseMaster還有一個職責就是負責分配Region給RegionServer。HBase允許多個Master節點共存,但是這需要Zookeeper的幫助。不過當多個Master節點共存時,只有一個Master是提供服務的,其他的Master節點處於待命的狀態。當正在工作的Master節點宕機時,其他的Master則會接管HBase的叢集。
  • Region Server
    對於一個RegionServer而言,其包括了多個Region。RegionServer的作用只是管理表格,以及實現讀寫操作。Client直接連線RegionServer,並通訊獲取HBase中的資料。對於Region而言,則是真實存放HBase資料的地方,也就說Region是HBase可用性和分散式的基本單位。如果當一個表格很大,並由多個CF組成時,那麼表的資料將存放在多個Region之間,並且在每個Region中會關聯多個儲存的單元(Store)。
  • Zookeeper
    對於 HBase 而言,Zookeeper的作用是至關重要的。首先Zookeeper是作為HBase Master的HA解決方案。也就是說,是Zookeeper保證了至少有一個HBase Master 處於執行狀態。並且Zookeeper負責Region和Region Server的註冊。其實Zookeeper發展到目前為止,已經成為了分散式大資料框架中容錯性的標準框架。不光是HBase,幾乎所有的分散式大資料相關的開源框架,都依賴於Zookeeper實現HA。

2. Hbase資料模型

2.1 邏輯檢視

基本概念:

  • RowKey:是Byte array,是表中每條記錄的“主鍵”,方便快速查詢,Rowkey的設計非常重要;
  • Column Family:列族,擁有一個名稱(string),包含一個或者多個相關列;
  • Column:屬於某一個columnfamily,familyName:columnName,每條記錄可動態新增;
  • Version Number:型別為Long,預設值是系統時間戳,可由使用者自定義;
  • Value(Cell):Byte array。

2.2 物理模型:

  • 每個column family儲存在HDFS上的一個單獨檔案中,空值不會被儲存。
  • Key 和 Version number在每個column family中均有一份;
  • HBase為每個值維護了多級索引,即:<key, columnfamily, columnname, timestamp>;
  • 表在行的方向上分割為多個Region;
  • Region是Hbase中分散式儲存和負載均衡的最小單元,不同Region分佈到不同RegionServer上。
  • Region按大小分割的,隨著資料增多,Region不斷增大,當增大到一個閥值的時候,Region就會分成兩個新的Region;
  • Region雖然是分散式儲存的最小單元,但並不是儲存的最小單元。每個Region包含著多個Store物件。每個Store包含一個MemStore或若干StoreFile,StoreFile包含一個或多個HFile。MemStore存放在記憶體中,StoreFile儲存在HDFS上。

table在region中是按照row key來排序的,並且一個row key所對應的行只會儲存在一個region中,這一點保證了Hbase的強一致性 。在一個region中有一個或多個stroe,每個stroe對應一個column families(列族)。一個store中包含一個memstore 和 0 或 多個store files。每個column family 是分開存放和分開訪問的。

2.3 ROOT表和META表

HBase的所有Region元資料被儲存在.META.表中,隨著Region的增多,.META.表中的資料也會增大,並分裂成多個新的Region。為了定位.META.表中各個Region的位置,把.META.表中所有Region的元資料儲存在-ROOT-表中,最後由Zookeeper記錄-ROOT-表的位置資訊。所有客戶端訪問使用者資料前,需要首先訪問Zookeeper獲得-ROOT-的位置,然後訪問-ROOT-表獲得.META.表的位置,最後根據.META.表中的資訊確定使用者資料存放的位置,如下圖所示。

-ROOT-表永遠不會被分割,它只有一個Region,這樣可以保證最多隻需要三次跳轉就可以定位任意一個Region。為了加快訪問速度,.META.表的所有Region全部儲存在記憶體中。客戶端會將查詢過的位置資訊快取起來,且快取不會主動失效。如果客戶端根據快取資訊還訪問不到資料,則詢問相關.META.表的Region伺服器,試圖獲取資料的位置,如果還是失敗,則詢問-ROOT-表相關的.META.表在哪裡。最後,如果前面的資訊全部失效,則通過ZooKeeper重新定位Region的資訊。所以如果客戶端上的快取全部是失效,則需要進行6次網路來回,才能定位到正確的Region。

一個完整分散式的HBase的組成示意圖如下,後面我們再詳細談其工作原理。

3. 高可用

3.1 Write-Ahead-Log(WAL)保障資料高可用

我們理解下HLog的作用。HBase中的HLog機制是WAL的一種實現,而WAL(一般翻譯為預寫日誌)是事務機制中常見的一致性的實現方式。每個RegionServer中都會有一個HLog的例項,RegionServer會將更新操作(如 Put,Delete)先記錄到 WAL(也就是HLo)中,然後將其寫入到Store的MemStore,最終MemStore會將資料寫入到持久化的HFile中(MemStore 到達配置的記憶體閥值)。這樣就保證了HBase的寫的可靠性。如果沒有 WAL,當RegionServer宕掉的時候,MemStore 還沒有寫入到HFile,或者StoreFile還沒有儲存,資料就會丟失。或許有的讀者會擔心HFile本身會不會丟失,這是由 HDFS 來保證的。在HDFS中的資料預設會有3份。因此這裡並不考慮 HFile 本身的可靠性。

HFile由很多個數據塊(Block)組成,並且有一個固定的結尾塊。其中的資料塊是由一個Header和多個Key-Value的鍵值對組成。在結尾的資料塊中包含了資料相關的索引資訊,系統也是通過結尾的索引資訊找到HFile中的資料。

3.2 元件高可用

  • Master容錯:Zookeeper重新選擇一個新的Master。如果無Master過程中,資料讀取仍照常進行,但是,region切分、負載均衡等無法進行;
  • RegionServer容錯:定時向Zookeeper彙報心跳,如果一旦時間內未出現心跳,Master將該RegionServer上的Region重新分配到其他RegionServer上,失效伺服器上“預寫”日誌由主伺服器進行分割並派送給新的RegionServer;
  • Zookeeper容錯:Zookeeper是一個可靠地服務,一般配置3或5個Zookeeper例項。

4. HBase讀寫流程

上圖是RegionServer資料儲存關係圖。上文提到,HBase使用MemStore和StoreFile儲存對錶的更新。資料在更新時首先寫入HLog和MemStore。MemStore中的資料是排序的,當MemStore累計到一定閾值時,就會建立一個新的MemStore,並且將老的MemStore新增到Flush佇列,由單獨的執行緒Flush到磁碟上,成為一個StoreFile。與此同時,系統會在Zookeeper中記錄一個CheckPoint,表示這個時刻之前的資料變更已經持久化了。當系統出現意外時,可能導致MemStore中的資料丟失,此時使用HLog來恢復CheckPoint之後的資料。
StoreFile是隻讀的,一旦建立後就不可以再修改。因此Hbase的更新其實是不斷追加的操作。當一個Store中的StoreFile達到一定閾值後,就會進行一次合併操作,將對同一個key的修改合併到一起,形成一個大的StoreFile。當StoreFile的大小達到一定閾值後,又會對 StoreFile進行切分操作,等分為兩個StoreFile。

4.1 寫操作流程

  • (1) Client通過Zookeeper的排程,向RegionServer發出寫資料請求,在Region中寫資料。
  • (2) 資料被寫入Region的MemStore,直到MemStore達到預設閾值。
  • (3) MemStore中的資料被Flush成一個StoreFile。
  • (4) 隨著StoreFile檔案的不斷增多,當其數量增長到一定閾值後,觸發Compact合併操作,將多個StoreFile合併成一個StoreFile,同時進行版本合併和資料刪除。
  • (5) StoreFiles通過不斷的Compact合併操作,逐步形成越來越大的StoreFile。
  • (6) 單個StoreFile大小超過一定閾值後,觸發Split操作,把當前Region Split成2個新的Region。父Region會下線,新Split出的2個子Region會被HMaster分配到相應的RegionServer上,使得原先1個Region的壓力得以分流到2個Region上。

可以看出HBase只有增添資料,所有的更新和刪除操作都是在後續的Compact歷程中舉行的,使得使用者的寫操作只要進入記憶體就可以立刻返回,實現了HBase I/O的高機能。

4.2 讀操作流程

  • (1) Client訪問Zookeeper,查詢-ROOT-表,獲取.META.表資訊。
  • (2) 從.META.表查詢,獲取存放目標資料的Region資訊,從而找到對應的RegionServer。
  • (3) 通過RegionServer獲取需要查詢的資料。
  • (4) Regionserver的記憶體分為MemStore和BlockCache兩部分,MemStore主要用於寫資料,BlockCache主要用於讀資料。讀請求先到MemStore中查資料,查不到就到BlockCache中查,再查不到就會到StoreFile上讀,並把讀的結果放入BlockCache。

定址過程:client-->Zookeeper-->-ROOT-表-->.META.表-->RegionServer-->Region-->client

5.HBase列族不能太多,會引發的問題

HRegionServer內部管理了一系列HRegion物件,每個HRegion對 應了table中的一個region,HRegion中由多 個HStore組成。每個HStore對應了Table中的一個column family的儲存,可以看出每個columnfamily其實就是一個集中的儲存單元,因此最好將具備共同IO特性的column放在一個column family中,這樣最高效。

HStore儲存是HBase儲存的核心,由兩部分組成,一部分是MemStore,一 部分是StoreFile。MemStore是 Sorted Memory Buffer,使用者寫入的資料首先會放入MemStore,當MemStore滿了以後會Flush成一個StoreFile(底層實現是HFile)。


進行split的條件:該regiion下所有的storeFile中最大的storeFile大小超過閥值即進行spliet
在檔案層次上,不同的列族,儲存在不同的檔案中。但是不同的列族,可能會共享一個region。



/hbase/zz/3917ebd872c0adcb9d6c5a9cfd30b87f/a

/hbase/zz/3917ebd872c0adcb9d6c5a9cfd30b87f/a/9210131397650425238
/hbase/zz/3917ebd872c0adcb9d6c5a9cfd30b87f/b
/hbase/zz/3917ebd872c0adcb9d6c5a9cfd30b87f/b/7083844554431109536

如上所示:兩個不同的列族,共享了同一個region(3917ebd872c0adcb9d6c5a9cfd30b87f)。

由於不同的列族會共享region,所以有可能出現,一個列族已經有1000萬行,而另外一個才100行。當一個要求region分割的時候,會導致100行的列會同樣分佈到多個region中。
這樣就出現了基數問題。(如果表存在多個列族,列族A有100萬行,列族B有10億行,那麼列族A可能會被分散到很多個Region上,這會導致掃描列族A的效能低下)


(某個column family在flush的時候,它鄰近的column family也會因關聯效應被觸發flush,最終導致系統產生更多的I/O)
所以,一般建議不要設定多個列族。