這張圖是有一個錯誤點:應該是每個 RegionServer 就只有一個 HLog,而不是一個 Region 有一個 HLog。node
從HBase的架構圖上能夠看出,HBase中的組件包括Client、Zookeeper、HMaster、HRegionServer、HRegion、Store、MemStore、StoreFile、HFile、HLog等,接下來介紹他們的做用。mysql
一、HBase 有兩張特殊表:sql
.META.:記錄了用戶全部表拆分出來的的 Region 映射信息,.META.能夠有多個 Regoin數組
-ROOT-:記錄了.META.表的 Region 信息,-ROOT-只有一個 Region,不管如何不會分裂緩存
二、Client 訪問用戶數據前須要首先訪問 ZooKeeper,找到-ROOT-表的 Region 所在的位置,然 後訪問-ROOT-表,接着訪問.META.表,最後才能找到用戶數據的位置去訪問,中間須要屢次 網絡操做,不過 client 端會作 cache 緩存。安全
一、ZooKeeper 爲 HBase 提供 Failover 機制,選舉 Master,避免單點 Master 單點故障問題服務器
二、存儲全部 Region 的尋址入口:-ROOT-表在哪臺服務器上。-ROOT-這張表的位置信息網絡
三、實時監控 RegionServer 的狀態,將 RegionServer 的上線和下線信息實時通知給 Master架構
四、存儲 HBase 的 Schema,包括有哪些 Table,每一個 Table 有哪些 Column Family負載均衡
一、爲 RegionServer 分配 Region
二、負責 RegionServer 的負載均衡
三、發現失效的 RegionServer 並從新分配其上的 Region
四、HDFS 上的垃圾文件(HBase)回收
五、處理 Schema 更新請求(表的建立,刪除,修改,列簇的增長等等)
一、RegionServer 維護 Master 分配給它的 Region,處理對這些 Region 的 IO 請求
二、RegionServer 負責 Split 在運行過程當中變得過大的 Region,負責 Compact 操做
能夠看到,client 訪問 HBase 上數據的過程並不須要 master 參與(尋址訪問 zookeeper 和 RegioneServer,數據讀寫訪問 RegioneServer),Master 僅僅維護者 Table 和 Region 的元數據信息,負載很低。
.META. 存的是全部的 Region 的位置信息,那麼 RegioneServer 當中 Region 在進行分裂以後 的新產生的 Region,是由 Master 來決定發到哪一個 RegioneServer,這就意味着,只有 Master 知道 new Region 的位置信息,因此,由 Master 來管理.META.這個表當中的數據的 CRUD
因此結合以上兩點代表,在沒有 Region 分裂的狀況,Master 宕機一段時間是能夠忍受的。
table在行的方向上分隔爲多個Region。Region是HBase中分佈式存儲和負載均衡的最小單元,即不一樣的region能夠分別在不一樣的Region Server上,但同一個Region是不會拆分到多個server上。
Region按大小分隔,每一個表通常是隻有一個region。隨着數據不斷插入表,region不斷增大,當region的某個列族達到一個閾值時就會分紅兩個新的region。
每一個region由如下信息標識:< 表名,startRowkey,建立時間>
由目錄表(-ROOT-和.META.)記錄該region的endRowkey
每個region由一個或多個store組成,至少是一個store,hbase會把一塊兒訪問的數據放在一個store裏面,即爲每一個 ColumnFamily建一個store,若是有幾個ColumnFamily,也就有幾個Store。一個Store由一個memStore和0或者 多個StoreFile組成。 HBase以store的大小來判斷是否須要切分region
memStore 是放在內存裏的。保存修改的數據即keyValues。當memStore的大小達到一個閥值(默認128MB)時,memStore會被flush到文 件,即生成一個快照。目前hbase 會有一個線程來負責memStore的flush操做。
memStore內存中的數據寫到文件後就是StoreFile,StoreFile底層是以HFile的格式保存。
HBase中KeyValue數據的存儲格式,HFile是Hadoop的 二進制格式文件,實際上StoreFile就是對Hfile作了輕量級包裝,即StoreFile底層就是HFile
HLog(WAL log):WAL意爲write ahead log,用來作災難恢復使用,HLog記錄數據的全部變動,一旦region server 宕機,就能夠從log中進行恢復。
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。
一、Table 中的全部行都按照 RowKsey 的字典序排列。
二、Table 在行的方向上分割爲多個 HRegion。
三、HRegion 按大小分割的(默認 10G),每一個表一開始只有一個 HRegion,隨着數據不斷插入 表,HRegion 不斷增大,當增大到一個閥值的時候,HRegion 就會等分會兩個新的 HRegion。 當表中的行不斷增多,就會有愈來愈多的 HRegion。
四、HRegion 是 Hbase 中分佈式存儲和負載均衡的最小單元。最小單元就表示不一樣的 HRegion 能夠分佈在不一樣的 HRegionserver 上。但一個 HRegion 是不會拆分到多個 server 上的。
五、HRegion 雖然是負載均衡的最小單元,但並非物理存儲的最小單元。事實上,HRegion 由一個或者多個 Store 組成,每一個 Store 保存一個 Column Family。每一個 Strore 又由一個 memStore 和 0 至多個 StoreFile 組成
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 部分沒有這麼複雜的結構,就是純粹的二進制數據了。
一個 Hregion 由多個 Store 組成,每一個 Store 包含一個列族的全部數據。
Store 包括位於內存的一個 memstore 和位於硬盤的多個 storefile 組成。
寫操做先寫入 memstore,當 memstore 中的數據量達到某個閾值,HRegionServer 啓動 flushcache 進程寫入 storefile,每次寫入造成單獨一個 Hfile。
當總 storefile 大小超過必定閾值後,會把當前的 region 分割成兩個,並由 HMaster 分配給相 應的 region 服務器,實現負載均衡。
客戶端檢索數據時,先在 memstore 找,找不到再找 storefile。
WAL 意爲 Write ahead log(http://en.wikipedia.org/wiki/Write-ahead_logging),相似 mysql 中的 binlog,用來作災難恢復之用,Hlog 記錄數據的全部變動,一旦數據修改,就能夠從 log 中 進行恢復。
每一個 Region Server 維護一個 Hlog,而不是每一個 Region 一個。這樣不一樣 region(來自不一樣 table) 的日誌會混在一塊兒,這樣作的目的是不斷追加單個文件相對於同時寫多個文件而言,能夠減 少磁盤尋址次數,所以能夠提升對 table 的寫性能。帶來的麻煩是,若是一臺 region server 下線,爲了恢復其上的 region,須要將 region server 上的 log 進行拆分,而後分發到其它 region server 上進行恢復。
HLog 文件就是一個普通的 Hadoop Sequence File(序列化文件):
一、HLog Sequence File 的 Key 是 HLogKey 對象,HLogKey 中記錄了寫入數據的歸屬信息,除 了 table 和 region 名字外,同時還包括 sequence number 和 timestamp,timestamp 是」寫入 時間」,sequence number 的起始值爲 0,或者是最近一次存入文件系統中 sequence number。
二、HLog Sequece File 的 Value 是 HBase 的 KeyValue 對象,即對應 HFile 中的 KeyValue。
既然讀寫都在 RegionServer 上發生,咱們前面有講到,每一個 RegionSever 爲必定數量的 Region 服務,那麼 Client 要對某一行數據作讀寫的時候如何能知道具體要去訪問哪一個 RegionServer 呢?那就是接下來咱們要討論的問題
在 HBase-0.96 版本之前,HBase 有兩個特殊的表,分別是-ROOT-表和.META.表,其中-ROOT的位置存儲在 ZooKeeper 中,-ROOT-自己存儲了.META. Table 的 RegionInfo 信息,而且-ROOT不會分裂,只有一個 Region。而.META.表能夠被切分紅多個 Region。讀取的流程以下圖所示:
詳細步驟:
第 1 步:Client 請求 ZooKeeper 得到-ROOT-所在的 RegionServer 地址
第 2 步:Client 請求-ROOT-所在的 RS 地址,獲取.META.表的地址,Client 會將-ROOT-的相關 信息 cache 下來,以便下一次快速訪問
第 3 步:Client 請求.META.表的 RegionServer 地址,獲取訪問數據所在 RegionServer 的地址, Client 會將.META.的相關信息 cache 下來,以便下一次快速訪問
第 4 步:Client 請求訪問數據所在 RegionServer 的地址,獲取對應的數據
從上面的路徑咱們能夠看出,用戶須要 3 次請求才能直到用戶 Table 真正的位置,這在必定 程序帶來了性能的降低。在 0.96 以前使用 3 層設計的主要緣由是考慮到元數據可能須要很 大。可是真正集羣運行,元數據的大小其實很容易計算出來。在 BigTable 的論文中,每行 METADATA 數據存儲大小爲 1KB 左右,若是按照一個 Region 爲 128M 的計算,3 層設計能夠支持的 Region 個數爲 2^34 個,採用 2 層設計能夠支持 2^17(131072)。那麼 2 層設計的情 況下一個集羣能夠存儲 4P 的數據。這僅僅是一個 Region 只有 128M 的狀況下。若是是 10G 呢? 所以,經過計算,其實 2 層設計就能夠知足集羣的需求。所以在 0.96 版本之後就去掉 了-ROOT-表了。
如上面的計算,2 層結構其實徹底能知足業務的需求,所以 0.96 版本之後將-ROOT-表去掉了。 以下圖所示:
訪問路徑變成了 3 步:
第 1 步:Client 請求 ZooKeeper 獲取.META.所在的 RegionServer 的地址。
第 2 步:Client 請求.META.所在的 RegionServer 獲取訪問數據所在的 RegionServer 地址,Client 會將.META.的相關信息 cache 下來,以便下一次快速訪問。
第 3 步:Client 請求數據所在的 RegionServer,獲取所須要的數據。
總結去掉-ROOT-的緣由有以下 2 點:
其一:提升性能
其二:2 層結構已經足以知足集羣的需求
這裏還有一個問題須要說明,那就是 Client 會緩存.META.的數據,用來加快訪問,既然有緩 存,那它何時更新?若是.META.更新了,好比 Region1 不在 RerverServer2 上了,被轉移 到了 RerverServer3 上。Client 的緩存沒有更新會有什麼狀況?
其實,Client 的元數據緩存不更新,當.META.的數據發生更新。如上面的例子,因爲 Region1 的位置發生了變化,Client 再次根據緩存去訪問的時候,會出現錯誤,當出現異常達到重試 次數後就會去.META.所在的 RegionServer 獲取最新的數據,若是.META.所在的 RegionServer 也變了,Client 就會去 ZooKeeper 上獲取.META.所在的 RegionServer 的最新地址。
一、客戶端經過 ZooKeeper 以及-ROOT-表和.META.表找到目標數據所在的 RegionServer(就是 數據所在的 Region 的主機地址)
二、聯繫 RegionServer 查詢目標數據
三、RegionServer 定位到目標數據所在的 Region,發出查詢請求
四、Region 先在 Memstore 中查找,命中則返回
五、若是在 Memstore 中找不到,則在 Storefile 中掃描 爲了能快速的判斷要查詢的數據在不在這個 StoreFile 中,應用了 BloomFilter
(BloomFilter,布隆過濾器:迅速判斷一個元素是否是在一個龐大的集合內,可是他有一個 弱點:它有必定的誤判率)
(誤判率:本來不存在與該集合的元素,布隆過濾器有可能會判斷說它存在,可是,若是 布隆過濾器,判斷說某一個元素不存在該集合,那麼該元素就必定不在該集合內)
一、Client 先根據 RowKey 找到對應的 Region 所在的 RegionServer
二、Client 向 RegionServer 提交寫請求
三、RegionServer 找到目標 Region
四、Region 檢查數據是否與 Schema 一致
五、若是客戶端沒有指定版本,則獲取當前系統時間做爲數據版本
六、將更新寫入 WAL Log
七、將更新寫入 Memstore
八、判斷 Memstore 的是否須要 flush 爲 StoreFile 文件。
Hbase 在作數據插入操做時,首先要找到 RowKey 所對應的的 Region,怎麼找到的?其實這 個簡單,由於.META.表存儲了每張表每一個 Region 的起始 RowKey 了。
建議:在作海量數據的插入操做,避免出現遞增 rowkey 的 put 操做
若是 put 操做的全部 RowKey 都是遞增的,那麼試想,當插入一部分數據的時候恰好進行分 裂,那麼以後的全部數據都開始往分裂後的第二個 Region 插入,就形成了數據熱點現象。
細節描述:
HBase 使用 MemStore 和 StoreFile 存儲對錶的更新。
數據在更新時首先寫入 HLog(WAL Log),再寫入內存(MemStore)中,MemStore 中的數據是排 序的,當 MemStore 累計到必定閾值時,就會建立一個新的 MemStore,而且將老的 MemStore 添加到 flush 隊列,由單獨的線程 flush 到磁盤上,成爲一個 StoreFile。於此同時,系統會在 ZooKeeper 中記錄一個 redo point,表示這個時刻以前的變動已經持久化了。當系統出現意外時,可能致使內存(MemStore)中的數據丟失,此時使用 HLog(WAL Log)來恢復 checkpoint 以後的數據。
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 高性能。
寫入數據的過程補充:
工做機制:每一個 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,完成數據 恢復。
任什麼時候刻,一個 Region 只能分配給一個 RegionServer。master 記錄了當前有哪些可用的 RegionServer。以及當前哪些 Region 分配給了哪些 RegionServer,哪些 Region 尚未分配。 當須要分配的新的 Region,而且有一個 RegionServer 上有可用空間時,Master 就給這個 RegionServer 發送一個裝載請求,把 Region 分配給這個 RegionServer。RegionServer 獲得請 求後,就開始對此 Region 提供服務。
Master 使用 zookeeper 來跟蹤 RegionServer 狀態。當某個 RegionServer 啓動時,會首先在 ZooKeeper 上的 server 目錄下創建表明本身的 znode。因爲 Master 訂閱了 server 目錄上的變 更消息,當 server 目錄下的文件出現新增或刪除操做時,Master 能夠獲得來自 ZooKeeper 的實時通知。所以一旦 RegionServer 上線,Master 能立刻獲得消息。
當 RegionServer 下線時,它和 zookeeper 的會話斷開,ZooKeeper 而自動釋放表明這臺 server 的文件上的獨佔鎖。Master 就能夠肯定:
一、RegionServer 和 ZooKeeper 之間的網絡斷開了。
二、RegionServer 掛了。
不管哪一種狀況,RegionServer都沒法繼續爲它的Region提供服務了,此時Master會刪除server 目錄下表明這臺 RegionServer 的 znode 數據,並將這臺 RegionServer 的 Region 分配給其它還 活着的同志。
Master 啓動進行如下步驟:
一、從 ZooKeeper 上獲取惟一一個表明 Active Master 的鎖,用來阻止其它 Master 成爲 Master。
二、掃描 ZooKeeper 上的 server 父節點,得到當前可用的 RegionServer 列表。
三、和每一個 RegionServer 通訊,得到當前已分配的 Region 和 RegionServer 的對應關係。
四、掃描.META. Region 的集合,計算獲得當前還未分配的 Region,將他們放入待分配 Region 列表。
因爲 Master 只維護表和 Region 的元數據,而不參與表數據 IO 的過程,Master 下線僅 致使全部元數據的修改被凍結(沒法建立刪除表,沒法修改表的 schema,沒法進行 Region 的負載均衡,沒法處理 Region 上下線,沒法進行 Region 的合併,惟一例外的是 Region 的 split 能夠正常進行,由於只有 RegionServer 參與),表的數據讀寫還能夠正常進行。所以 Master 下線短期內對整個 hbase 集羣沒有影響。
從上線過程能夠看到,Master 保存的信息全是能夠冗餘信息(均可以從系統其它地方 收集到或者計算出來)
所以,通常 HBase 集羣中老是有一個 Master 在提供服務,還有一個以上的 Master 在等 待時機搶佔它的位置。