HBase學習-HBase原理

1.系統架構java

1.1 圖解node


 

從HBase的架構圖上能夠看出,HBase中的組件包括Client、Zookeeper、HMaster、HRegionServer、HRegion、Store、MemStore、StoreFile、HFile、HLog等,每個 RegionServer 就只有一個 HLog,而不是一個 Region 有一個 HLog。mysql

1.2 client算法

HBase 有兩張特殊表:sql

1).META.:記錄了用戶全部表拆分出來的的Region映射信息,.META.能夠有多個 Regoin shell

2)-ROOT-:記錄了.META.表的Region信息,-ROOT-只有一個Region,不管如何不會分裂api

Client訪問用戶數據前須要首先訪問ZooKeeper,找到-ROOT-表的 Region所在的位置,而後訪問-ROOT-表,接着訪問.META.表,最後才能找到用戶數據的位置去訪問,中間須要屢次網絡操做,不過client端會作cache緩存。數組

所以,client包含了訪問HBase的接口,另外Client還維護了對應的cache來加速HBase的訪問,好比cache的.META.元數據的信息。緩存

1.3 ZooKeeper安全

Hbase經過Zookeeper來作master的高可用、RegionServer的監控、元數據的入口以及集羣配置的維護等工做。具體工做以下:

1)爲HBase提供Failover機制,保障羣衆只有1個master在運行,若是有master異常,會經過競爭機制選舉新的Master提供服務,避免單點Master單點故障問題

2)存儲全部Region的尋址入口:-ROOT-表在哪臺服務器上。-ROOT-這張表的位置信息

3)實時監控RegionServer的狀態,當RegionServer有異常的時候,經過回調將RegionServer的上線和下線信息實時通知給Master

4)存儲HBase的Schema,包括有哪些Table,每一個Table有哪些Column Family

1.4 HMaster

一、爲RegionServer 分配 Region

二、負責 RegionServer的負載均衡

三、發現失效的RegionServer並從新分配其上的Region。當RegionServer失效的時候,協調對應的Hlog的拆分

四、HDFS上的垃圾文件(HBase)回收

五、維護集羣的元數據信息,處理Schema更新請求(表的建立,刪除,修改,列簇的增長等等)

1.5 HRegionServer

HregionServer直接對接用戶的讀寫請求,是真正的「幹活」的節點。它的功能歸納以下:

1)RegionServer維護Master分配給它的Region,處理對這些Region的IO請求

2)RegionServer負責Split在運行過程當中變得過大的Region,負責Compact操做

3)負責和底層HDFS交互,存儲數據到HDFS

4)負責Storefile合併工做

能夠看到,client訪問HBase上數據的過程並不須要master參與(尋址訪問zookeeper和RegioneServer,數據讀寫訪問RegioneServer),Master僅僅維護者Table和Region的元數據信息,負載很低。

.META. 存的是全部的Region的位置信息,那麼RegioneServer 當中 Region在進行分裂以後的新產生的Region,是由Master來決定發到哪一個RegioneServer,這就意味着,只有Master知道newRegion的位置信息,因此,由Master來管理.META.這個表當中的數據的CRUD,因此結合以上兩點代表,在沒有Region分裂的狀況,Master宕機一段時間是能夠忍受的。

1.6 HRegion

table在行的方向上分隔爲多個Region。Region是HBase中分佈式存儲和負載均衡的最小單元,即不一樣的region能夠分別在不一樣的Region Server上,但同一個Region是不會拆分到多個server上。

Region按大小分隔,每一個表通常是隻有一個region。隨着數據不斷插入表,region不斷增大,當region的某個列族達到一個閾值時就會分紅兩個新的region。

每一個region由如下信息標識:< 表名,startRowkey,建立時間>

由目錄表(-ROOT-和.META.)記錄該region的endRowkey

1.7 Store

每個region由一個或多個store組成,至少是一個store,hbase會把一塊兒訪問的數據放在一個store裏面,即爲每一個 ColumnFamily建一個store,若是有幾個ColumnFamily,也就有幾個Store。一個Store由一個memStore和0或者 多個StoreFile組成。HBase以store的大小來判斷是否須要切分region

1.8 MemStore

memStore 是放在內存裏的。保存修改的數據即keyValues。當memStore的大小達到一個閥值(默認128MB)時,memStore會被flush到文件,即生成一個快照。目前hbase會有一個線程來負責memStore的flush操做。

1.9 StoreFile

memStore內存中的數據寫到文件後就是StoreFile,StoreFile底層是以HFile的格式保存。

1.10 HFile

HBase中KeyValue數據的存儲格式,HFile是Hadoop的二進制格式文件,實際上StoreFile就是對Hfile作了輕量級包裝,即StoreFile底層就是HFile

1.11 HLog

1.11.1 簡介

HLog(WAL log):是HBase實現WAL(write ahead log)方式產生的日誌,內部是一個簡單的順序日誌,用來作災難恢復使用。每一個RegionServer對應一個Hlog,全部對於該RegionServer的寫入都記錄到Hlog中,一旦region server宕機,就能夠從log中進行恢復。此外爲了保證恢復的效率,Hbase會限制最大保存的Hlog數量,若是達到Hlog的最大個數(hase.regionserver.max.logs參數控制)的時候,就會觸發強制刷盤操做。對於已經刷盤的數據,其對應的Hlog會有一個過時的概念,Hlog過時後,會被監控線程移動到.oldlogs,而後會被自動刪除掉。

HLog文件就是一個普通的Hadoop Sequence File,Sequence File的value是key時HLogKey對象,其中記錄了寫入數據的歸屬信息,除了table和region名字外,還同時包括sequence number和timestamp,timestamp是寫入時間,sequence number的起始值爲0,或者是最近一次存入文件系統中的sequence number。Sequence File的value是HBase的KeyValue對象,即對應HFile中的KeyValue。

1.11.2 Hlog結構

下圖是Hlog的詳細結構:


 

從上圖咱們能夠看出多個Region共享一個Hlog文件,單個Region在Hlog中是按照時間順序存儲的,可是多個Region可能並非徹底按照時間順序。

每一個Hlog最小單元由Hlogkey和WALEdit兩部分組成。Hlogkey由sequenceid、timestamp、cluster ids、regionname以及tablename等組成,WALEdit是由一系列的KeyValue組成,對一行上全部列(即全部KeyValue)的更新操做,都包含在同一個WALEdit對象中,這主要是爲了實現寫入一行多個列時的原子性。

注意,sequenceid是一個store級別的自增序列號,很是重要,region的數據恢復和Hlog過時清除都要依賴sequenceid。

Memstore在達到必定的條件會觸發刷盤的操做,刷盤的時候會獲取刷新到最新的一個sequenceid的下一個sequenceid,並將新的sequenceid賦給oldestUnflushedSequenceId,並刷到Ffile中。舉個例子來講明:好比對於某一個store,開始的時候oldestUnflushedSequenceId爲NULL,此時,若是觸發flush的操做,假設初始刷盤到sequenceid爲10,那麼hbase會在10的基礎上append一個空的Entry到HLog,最新的sequenceid爲11,而後將sequenceid爲11的號賦給oldestUnflushedSequenceId,並將oldestUnflushedSequenceId的值刷到Hfile文件中進行持久化。

Hlog文件對應全部Region的store中最大的sequenceid若是已經刷盤,就認爲Hlog文件已通過期,就會移動到.oldlogs,等待被移除。

當RegionServer出現故障的時候,須要對Hlog進行回放來恢復數據。回放的時候會讀取Hfile的oldestUnflushedSequenceId中的sequenceid和Hlog中的sequenceid進行比較,小於sequenceid的就直接忽略,但與或者等於的就進行重作。回放完成後,就完成了數據的恢復工做。

1.11.3 Hlog的生命週期

Hlog從產生到最後刪除須要經歷以下幾個過程:

1.11.3.1 產生

全部涉及到數據的變動都會先寫Hlog,除非是你關閉了Hlog

1.11.3.2 滾動

Hlog的大小經過參數hbase.regionserver.logroll.period控制,默認是1個小時,時間達到hbase.regionserver.logroll.period設置的時間,Hbase會建立一個新的Hlog文件。這就實現了Hlog滾動的目的。Hbase經過hbase.regionserver.maxlogs參數控制Hlog的個數。滾動的目的,爲了控制單個Hlog文件過大的狀況,方便後續的過時和刪除。

1.11.3.3 過時

Hlog的過時依賴於對sequenceid的判斷。Hbase會將Hlog的sequenceid和Hfile最大的sequenceid(刷新到的最新位置)進行比較,若是該Hlog文件中的sequenceid比刷新的最新位置的sequenceid都要小,那麼這個Hlog就過時了,過時了之後,對應Hlog會被移動到.oldlogs目錄。

這裏有個問題,爲何要將過時的Hlog移動到.oldlogs目錄,而不是直接刪除呢?答案是由於Hbase還有一個主從同步的功能,這個依賴Hlog來同步Hbase的變動,有一種狀況不能刪除Hlog,那就是Hlog雖然過時,可是對應的Hlog並無同步完成,所以比較好的作法是移動到別的目錄。再增長對應的檢查和保留時間。

1.11.3.4 刪除

若是Hbase開啓了replication,當replication執行完一個Hlog的時候,會刪除Zoopkeeper上的對應Hlog節點。在Hlog被移動到.oldlogs目錄後,Hbase每隔hbase.master.cleaner.interval(默認60秒)時間會去檢查.oldlogs目錄下的全部Hlog,確認對應的Zookeeper的Hlog節點是否被刪除,若是Zookeeper上不存在對應的Hlog節點,那麼就直接刪除對應的Hlog。

hbase.master.logcleaner.ttl(默認10分鐘)這個參數設置Hlog在.oldlogs目錄保留的最長時間。

1.12 HDFS

HDFS爲HBase提供最終的底層數據存儲服務,同事爲HBase提供高可用(Hlog存儲在HDFS)的支持,具體功能有:

提供元數據和表數據的底層分佈式存儲服務

數據多副本,提供的高可靠和高可用性

2 物理存儲

2.1 總體的物理結構


 

一、Table 中的全部行都按照 RowKsey 的字典序排列。

二、Table 在行的方向上分割爲多個 HRegion。

三、HRegion按大小分割的(默認10G),每一個表一開始只有一個HRegion,隨着數據不斷插入表,HRegion不斷增大,當增大到一個閥值的時候,HRegion就會等分會兩個新的 HRegion。當表中的行不斷增多,就會有愈來愈多的 HRegion。


 

四、HRegion 是Hbase中分佈式存儲和負載均衡的最小單元。最小單元就表示不一樣的HRegion能夠分佈在不一樣的HRegionserver 上。但一個 HRegion 是不會拆分到多個server 上的。

五、HRegion 雖然是負載均衡的最小單元,但並非物理存儲的最小單元。事實上,HRegion由一個或者多個Store 組成,每一個 Store 保存一個 ColumnFamily。每一個Strore 又由一個 memStore 和 0 至多個 StoreFile 組成

2.2 StoreFile 和 HFile 結構

StoreFile 以 HFile 格式保存在 HDFS 上,請看下圖 HFile 的數據組織格式:


 

首先HFile文件是不定長的,長度固定的只有其中的兩塊:Trailer 和 FileInfo。Trailer中有指針指向其餘數據塊的起始點。FileInfo中記錄了文件的一些Meta信息,例如:AVG_KEY_LEN,AVG_VALUE_LEN,LAST_KEY,COMPARATOR, MAX_SEQ_ID_KEY 等。

HFile 分爲六個部分:

Data Block 段–保存表中的數據,這部分能夠被壓縮

Meta Block 段(可選的)–保存用戶自定義的kv對,能夠被壓縮。

File Info 段–Hfile的元信息,不被壓縮,用戶也能夠在這一部分添加本身的元信息。

Data Block Index 段–Data Block的索引。每條索引的 key 是被索引的 block 的第一條記錄的 key。

Meta Block Index 段 (可選的)–Meta Block 的索引。

Trailer 段–這一段是定長的。保存了每一段的偏移量,讀取一個 HFile時,會首先讀取Trailer,Trailer保存了每一個段的起始位置(段的Magic Number用來作安全check),而後,DataBlock Index 會被讀取到內存中,這樣,當檢索某個key時,不須要掃描整個 HFile,而只需從內存中找到key所在的block,經過一次磁盤io將整個block讀取到內存中,再找到須要的key。DataBlock Index 採用 LRU 機制淘汰。

HFile的Data Block,Meta Block一般採用壓縮方式存儲,壓縮以後能夠大大減小網絡 IO 和磁盤IO,隨之而來的開銷固然是須要花費cpu進行壓縮和解壓縮。目標Hfile的壓縮支持兩種方式:Gzip,LZO。

Data Index 和 Meta Index 塊記錄了每一個 Data 塊和 Meta 塊的起始點。

Data Block是HBase I/O的基本單元,爲了提升效率,HRegionServer中有基於 LRU 的 Block Cache機制。每一個Data塊的大小能夠在建立一個 Table的時候經過參數指定,大號的Block有利於順序 Scan,小號Block利於隨機查詢。每一個Data塊除了開頭的Magic之外就是一個個KeyValue對拼接而成,Magic內容就是一些隨機數字,目的是防止數據損壞。

HFile 裏面的每一個KeyValue對就是一個簡單的byte數組。可是這個 byte數組裏麪包含了不少項,而且有固定的結構。咱們來看看裏面的具體結構:


 

開始是兩個固定長度的數值,分別表示Key的長度和Value 的長度。緊接着是Key,開始是固定長度的數值,表示RowKey 的長度,緊接着是RowKey,而後是固定長度的數值,表示 Family 的長度,而後是Family,接着是Qualifier,而後是兩個固定長度的數值,表示 Time Stamp 和 Key Type(Put/Delete)。Value部分沒有這麼複雜的結構,就是純粹的二進制數據了。

2.3 MemStore 和 StoreFile

一個 Hregion 由多個 Store 組成,每一個 Store 包含一個列族的全部數據。

Store 包括位於內存的一個 memstore 和位於硬盤的多個 storefile 組成。

寫操做先寫入 memstore,當memstore中的數據量達到某個閾值,HRegionServer啓動flushcache進程寫入storefile,每次寫入造成單獨一個 Hfile。

當總 storefile大小超過必定閾值後,會把當前的region 分割成兩個,並由HMaster分配給相應的region服務器,實現負載均衡。

客戶端檢索數據時,先在memstore找,找不到再找storefile。

2.4 Hbase WAL HLog預寫

WAL意爲Write.ahead.log,相似mysql中的binlog,用來作災難恢復之用,Hlog記錄數據的全部變動,一旦數據修改,就能夠從 log 中 進行恢復。

每一個 Region Server 維護一個 Hlog,而不是每一個 Region 一個。這樣不一樣region(來自不一樣table)的日誌會混在一塊兒,這樣作的目的是不斷追加單個文件相對於同時寫多個文件而言,能夠減小磁盤尋址次數,所以能夠提升對table 的寫性能。帶來的麻煩是,若是一臺 region server下線,爲了恢復其上的 region,須要將 region server 上的 log 進行拆分,而後分發到其它 region server 上進行恢復。

HLog 文件就是一個普通的 Hadoop Sequence File(序列化文件):

1)HLog Sequence File 的 Key 是 HLogKey 對象,HLogKey中記錄了寫入數據的歸屬信息,除了table 和 region 名字外,同時還包括 sequence number和timestamp,timestamp是」寫入時間」,sequence number 的起始值爲 0,或者是最近一次存入文件系統中 sequence number。

2)HLog Sequece File 的 Value 是 HBase 的 KeyValue 對象,即對應 HFile 中的 KeyValue。

2.5 Region 尋址機制

既然讀寫都在RegionServer上發生,咱們前面有講到,每一個RegionSever 爲必定數量的Region服務,那麼 Client要對某一行數據作讀寫的時候如何能知道具體要去訪問哪一個 RegionServer


 

訪問路徑爲3 步:

第 1 步:Client 請求 ZooKeeper 獲取.META.所在的 RegionServer 的地址。

第 2 步:Client請求.META.所在的RegionServer獲取訪問數據所在的RegionServer地址,Client會將.META.的相關信息cache下來,以便下一次快速訪問。

第 3 步:Client請求數據所在的RegionServer,獲取所須要的數據。

Client 會緩存.META.的數據,用來加快訪問,既然有緩存,那它何時更新?若是.META.更新了,好比Region1不在RerverServer2上了,被轉移到了RerverServer3 上。Client 的緩存沒有更新會有什麼狀況?

其實,Client 的元數據緩存不更新,當.META.的數據發生更新。如上面的例子,因爲Region1的位置發生了變化,Client 再次根據緩存去訪問的時候,會出現錯誤,當出現異常達到重試次數後就會去.META.所在的RegionServer 獲取最新的數據,若是.META.所在的RegionServer也變了,Client 就會去 ZooKeeper 上獲取.META.所在的 RegionServer 的最新地址。

2.6 讀寫過程

2.6.1 讀請求過程

一、客戶端經過ZooKeeper以及-ROOT-表和.META.表找到目標數據所在的RegionServer(就是 數據所在的 Region 的主機地址)

二、聯繫 RegionServer 查詢目標數據

三、RegionServer定位到目標數據所在的Region,發出查詢請求

四、Region 先在 Memstore 中查找,命中則返回

五、若是在 Memstore中找不到,則在Storefile中掃描爲了能快速的判斷要查詢的數據在不在這個 StoreFile 中,應用了 BloomFilter

(BloomFilter,布隆過濾器:迅速判斷一個元素是否是在一個龐大的集合內,可是他有一個弱點:它有必定的誤判率)

(誤判率:本來不存在與該集合的元素,布隆過濾器有可能會判斷說它存在,可是,若是布隆過濾器,判斷說某一個元素不存在該集合,那麼該元素就必定不在該集合內)

2.6.2 寫請求過程

HBase的寫邏輯涉及到寫內存、寫log、刷盤等操做。

2.6.2.1 HBase寫入邏輯

寫入流程:


 

一、Client先根據RowKey找到對應的 Region 所在的RegionServer

二、Client向RegionServer提交寫請求

三、RegionServer找到目標Region

四、Region檢查數據是否與Schema 一致

五、若是客戶端沒有指定版本,則獲取當前系統時間做爲數據版本

六、將更新寫入 WAL Log

七、將更新寫入 Memstore

八、判斷 Memstore 的是否須要 flush 爲 StoreFile 文件。

只有寫當Hlog和寫MemStore都成功了纔算請求寫入成功,MemStore後續會主鍵刷到HDFS中。Hlog存儲在HDFS中,當RegionServer出現異常,須要使用Hlog來恢復數據。

Hbase 在作數據插入操做時,首先要找到RowKey所對應的的Region,由於.META.表存儲了每張表每一個Region的起始RowKey了,能夠很容易找到相應Region.

建議:在作海量數據的插入操做,避免出現遞增 rowkey 的 put 操做

若是put操做的全部RowKey都是遞增的,那麼試想,當插入一部分數據的時候恰好進行分裂,那麼以後的全部數據都開始往分裂後的第二個Region插入,就形成了數據熱點現象。

2.6.2.2 MemStore刷盤

數據在更新時首先寫入HLog(WAL Log),再寫入內存(MemStore)中,MemStore中的數據是排序的,此時並不會當即刷盤,而是當MemStore累計到必定閾值時,就會建立一個新的MemStore,而且將老的MemStore添加到flush隊列,由單獨的線程flush到磁盤上,成爲一個StoreFile。於此同時,系統會在ZooKeeper中記錄一個redoPoint,表示這個時刻以前的變動已經持久化了。當系統出現意外時,可能致使內存(MemStore)中的數據丟失,此時使用HLog(WAL Log)來恢復checkpoint以後的數據。

2.6.2.2.1 全局內存控制

這個全局的參數是控制內存總體的使用狀況,當全部memstore佔整個heap的最大比例的時候,會觸發刷盤的操做。這個參數是hbase.regionserver.global.memstore.upperLimit,默認爲整個heap內存的40%。但這並不意味着全局內存觸發的刷盤操做會將全部的MemStore都進行輸盤,而是經過另一個參數hbase.regionserver.global.memstore.lowerLimit來控制,默認是整個heap內存的35%。當flush到全部memstore佔整個heap內存的比率爲35%的時候,就中止刷盤。這麼作主要是爲了減小刷盤對業務帶來的影響,實現平滑系統負載的目的。

2.6.2.2.2 MemStore達到上限

當MemStore的大小達到hbase.hregion.memstore.flush.size大小的時候會觸發刷盤,默認128M大小

2.6.2.2.3 RegionServer的Hlog數量達到上限

前面說到Hlog爲了保證Hbase數據的一致性,那麼若是Hlog太多的話,會致使故障恢復的時間太長,所以Hbase會對Hlog的最大個數作限制。當達到Hlog的最大個數的時候,會強制刷盤。這個參數是hase.regionserver.max.logs,默認是32個。

2.6.2.2.4 手工觸發

能夠經過hbase shell或者java api手工觸發flush的操做。

2.6.2.2.5 關閉RegionServer觸發

在正常關閉RegionServer會觸發刷盤的操做,所有數據刷盤後就不須要再使用Hlog恢復數據。

2.6.2.2.6 Region使用HLOG恢復完數據後觸發

工做機制:每一個 HRegionServer 中都會有一個 HLog對象,HLog是一個實現Write Ahead Log 的類,每次用戶操做寫入 Memstore 的同時,也會寫一份數據到 HLog 文件,HLog 文件按期會滾動出新,並刪除舊的文件(已持久化到StoreFile中的數據)。當 HRegionServer 意外終止 後,HMaster 會經過ZooKeeper感知,HMaster 首先處理遺留的 HLog 文件,將不一樣Region的log數據拆分,分別放到相應Region 目錄下,而後再將失效的 Region(帶有剛剛拆分的 log)從新分配,領取到這些 Region的HRegionServer在load Region的過程當中,會發現有歷史HLog須要處理,所以會Replay HLog中的數據到MemStore中,而後flush到StoreFiles,完成數據恢復。

當RegionServer出現故障的時候,其上面的Region會遷移到其餘正常的RegionServer上,在恢復完Region的數據後,會觸發刷盤,當刷盤完成後纔會提供給業務訪問。。

2.6.2.3 StoreFile存儲

StoreFile是隻讀的,一旦建立後就不能夠再修改。所以HBase的更新/修改實際上是不斷追加的操做。當一個Store中的StoreFile達到必定的閾值後,就會進行一次合併(minor_compact,major_compact),將對同一個key的修改合併到一塊兒,造成一個大的StoreFile,當StoreFile的大小達到必定閾值後,又會對StoreFile進行split,等分爲兩個 StoreFile。因爲對錶的更新是不斷追加的,compact時,須要訪問 Store中所有的StoreFile和MemStore,將他們按 rowkey 進行合併,因爲 StoreFile 和 MemStore都是通過排序的,而且StoreFile帶有內存中索引,合併的過程仍是比較快。

major_compact 和 minor_compact 的區別:

minor_compact 僅僅合併小文件(HFile)

major_compact 合併一個 region 內的全部文件

Client 寫入 -> 存入MemStore,一直到MemStore滿->Flush成一個StoreFile,直至增加到 必定閾值 -> 觸發 Compact 合併操做 -> 多個 StoreFile 合併成一個 StoreFile,同時進行版本合併和數據刪除->當StoreFiles Compact後,逐步造成愈來愈大的StoreFile->單個StoreFile大小超過必定閾值後,觸發Split操做,把當前 Region Split 成 2 個 Region,Region 會下線, 新 Split 出的 2 個孩子Region會被HMaster分配到相應的HRegionServer上,使得原先1個Region的壓力得以分流到2個Region上。

由此過程可知,HBase只是增長數據,有所得更新和刪除操做,都是在Compact階段作的,因此,用戶寫操做只須要進入到內存便可當即返 回,從而保證 I/O 高性能。

2.7 Master 工做機制

2.7.1 Master 上線

Master 啓動進行如下步驟:

一、從 ZooKeeper 上獲取惟一一個表明 Active Master 的鎖,用來阻止其它 Master 成爲 Master。

二、掃描 ZooKeeper 上的 server 父節點,得到當前可用的 RegionServer 列表。

三、和每一個 RegionServer 通訊,得到當前已分配的 Region 和 RegionServer 的對應關係。

四、掃描.META. Region 的集合,計算獲得當前還未分配的 Region,將他們放入待分配 Region 列表。

2.7.2 Master 下線

因爲Master只維護表和Region的元數據,而不參與表數據IO的過程,Master下線僅致使全部元數據的修改被凍結(沒法建立刪除表,沒法修改表的schema,沒法進行Region的負載均衡,沒法處理Region上下線,沒法進行Region的合併,惟一例外的是Region的split能夠正常進行,由於只有RegionServer參與),表的數據讀寫還能夠正常進行。所以Master 下線短期內對整個hbase集羣沒有影響。

從上線過程能夠看到,Master保存的信息全是能夠冗餘信息(均可以從系統其它地方收集到或者計算出來)

所以,通常 HBase 集羣中老是有一個Master在提供服務,還有一個以上的Master 在等 待時機搶佔它的位置。

3. Region Server

3.1 RegionServer 工做機制

3.1.1 Region 分配

任什麼時候刻,一個Region只能分配給一個RegionServer。master 記錄了當前有哪些可用的RegionServer。以及當前哪些 Region 分配給了哪些 RegionServer,哪些 Region 尚未分配。 當須要分配的新的Region,而且有一個RegionServer 上有可用空間時,Master就給這個RegionServer 發送一個裝載請求,把Region分配給這個RegionServer。RegionServer獲得請求後,就開始對此Region提供服務。

3.1.2 RegionServer 上線

Master使用 zookeeper來跟蹤RegionServer狀態。當某個 RegionServer 啓動時,會首先在 ZooKeeper 上的server 目錄下創建表明本身的 znode。因爲Master訂閱了server 目錄上的變 更消息,當server目錄下的文件出現新增或刪除操做時,Master能夠獲得來自ZooKeeper的實時通知。所以一旦RegionServer上線,Master能立刻獲得消息。

3.1.3 RegionServer 下線

當 RegionServer下線時,它和zookeeper的會話斷開,ZooKeeper 而自動釋放表明這臺server的文件上的獨佔鎖。Master 就能夠肯定:

一、RegionServer 和 ZooKeeper 之間的網絡斷開了。

二、RegionServer 掛了。

不管哪一種狀況,RegionServer都沒法繼續爲它的Region提供服務了,此時Master會刪除server目錄下表明這臺RegionServer 的 znode數據,並將這臺RegionServer的Region 分配給其它。

3.2 RegionServer的故障恢復

RegionServer的相關信息保存在ZK中,在RegionServer啓動的時候,會在Zookeeper中建立對應的臨時節點。RegionServer經過Socket和Zookeeper創建session會話,RegionServer會週期性地向Zookeeper發送ping消息包,以此說明本身還處於存活狀態。而Zookeeper收到ping包後,則會更新對應session的超時時間。

當Zookeeper超過session超時時間還未收到RegionServer的ping包,則Zookeeper會認爲該RegionServer出現故障,ZK會將該RegionServer對應的臨時節點刪除,並通知Master,Master收到RegionServer掛掉的信息後就會啓動數據恢復的流程。Master啓動數據恢復流程後,其實主要的流程以下:

RegionServer宕機---》ZK檢測到RegionServer異常---》Master啓動數據恢復---》Hlog切分---》Region從新分配---》Hlog重放---》恢復完成並提供服務

故障恢復有3中模式

3.2.1 LogSplitting

在最開始的恢復流程中,Hlog的整個切分過程都因爲Master來執行,以下圖所示:


 

a、將待切分的日誌文件夾進行重命名,防止RegionServer未真的宕機而持續寫入Hlog

b、Master啓動讀取線程讀取Hlog的數據,並將不一樣RegionServer的日誌寫入到不通的內存buffer中

c、針對每一個buffer,Master會啓動對應的寫線程將不一樣Region的buffer數據寫入到HDFS中,對應的路徑爲/hbase/table_name/region/recoverd.edits/.tmp。

d、Master從新將宕機的RegionServer中的Rgion分配到正常的RegionServer中,對應的RegionServer讀取Region的數據,會發現該region目錄下的recoverd.edits目錄以及相關的日誌,而後RegionServer重放對應的Hlog日誌,從而實現對應Region數據的恢復。

從上面的步驟中,咱們能夠看出Hlog的切分一直都是master在幹活,效率比較低。設想,若是集羣中有多臺RegionServer在同一時間宕機,會是什麼狀況?串行修復,確定異常慢,由於只有master一我的在幹Hlog切分的活。所以,爲了提升效率,開發了Distributed Log Splitting架構。

3.2.2 Distributed Log Splitting

Distributed Log Splitting是LogSplitting的分佈式實現,分佈式就不是master一我的在幹活了,而是充分使用各個RegionServer上的資源,利用多個RegionServer來並行切分Hlog,提升切分的效率。以下圖所示:


 

上圖的操做順序以下:

a、Master將要切分的日誌發佈到Zookeeper節點上(/hbase/splitWAL),每一個Hlog日誌一個任務,任務的初始狀態爲TASK_UNASSIGNED

b、在Master發佈Hlog任務後,RegionServer會採用競爭方式認領對應的任務(先查看任務的狀態,若是是TASK_UNASSIGNED,就將該任務狀態修改成TASK_OWNED)

c、RegionServer取得任務後會讓對應的HLogSplitter線程處理Hlog的切分,切分的時候讀取出Hlog的對,而後寫入不通的Region buffer的內存中。

d、RegionServer啓動對應寫線程,將Region buffer的數據寫入到HDFS中,路徑爲/hbase/table/region/seqenceid.temp,seqenceid是一個日誌中該Region對應的最大sequenceid,若是日誌切分紅功,而RegionServer會將對應的ZK節點的任務修改成TASK_DONE,若是切分失敗,則會將任務修改成TASK_ERR。

e、若是任務是TASK_ERR狀態,則Master會從新發布該任務,繼續由RegionServer競爭任務,並作切分處理。

f、Master從新將宕機的RegionServer中的Rgion分配到正常的RegionServer中,對應的RegionServer讀取Region的數據,將該region目錄下的一系列的seqenceid.temp進行從小到大進行重放,從而實現對應Region數據的恢復。

從上面的步驟中,咱們能夠看出Distributed Log Splitting採用分佈式的方式,使用多臺RegionServer作Hlog的切分工做,確實能提升效率。正常故障恢復能夠下降到分鐘級別。可是這種方式有個弊端是會產生不少小文件(切分的Hlog數宕機的RegionServer上的Region數)。好比一個RegionServer有20個Region,有50個Hlog,那麼產生的小文件數量爲2050=1000個。若是集羣中有多臺RegionServer宕機的狀況,小文件更是會成倍增長,恢復的過程仍是會比較慢。由次誕生了Distributed Log Replay模式。

3.2.3 Distributed Log Replay

Distributed Log Replay和Distributed Log Splitting的不一樣是先將宕機RegionServer上的Region分配給正常的RgionServer,並將該Region標記爲recovering。再使用Distributed Log Splitting相似的方式進行Hlog切分,不一樣的是,RegionServer將Hlog切分到對應Region buffer後,並不寫HDFS,而是直接進行重放。這樣能夠減小將大量的文件寫入HDFS中,大大減小了HDFS的IO消耗。以下圖所示:


 

3.3 Region的拆分

3.3.1 Hbase Region的三種拆分策略

Hbase Region的拆分策略有比較多,好比除了3種默認過的策略,還有DelimitedKeyPrefixRegionSplitPolicy、KeyPrefixRegionSplitPolicy、DisableSplitPolicy等策略,這裏只介紹3種默認的策略。分別是ConstantSizeRegionSplitPolicy策略、IncreasingToUpperBoundRegionSplitPolicy策略和SteppingSplitPolicy策略。

3.3.1.1 ConstantSizeRegionSplitPolicy

ConstantSizeRegionSplitPolicy策略是0.94版本以前的默認拆分策略,這個策略的拆分規則是:當region大小達到hbase.hregion.max.filesize(默認10G)後拆分。

這種拆分策略對於小表不太友好,按照默認的設置,若是1個表的Hfile小於10G就一直不會拆分。注意10G是壓縮後的大小,若是使用了壓縮的話。若是1個表一直不拆分,訪問量小也不會有問題,可是若是這個表訪問量比較大的話,就比較容易出現性能問題。這個時候只能手工進行拆分。仍是很不方便。

3.3.1.2 IncreasingToUpperBoundRegionSplitPolicy

IncreasingToUpperBoundRegionSplitPolicy策略是Hbase的0.94~2.0版本默認的拆分策略,這個策略相較於ConstantSizeRegionSplitPolicy策略作了一些優化,該策略的算法爲:min(r^2*flushSize,maxFileSize ),最大爲maxFileSize 。

從這個算是咱們能夠得出flushsize爲128M、maxFileSize爲10G的狀況下,能夠計算出Region的分裂狀況以下:

- 第一次拆分大小爲:min(10G,11128M)=128M

- 第二次拆分大小爲:min(10G,33128M)=1152M

- 第三次拆分大小爲:min(10G,55128M)=3200M

- 第四次拆分大小爲:min(10G,77128M)=6272M

- 第五次拆分大小爲:min(10G,99128M)=10G

- 第六次拆分大小爲:min(10G,1111128M)=10G

從上面的計算咱們能夠看到這種策略可以自適應大表和小表,可是這種策略會致使小表產生比較多的小region,對於小表仍是不是很完美。

3.3.1.3 SteppingSplitPolicy

SteppingSplitPolicy是在Hbase 2.0版本後的默認策略,拆分規則爲

:If region=1 then: flush size * 2 else: MaxRegionFileSize

仍是以flushsize爲128M、maxFileSize爲10場景爲列,計算出Region的分裂狀況以下:

第一次拆分大小爲:2*128M=256M

第二次拆分大小爲:10G

從上面的計算咱們能夠看出,這種策略兼顧了ConstantSizeRegionSplitPolicy策略和IncreasingToUpperBoundRegionSplitPolicy策略,對於小表也肯呢個比較好的適配。

3.3.2 Hbase Region拆分的詳細流程

Hbase的詳細拆分流程圖以下:


 

從上圖咱們能夠看出Region切分的詳細流程以下:

- 第1步會ZK的/hbase/region-in-transition/region-name下建立一個znode,並設置狀態爲SPLITTING

- 第2步master經過watch節點檢測到Region狀態的變化,並修改內存中Region狀態的變化

- 第3步RegionServer在父Region的目錄下建立一個名稱爲.splits的子目錄

- 第4步RegionServer關閉父Region,強制將數據刷新到磁盤,並這個Region標記爲offline的狀態。此時,落到這個Region的請求都會返回NotServingRegionException這個錯誤

- 第5步RegionServer在.splits建立daughterA和daughterB,並在文件夾中建立對應的reference文件,指向父Region的Region文件

- 第6步RegionServer在HDFS中建立daughterA和daughterB的Region目錄,並將reference文件移動到對應的Region目錄中

- 第7步在.META.表中設置父Region爲offline狀態,再也不提供服務,並將父Region的daughterA和daughterB的Region添加到.META.表中,已表名父Region被拆分紅了daughterA和daughterB兩個Region

- 第8步RegionServer並行開啓兩個子Region,並正式提供對外寫服務

- 第9步RegionSever將daughterA和daughterB添加到.META.表中,這樣就能夠從.META.找到子Region,並能夠對子Region進行訪問了

- 第10步RegionServr修改/hbase/region-in-transition/region-name的znode的狀態爲SPLIT

備註:爲了減小對業務的影響,Region的拆分並不涉及到數據遷移的操做,而只是建立了對父Region的指向。只有在作大合併的時候,纔會將數據進行遷移。

那麼經過reference文件如何才能查找到對應的數據呢?以下圖所示:


 

根據文件名來判斷是不是reference文件

因爲reference文件的命名規則爲前半部分爲父Region對應的File的文件名,後半部分是父Region的名稱,所以讀取的時候也根據前半部分和後半部分來識別

根據reference文件的內容來肯定掃描的範圍,reference的內容包含兩部分,一部分是切分點splitkey,另外一部分是boolean類型的變量(true或者false)。若是爲true則掃描文件的上半部分,false則掃描文件的下半部分

接下來肯定了掃描的文件,以及文件的掃描範圍,那就按照正常的文件檢索了

3.4 Region的合併

Region的合併分爲小合併和大合併,下面就分別來作介紹:

3.4.1 小合併(MinorCompaction)

由前面的刷盤部分的介紹,咱們知道當MemStore達到hbase.hregion.memstore.flush.size大小的時候會將數據刷到磁盤,生產StoreFile,所以勢必產生不少的小問題,對於Hbase的讀取,若是要掃描大量的小文件,會致使性能不好,所以須要將這些小文件合併成大一點的文件。所以所謂的小合併,就是把多個小的StoreFile組合在一塊兒,造成一個較大的StoreFile,一般是累積到3個Store File後執行。經過參數hbase.hstore,compactionThreadhold配置。小合併的大體步驟爲: - 分別讀取出待合併的StoreFile文件的KeyValues,並順序地寫入到位於./tmp目錄下的臨時文件中 - 將臨時文件移動到對應的Region目錄中 - 將合併的輸入文件路徑和輸出路徑封裝成KeyValues寫入WAL日誌,並打上compaction標記,最後強制自行sync - 將對應region數據目錄下的合併的輸入文件所有刪除,合併完成

這種小合併通常速度很快,對業務的影響也比較小。本質上,小合併就是使用短期的IO消耗以及帶寬消耗換取後續查詢的低延遲。

3.4.2 大合併(MajorCompaction)

所謂的大合併,就是將一個Region下的全部StoreFile合併成一個StoreFile文件,在大合併的過程當中,以前刪除的行和過時的版本都會被刪除,拆分的母Region的數據也會遷移到拆分後的子Region上。大合併通常一週作一次,控制參數爲hbase.hregion.majorcompaction。大合併的影響通常比較大,儘可能避免統一時間多個Region進行合併,所以Hbase經過一些參數來進行控制,用於防止多個Region同時進行大合併。該參數爲:hbase.hregion.majorcompaction.jitter

具體算法爲:

hbase.hregion.majorcompaction參數的值乘於一個隨機分數,這個隨機分數不能超過hbase.hregion.majorcompaction.jitter的值。hbase.hregion.majorcompaction.jitter的值默認爲0.5。

經過hbase.hregion.majorcompaction參數的值加上或減去hbase.hregion.majorcompaction參數的值乘於一個隨機分數的值就肯定下一次大合併的時間區間。

用戶若是想禁用major compaction,只須要將參數hbase.hregion.majorcompaction設爲0。建議禁用。

 

相關文章
相關標籤/搜索