【轉】HBase原理和設計

簡介

HBase —— Hadoop Database的簡稱,Google BigTable的另外一種開源實現方式,從問世之初,就爲了解決用大量廉價的機器高速存取海量數據、實現數據分佈式存儲提供可靠的方案。從功能上來說,HBase徹徹底底是一個數據庫,與咱們熟悉的Oracle、MySQL、MSSQL等同樣,對外提供數據的存儲和讀取服務。而從應用的角度來講,HBase與通常的數據庫又有所區別,HBase自己的存取接口至關簡單,不支持複雜的數據存取,更不支持SQL等結構化的查詢語言;HBase也沒有除了rowkey之外的索引,全部的數據分佈和查詢都依賴rowkey。因此,HBase在表的設計上會有很嚴格的要求。架構上,HBase是分佈式數據庫的典範,這點比較像MongoDB的sharding模式,能根據鍵值的大小,把數據分佈到不一樣的存儲節點上,MongoDB根據configserver來定位數據落在哪一個分區上,HBase經過訪問Zookeeper來獲取-ROOT-表所在地址,經過-ROOT-表獲得相應.META.表信息,從而獲取數據存儲的region位置。數據庫

 

架構

上面提到,HBase是一個分佈式的架構,除去底層存儲的HDFS外,HBase自己從功能上能夠分爲三塊:Zookeeper羣、Master羣和RegionServer羣。緩存

  • Zookeeper羣:HBase集羣中不可缺乏的重要部分,主要用於存儲Master地址、協調Master和RegionServer等上下線、存儲臨時數據等等。
  • Master羣:Master主要是作一些管理操做,如:region的分配,手動管理操做下發等等,通常數據的讀寫操做並不須要通過Master集羣,因此Master通常不須要很高的配置便可。
  • RegionServer羣:RegionServer羣是真正數據存儲的地方,每一個RegionServer由若干個region組成,而一個region維護了必定區間rowkey值的數據,整個結構以下圖:
hbase

HBase結構圖安全

上圖中,Zookeeper(簡稱ZK)是一個集羣,一般有奇數個ZK服務組成。Master爲了服務可用性,也建議部署成集羣方式,由於Master是整個管理操做的發起者,若是Master一旦發生意外停機,整個集羣將會沒法進行管理操做,因此Master也必須有多個,固然多個Master也有主從之分,如何區分哪一個是主,哪一個是從?關鍵看哪一個Master能競爭到ZK上對應Master目錄下的鎖,持有該目錄鎖的Master爲主Master,其餘從Master輪詢競爭該鎖,因此一旦主Master發生意外停機,從Master很快會由於競爭到Master文件夾上的鎖而接管服務。
RegionServer(簡稱RS)在非Replication模式下,整個系統中都是惟一的,也就是說,在整個非Replication的HBase集羣中,每臺RS上保存的數據都不同,因此相對於前面二者,該模式下的RS並非高可用的,至少RS可能存在單點故障的問題,可是因爲HBase內部數據分region存儲和region能夠遷移的機制,RS服務的單點故障可能會在極小代價下很快恢復,可是一旦停掉的RS上有-ROOT-或者.META.表的region,那後果仍是比較嚴重,由於數據節點的RS停機,只會在短期內影響該臺RS上的region不可訪問,等到region遷移完成後便可恢復,若是是-ROOT-、.META.所在的RS停機,整個HBase的新的求情都將受到影響,由於須要經過.META.表來路由,從而尋找到region所在RS的地址。架構

數據組織

整個架構中,ZK用於服務協調和整個集羣運行過程當中部分信息的保存和-ROOT-表地址定位,Master用於集羣內部管理,因此剩下的RS主要用於處理數據。
RS是處理數據的主要場所,那麼在RS內部的數據是怎麼分佈的?其實RS自己只是一個容器,其定義了一些功能線程,好比:數據合併線程(compact thread)、storeFile分割線程(split thread)等等。容器中的主要對象就是region,region是一個表根據自身rowkey範圍劃分的一部分,一個表能夠被劃分紅若干部分,也就是若干個region,region能夠根據rowkey範圍不一樣而被分佈在不一樣的RS上(固然也能夠在同一個RS上,但不建議這麼作)。一個RS上能夠包含多個表的region,也能夠只包含一個表的部分region,RS和表是兩個不一樣的概念。
這裏還有一個概念——列簇。對HBase有一些瞭解的人,或多或少據說過:HBase是一個列式存儲的數據庫,而這個列式存儲中的列,實際上是區別於通常數據庫的列,這裏的列的概念,就是列簇,列簇,顧名思義就是不少列的集合,而在數據存儲上來說,不一樣列簇的數據,必定是分開存儲的,即便是在同一個region內部,不一樣的列簇也存儲在不一樣的文件夾中,這樣作的好處是,通常咱們定義列簇的時候,一般會把相似的數據放入同一個列簇,不一樣的列簇分開存儲,有利於數據的壓縮,而且HBase自己支持多種壓縮方式。app

原理

前面介紹了HBase的通常架構,咱們知道了HBase有ZK、Master和RS等組成,本節咱們來介紹下HBase的基本原理,從數據訪問、RS路由到RS內部緩存、數據存儲和刷寫再到region的合併和拆分等等功能。異步

RegionServer定位

訪問HBase經過HBase客戶端(或API)進行,整個HBase提供給外部的地址,實際上是ZK的入口,前面也介紹了,ZK中有保存-ROOT-所在的RS地址,從-ROOT-表能夠獲取.META.表信息,根據.META.表能夠獲取region在RS上的分佈,整個region尋址過程大體以下:分佈式

direct

RS定位過程oop

  1. 首先,Client經過訪問ZK來請求目標數據的地址。
  2. ZK中保存了-ROOT-表的地址,因此ZK經過訪問-ROOT-表來請求數據地址。
  3. 一樣,-ROOT-表中保存的是.META.的信息,經過訪問.META.表來獲取具體的RS。
  4. .META.表查詢到具體RS信息後返回具體RS地址給Client。
  5. Client端獲取到目標地址後,而後直接向該地址發送數據請求。

上述過程實際上是一個三層索引結構,從ZK獲取-ROOT-信息,再從-ROOT-獲取.META.表信息,最後從.META.表中查到RS地址後緩存。這裏有幾個問題:性能

  • 既然ZK中能保存-ROOT-信息,那麼爲何不把.META.信息直接保存在ZK中,而須要經過-ROOT-表來定位?
  • Client查找到目標地址後,下一次請求還須要走ZK  —> -ROOT- —> .META.這個流程麼?

先來回答第一個問題:爲何不直接把.META.表信息直接保存到ZK中?主要是爲了保存的數據量考慮,ZK中不宜保存大量數據,而.META.表主要是保存Region和RS的映射信息,region的數量沒有具體約束,只要在內存容許的範圍內,region數量能夠有不少,若是保存在ZK中,ZK的壓力會很大。因此,經過一個-ROOT-表來轉存到RS中是一個比較理想的方案,相比直接保存在ZK中,也就多了一層-ROOT-表的查詢,對性能來講影響不大。
第二個問題:每次訪問都須要走ZK –> -ROOT- —> .META.的流程麼?固然不須要,Client端有緩存,第一次查詢到相應region所在RS後,這個信息將被緩存到Client端,之後每次訪問都直接從緩存中獲取RS地址便可。固然這裏有個意外:訪問的region若果在RS上發生了改變,好比被balancer遷移到其餘RS上了,這個時候,經過緩存的地址訪問會出現異常,在出現異常的狀況下,Client須要從新走一遍上面的流程來獲取新的RS地址。整體來講,region的變更只會在極少數狀況下發生,通常變更不會很大,因此在整個集羣訪問過程當中,影響能夠忽略。操作系統

Region數據寫入

HBase經過ZK —> -ROOT-  —> .META.的訪問獲取RS地址後,直接向該RS上進行數據寫入操做,整個過程以下圖:

data_write

RegionServer數據操做過程

Client經過三層索引得到RS的地址後,便可向指定RS的對應region進行數據寫入,HBase的數據寫入採用WAL(write ahead log)的形式,先寫log,後寫數據。HBase是一個append類型的數據庫,沒有關係型數據庫那麼複雜的操做,因此記錄HLog的操做都是簡單的put操做(delete/update操做都被轉化爲put進行)

HLog

HLog寫入

HLog是HBase實現WAL方式產生的日誌信息,其內部是一個簡單的順序日誌,每一個RS上的region都共享一個HLog,全部對於該RS上的region數據寫入都被記錄到該HLog中。HLog的主要做用就是在RS出現意外崩潰的時候,能夠儘可能多的恢復數據,這裏說是儘可能多,由於在通常狀況下,客戶端爲了提升性能,會把HLog的auto flush關掉,這樣HLog日誌的落盤全靠操做系統保證,若是出現意外崩潰,短期內沒有被fsync的日誌會被丟失。

HLog過時

HLog的大量寫入會形成HLog佔用存儲空間會愈來愈大,HBase經過HLog過時的方式進行HLog的清理,每一個RS內部都有一個HLog監控線程在運行,其週期能夠經過hbase.master.cleaner.interval進行配置。
HLog在數據從memstore flush到底層存儲上後,說明該段HLog已經再也不被須要,就會被移動到.oldlogs這個目錄下,HLog監控線程監控該目錄下的HLog,當該文件夾下的HLog達到hbase.master.logcleaner.ttl設置的過時條件後,監控線程當即刪除過時的HLog。

Memstore

數據存儲

memstore是region內部緩存,其大小經過HBase參數hbase.hregion.memstore.flush.size進行配置。RS在寫完HLog之後,數據寫入的下一個目標就是region的memstore,memstore在HBase內部經過LSM-tree結構組織,因此可以合併大量對於相同rowkey上的更新操做。
正是因爲memstore的存在,HBase的數據寫入都是異步的,並且性能很是不錯,寫入到memstore後,該次寫入請求就能夠被返回,HBase即認爲該次數據寫入成功。這裏有一點須要說明,寫入到memstore中的數據都是預先按照rowkey的值進行排序的,這樣有利於後續數據查找。

數據刷盤

memstore中的數據在必定條件下會進行刷寫操做,使數據持久化到相應的存儲設備上,觸發memstore刷盤的操做有多種不一樣的方式以下圖:

flush

Memstore刷寫流程

以上1,2,3均可以觸發memstore的flush操做,可是實現的方式不一樣:

  • 1經過全局內存控制,觸發memstore刷盤操做。memstore總體內存佔用上限經過參數hbase.regionserver.global.memstore.upperLimit進行設置,固然在達到上限後,memstore的刷寫也不是一直進行,在內存降低到hbase.regionserver.global.memstore.lowerLimit配置的值後,即中止memstore的刷盤操做。這樣作,主要是爲了防止長時間的memstore刷盤,會影響總體的性能。
  • 在該種狀況下,RS中全部region的memstore內存佔用都沒達到刷盤條件,但總體的內存消耗已經到一個很是危險的範圍,若是持續下去,頗有可能形成RS的OOM,這個時候,須要進行memstore的刷盤,從而釋放內存。
  • 2手動觸發memstore刷盤操做
  • HBase提供API接口,運行經過外部調用進行memstore的刷盤
  • 3 memstore上限觸發數據刷盤
  • 前面提到memstore的大小經過hbase.hregion.memstore.flush.size進行設置,當region中memstore的數據量達到該值時,會自動觸發memstore的刷盤操做。

刷盤影響

memstore在不一樣的條件下會觸發數據刷盤,那麼整個數據在刷盤過程當中,對region的數據寫入等有什麼影響?memstore的數據刷盤,對region的直接影響就是:在數據刷盤開始到結束這段時間內,該region上的訪問都是被拒絕的,這裏主要是由於在數據刷盤結束時,RS會對改region作一個snapshot,同時HLog作一個checkpoint操做,通知ZK哪些HLog能夠被移到.oldlogs下。從前面圖上也能夠看到,在memstore寫盤開始,相應region會被加上UpdateLock鎖,寫盤結束後該鎖被釋放。

StoreFile

memstore在觸發刷盤操做後會被寫入底層存儲,每次memstore的刷盤就會相應生成一個存儲文件HFile,storeFile即HFile在HBase層的輕量級分裝。數據量的持續寫入,形成memstore的頻繁flush,每次flush都會產生一個HFile,這樣底層存儲設備上的HFile文件數量將會愈來愈多。無論是HDFS仍是Linux下經常使用的文件系統如Ext四、XFS等,對小而多的文件上的管理都沒有大文件來的有效,好比小文件打開須要消耗更多的文件句柄;在大量小文件中進行指定rowkey數據的查詢性能沒有在少許大文件中查詢來的快等等。

Compact

大量HFile的產生,會消耗更多的文件句柄,同時會形成RS在數據查詢等的效率大幅度降低,HBase爲解決這個問題,引入了compact操做,RS經過compact把大量小的HFile進行文件合併,生成大的HFile文件。
RS上的compact根據功能的不一樣,能夠分爲兩種不一樣類型,即:minor compact和major compact。

  • Minor Compact

minor compact又叫small compact,在RS運行過程當中會頻繁進行,主要經過參數hbase.hstore.compactionThreshold進行控制,該參數配置了HFile數量在知足該值時,進行minor compact,minor compact只選取region下部分HFile進行compact操做,而且選取的HFile大小不能超過hbase.hregion.max.filesize參數設置。

  • Major Compact

相反major compact也被稱之爲large compact,major compact會對整個region下相同列簇的全部HFile進行compact,也就是說major compact結束後,同一個列簇下的HFile會被合併成一個。major compact是一個比較長的過程,對底層I/O的壓力相對較大。
major compact除了合併HFile外,另一個重要功能就是清理過時或者被刪除的數據。前面提到過,HBase的delete操做也是經過append的方式寫入,一旦某些數據在HBase內部被刪除了,在內部只是被簡單標記爲刪除,真正在存儲層面沒有進行數據清理,只有經過major compact對HFile進行重組時,被標記爲刪除的數據才能被真正的清理。
compact操做都有特定的線程進行,通常狀況下不會影響RS上數據寫入的性能,固然也有例外:在compact操做速度跟不上region中HFile增加速度時,爲了安全考慮,RS會在HFile達到必定數量時,對寫入進行鎖定操做,直到HFile經過compact降到必定的範圍內才釋放鎖。

Split

compact將多個HFile合併單個HFile文件,隨着數據量的不斷寫入,單個HFile也會愈來愈大,大量小的HFile會影響數據查詢性能,大的HFile也會,HFile越大,相對的在HFile中搜索的指定rowkey的數據花的時間也就越長,HBase一樣提供了region的split方案來解決大的HFile形成數據查詢時間過長問題。
一個較大的region經過split操做,會生成兩個小的region,稱之爲Daughter,通常Daughter中的數據是根據rowkey的之間點進行切分的,region的split過程大體以下圖:

hbase_split

region split流程

  1. region先更改ZK中該region的狀態爲SPLITING。
  2. Master檢測到region狀態改變。
  3. region會在存儲目錄下新建.split文件夾用於保存split後的daughter region信息。
  4. Parent region關閉數據寫入並觸發flush操做,保證全部寫入Parent region的數據都能持久化。
  5. 在.split文件夾下新建兩個region,稱之爲daughter A、daughter B。
  6. Daughter A、Daughter B拷貝到HBase根目錄下,造成兩個新的region。
  7. Parent region通知修改.META.表後下線,再也不提供服務。
  8. Daughter A、Daughter B上線,開始向外提供服務。
  9. 若是開啓了balance_switch服務,split後的region將會被從新分佈。

上面1 ~ 9就是region split的整個過程,split過程很是快,速度基本會在秒級內,那麼在這麼快的時間內,region中的數據怎麼被從新組織的?
其實,split只是簡單的把region從邏輯上劃分紅兩個,並無涉及到底層數據的重組,split完成後,Parent region並無被銷燬,只是被作下線處理,再也不對外部提供服務。而新產生的region Daughter A和Daughter B,內部的數據只是簡單的到Parent region數據的索引,Parent region數據的清理在Daughter A和Daughter B進行major compact之後,發現已經沒有到其內部數據的索引後,Parent region纔會被真正的清理。

HBase設計

HBase是一個分佈式數據庫,其性能的好壞主要取決於內部表的設計和資源的分配是否合理。

Rowkey設計

rowkey是HBase實現分佈式的基礎,HBase經過rowkey範圍劃分不一樣的region,分佈式系統的基本要求就是在任什麼時候候,系統的訪問都不要出現明顯的熱點現象,因此rowkey的設計相當重要,通常咱們建議rowkey的開始部分以hash或者MD5進行散列,儘可能作到rowkey的頭部是均勻分佈的。禁止採用時間、用戶id等明顯有分段現象的標誌直接看成rowkey來使用。

列簇設計

HBase的表設計時,根據不一樣需求有不一樣選擇,須要作在線查詢的數據表,儘可能不要設計多個列簇,咱們知道,不一樣的列簇在存儲上是被分開的,多列簇設計會形成在數據查詢的時候讀取更多的文件,從而消耗更多的I/O。

TTL設計

選擇合適的數據過時時間也是表設計中須要注意的一點,HBase中容許列簇定義數據過時時間,數據一旦超過過時時間,能夠被major compact進行清理。大量無用歷史數據的殘餘,會形成region體積增大,影響查詢效率。

Region設計

通常地,region不宜設計成很大,除非應用對階段性性能要求不少,可是在未來運行一段時間能夠接受停服處理。region過大會致使major compact調用的週期變長,而單次major compact的時間也相應變長。major compact對底層I/O會形成壓力,長時間的compact操做可能會影響數據的flush,compact的週期變長會致使許多刪除或者過時的數據不能被及時清理,對數據的讀取速度等都有影響。
相反,小的region意味着major compact會相對頻繁,可是因爲region比較小,major compact的相對時間較快,並且相對較多的major compact操做,會加速過時數據的清理。
固然,小region的設計意味着更多的region split風險,region容量太小,在數據量達到上限後,region須要進行split來拆分,其實split操做在整個HBase運行過程當中,是被不怎麼但願出現的,由於一旦發生split,涉及到數據的重組,region的再分配等一系列問題。因此咱們在設計之初就須要考慮到這些問題,儘可能避免region的運行過程當中發生split。
HBase能夠經過在表建立的時候進行region的預分配來解決運行過程當中region的split產生,在表設計的時候,預先分配足夠多的region數,在region達到上限前,至少有部分數據會過時,經過major compact進行清理後, region的數據量始終維持在一個平衡狀態。
region數量的設計還須要考慮內存上的限制,經過前面的介紹咱們知道每一個region都有memstore,memstore的數量與region數量和region下列簇的數量成正比,一個RS下memstore內存消耗:

Memory = memstore大小 * region數量 * 列簇數量

若是不進行前期數據量估算和region的預分配,經過不斷的split產生新的region,容易致使由於內存不足而出現OOM現象。

相關文章
相關標籤/搜索