HBase採用Master/Slave架構搭建集羣,它隸屬於Hadoop生態系統,由一下類型節點組成:HMaster節點、HRegionServer節點、ZooKeeper集羣,而在底層,它將數據存儲於HDFS中,於是涉及到HDFS的NameNode、DataNode等,整體結構以下:
其中HMaster節點用於:php
HRegionServer節點用於:html
ZooKeeper集羣是協調系統,用於:算法
HBase Client經過RPC方式和HMaster、HRegionServer通訊;一個HRegionServer能夠存放1000個HRegion;底層Table數據存儲於HDFS中,而HRegion所處理的數據儘可能和數據所在的DataNode在一塊兒,實現數據的本地化;數據本地化並非總能實現,好比在HRegion移動(如因Split)時,須要等下一次Compact才能繼續回到本地化。
本着半翻譯的原則,再貼一個《An In-Depth Look At The HBase Architecture》的架構圖:
這個架構圖比較清晰的表達了HMaster和NameNode都支持多個熱備份,使用ZooKeeper來作協調;ZooKeeper並非雲般神祕,它通常由三臺機器組成一個集羣,內部使用PAXOS算法支持三臺Server中的一臺宕機,也有使用五臺機器的,此時則能夠支持同時兩臺宕機,既少於半數的宕機,然而隨着機器的增長,它的性能也會降低;RegionServer和DataNode通常會放在相同的Server上實現數據的本地化。apache
HBase使用RowKey將表水平切割成多個HRegion,從HMaster的角度,每一個HRegion都紀錄了它的StartKey和EndKey(第一個HRegion的StartKey爲空,最後一個HRegion的EndKey爲空),因爲RowKey是排序的,於是Client能夠經過HMaster快速的定位每一個RowKey在哪一個HRegion中。HRegion由HMaster分配到相應的HRegionServer中,而後由HRegionServer負責HRegion的啓動和管理,和Client的通訊,負責數據的讀(使用HDFS)。每一個HRegionServer能夠同時管理1000個左右的HRegion(這個數字怎麼來的?沒有從代碼中看到限制,難道是出於經驗?超過1000個會引發性能問題?來回答這個問題:感受這個1000的數字是從BigTable的論文中來的(5 Implementation節):Each tablet server manages a set of tablets(typically we have somewhere between ten to a thousand tablets per tablet server))。
api
HMaster沒有單點故障問題,能夠啓動多個HMaster,經過ZooKeeper的Master Election機制保證同時只有一個HMaster出於Active狀態,其餘的HMaster則處於熱備份狀態。通常狀況下會啓動兩個HMaster,非Active的HMaster會按期的和Active HMaster通訊以獲取其最新狀態,從而保證它是實時更新的,於是若是啓動了多個HMaster反而增長了Active HMaster的負擔。前文已經介紹過了HMaster的主要用於HRegion的分配和管理,DDL(Data Definition Language,既Table的新建、刪除、修改等)的實現等,既它主要有兩方面的職責:數組
ZooKeeper爲HBase集羣提供協調服務,它管理着HMaster和HRegionServer的狀態(available/alive等),而且會在它們宕機時通知給HMaster,從而HMaster能夠實現HMaster之間的failover,或對宕機的HRegionServer中的HRegion集合的修復(將它們分配給其餘的HRegionServer)。ZooKeeper集羣自己使用一致性協議(PAXOS協議)保證每一個節點狀態的一致性。
緩存
ZooKeeper協調集羣全部節點的共享信息,在HMaster和HRegionServer鏈接到ZooKeeper後建立Ephemeral節點,並使用Heartbeat機制維持這個節點的存活狀態,若是某個Ephemeral節點實效,則HMaster會收到通知,並作相應的處理。
另外,HMaster經過監聽ZooKeeper中的Ephemeral節點(默認:/hbase/rs/*)來監控HRegionServer的加入和宕機。在第一個HMaster鏈接到ZooKeeper時會建立Ephemeral節點(默認:/hbasae/master)來表示Active的HMaster,其後加進來的HMaster則監聽該Ephemeral節點,若是當前Active的HMaster宕機,則該節點消失,於是其餘HMaster獲得通知,而將自身轉換成Active的HMaster,在變爲Active的HMaster以前,它會建立在/hbase/back-masters/下建立本身的Ephemeral節點。架構
在HBase 0.96之前,HBase有兩個特殊的Table:-ROOT-和.META.(如BigTable中的設計),其中-ROOT- Table的位置存儲在ZooKeeper,它存儲了.META. Table的RegionInfo信息,而且它只能存在一個HRegion,而.META. Table則存儲了用戶Table的RegionInfo信息,它能夠被切分紅多個HRegion,於是對第一次訪問用戶Table時,首先從ZooKeeper中讀取-ROOT- Table所在HRegionServer;而後從該HRegionServer中根據請求的TableName,RowKey讀取.META. Table所在HRegionServer;最後從該HRegionServer中讀取.META. Table的內容而獲取這次請求須要訪問的HRegion所在的位置,而後訪問該HRegionSever獲取請求的數據,這須要三次請求才能找到用戶Table所在的位置,而後第四次請求開始獲取真正的數據。固然爲了提高性能,客戶端會緩存-ROOT- Table位置以及-ROOT-/.META. Table的內容。以下圖所示:
但是即便客戶端有緩存,在初始階段須要三次請求才能直到用戶Table真正所在的位置也是性能低下的,並且真的有必要支持那麼多的HRegion嗎?或許對Google這樣的公司來講是須要的,可是對通常的集羣來講好像並無這個必要。在BigTable的論文中說,每行METADATA存儲1KB左右數據,中等大小的Tablet(HRegion)在128MB左右,3層位置的Schema設計能夠支持2^34個Tablet(HRegion)。即便去掉-ROOT- Table,也還能夠支持2^17(131072)個HRegion, 若是每一個HRegion仍是128MB,那就是16TB,這個貌似不夠大,可是如今的HRegion的最大大小都會設置的比較大,好比咱們設置了2GB,此時支持的大小則變成了4PB,對通常的集羣來講已經夠了,於是在HBase 0.96之後去掉了-ROOT- Table,只剩下這個特殊的目錄表叫作Meta Table(hbase:meta),它存儲了集羣中全部用戶HRegion的位置信息,而ZooKeeper的節點中(/hbase/meta-region-server)存儲的則直接是這個Meta Table的位置,而且這個Meta Table如之前的-ROOT- Table同樣是不可split的。這樣,客戶端在第一次訪問用戶Table的流程就變成了:負載均衡
從這個過程當中,咱們發現客戶會緩存這些位置信息,然而第二步它只是緩存當前RowKey對應的HRegion的位置,於是若是下一個要查的RowKey不在同一個HRegion中,則須要繼續查詢hbase:meta所在的HRegion,然而隨着時間的推移,客戶端緩存的位置信息愈來愈多,以致於不須要再次查找hbase:meta Table的信息,除非某個HRegion由於宕機或Split被移動,此時須要從新查詢而且更新緩存。
oop
hbase:meta表存儲了全部用戶HRegion的位置信息,它的RowKey是:tableName,regionStartKey,regionId,replicaId等,它只有info列族,這個列族包含三個列,他們分別是:info:regioninfo列是RegionInfo的proto格式:regionId,tableName,startKey,endKey,offline,split,replicaId;info:server格式:HRegionServer對應的server:port;info:serverstartcode格式是HRegionServer的啓動時間戳。
HRegionServer通常和DataNode在同一臺機器上運行,實現數據的本地性。HRegionServer包含多個HRegion,由WAL(HLog)、BlockCache、MemStore、HFile組成。
雖然上面這張圖展示的是最新的HRegionServer的架構(可是並非那麼的精確),可是我一直比較喜歡看如下這張圖,即便它展示的應該是0.94之前的架構。
當客戶端發起一個Put請求時,首先它從hbase:meta表中查出該Put數據最終須要去的HRegionServer。而後客戶端將Put請求發送給相應的HRegionServer,在HRegionServer中它首先會將該Put操做寫入WAL日誌文件中(Flush到磁盤中)。
寫完WAL日誌文件後,HRegionServer根據Put中的TableName和RowKey找到對應的HRegion,並根據Column Family找到對應的HStore,並將Put寫入到該HStore的MemStore中。此時寫成功,並返回通知客戶端。
MemStore是一個In Memory Sorted Buffer,在每一個HStore中都有一個MemStore,即它是一個HRegion的一個Column Family對應一個實例。它的排列順序以RowKey、Column Family、Column的順序以及Timestamp的倒序,以下所示:
每一次Put/Delete請求都是先寫入到MemStore中,當MemStore滿後會Flush成一個新的StoreFile(底層實現是HFile),即一個HStore(Column Family)能夠有0個或多個StoreFile(HFile)。有如下三種狀況能夠觸發MemStore的Flush動做,須要注意的是MemStore的最小Flush單元是HRegion而不是單個MemStore。聽說這是Column Family有個數限制的其中一個緣由,估計是由於太多的Column Family一塊兒Flush會引發性能問題?具體緣由有待考證。
在MemStore Flush過程當中,還會在尾部追加一些meta數據,其中就包括Flush時最大的WAL sequence值,以告訴HBase這個StoreFile寫入的最新數據的序列,那麼在Recover時就直到從哪裏開始。在HRegion啓動時,這個sequence會被讀取,並取最大的做爲下一次更新時的起始sequence。
HBase的數據以KeyValue(Cell)的形式順序的存儲在HFile中,在MemStore的Flush過程當中生成HFile,因爲MemStore中存儲的Cell遵循相同的排列順序,於是Flush過程是順序寫,咱們直到磁盤的順序寫性能很高,由於不須要不停的移動磁盤指針。
HFile參考BigTable的SSTable和Hadoop的TFile實現,從HBase開始到如今,HFile經歷了三個版本,其中V2在0.92引入,V3在0.98引入。首先咱們來看一下V1的格式:
V1的HFile由多個Data Block、Meta Block、FileInfo、Data Index、Meta Index、Trailer組成,其中Data Block是HBase的最小存儲單元,在前文中提到的BlockCache就是基於Data Block的緩存的。一個Data Block由一個魔數和一系列的KeyValue(Cell)組成,魔數是一個隨機的數字,用於表示這是一個Data Block類型,以快速監測這個Data Block的格式,防止數據的破壞。Data Block的大小能夠在建立Column Family時設置(HColumnDescriptor.setBlockSize()),默認值是64KB,大號的Block有利於順序Scan,小號Block利於隨機查詢,於是須要權衡。Meta塊是可選的,FileInfo是固定長度的塊,它紀錄了文件的一些Meta信息,例如:AVG_KEY_LEN, AVG_VALUE_LEN, LAST_KEY, COMPARATOR, MAX_SEQ_ID_KEY等。Data Index和Meta Index紀錄了每一個Data塊和Meta塊的其實點、未壓縮時大小、Key(起始RowKey?)等。Trailer紀錄了FileInfo、Data Index、Meta Index塊的起始位置,Data Index和Meta Index索引的數量等。其中FileInfo和Trailer是固定長度的。
HFile裏面的每一個KeyValue對就是一個簡單的byte數組。可是這個byte數組裏麪包含了不少項,而且有固定的結構。咱們來看看裏面的具體結構:
開始是兩個固定長度的數值,分別表示Key的長度和Value的長度。緊接着是Key,開始是固定長度的數值,表示RowKey的長度,緊接着是 RowKey,而後是固定長度的數值,表示Family的長度,而後是Family,接着是Qualifier,而後是兩個固定長度的數值,表示Time Stamp和Key Type(Put/Delete)。Value部分沒有這麼複雜的結構,就是純粹的二進制數據了。隨着HFile版本遷移,KeyValue(Cell)的格式並未發生太多變化,只是在V3版本,尾部添加了一個可選的Tag數組。
HFileV1版本的在實際使用過程當中發現它佔用內存多,而且Bloom File和Block Index會變的很大,而引發啓動時間變長。其中每一個HFile的Bloom Filter能夠增加到100MB,這在查詢時會引發性能問題,由於每次查詢時須要加載並查詢Bloom Filter,100MB的Bloom Filer會引發很大的延遲;另外一個,Block Index在一個HRegionServer可能會增加到總共6GB,HRegionServer在啓動時須要先加載全部這些Block Index,於是增長了啓動時間。爲了解決這些問題,在0.92版本中引入HFileV2版本:
在這個版本中,Block Index和Bloom Filter添加到了Data Block中間,而這種設計同時也減小了寫的內存使用量;另外,爲了提高啓動速度,在這個版本中還引入了延遲讀的功能,即在HFile真正被使用時纔對其進行解析。
FileV3版本基本和V2版本相比,並無太大的改變,它在KeyValue(Cell)層面上添加了Tag數組的支持;並在FileInfo結構中添加了和Tag相關的兩個字段。關於具體HFile格式演化介紹,能夠參考這裏。
對HFileV2格式具體分析,它是一個多層的類B+樹索引,採用這種設計,能夠實現查找不須要讀取整個文件:
Data Block中的Cell都是升序排列,每一個block都有它本身的Leaf-Index,每一個Block的最後一個Key被放入Intermediate-Index中,Root-Index指向Intermediate-Index。在HFile的末尾還有Bloom Filter用於快速定位那麼沒有在某個Data Block中的Row;TimeRange信息用於給那些使用時間查詢的參考。在HFile打開時,這些索引信息都被加載並保存在內存中,以增長之後的讀取性能。
這篇就先寫到這裏,未完待續。。。。
https://www.mapr.com/blog/in-depth-look-hbase-architecture#.VdNSN6Yp3qx http://jimbojw.com/wiki/index.php?title=Understanding_Hbase_and_BigTable http://hbase.apache.org/book.html http://www.searchtb.com/2011/01/understanding-hbase.html http://research.google.com/archive/bigtable-osdi06.pdf