HBase簡介。。。。不是簡介,有比較深的介紹

轉載自:https://blog.csdn.net/jiangtao_st/article/details/19499923java

高江濤我的博客mysql

1、簡介

Hbase:全名Hadoop DataBase,是一種開源的,可伸縮的,嚴格一致性(並不是最終一致性)的分佈式存儲系統。具備最理想化的寫和極好的讀性能。它支持可插拔的壓縮算法(用戶能夠根據其列族中的數據特性合理選擇其壓縮算法),充分利用了磁盤空間。正則表達式

相似於Google的BigTable,其分佈式計算採用MapReduce,經過MapReduce完成大塊的數據加載和全表掃描操做等。文件存儲系統採用HDFS,經過Zookeeper來完成狀態管理協同服務。不過BigTable只支持一級索引,Hbase不只支持一級索引,還支持二級索引。算法

須要指出的是:不少人都認爲Hbase是面向列的數據庫,其實不是。從典型的關係型數據庫概念上來講Hbase並非面向列的數據庫。可是充分利用了磁盤上列式存儲格式的特性。Hbase跟傳統的Columnar databases仍是有區別的。Columnar databases擅長的是實時數據的分析訪問,而Hbase在基於key的單值訪問和範圍掃描上比較突出。不過咱們常常談及到的Hbase是面向列的存儲系統,實際上是由於Hbase是以列族的模式進行存儲的。sql

 

2、Hbase基本結構

1)架構圖

alt

 

從上圖中能夠看出,Hbase內部的核心結構由如下幾大塊組成:HMaster,HRegionServer,HLog,HRegion等。而Hbase依賴的外部系統有Zookeeper,HDFS等。數據庫

1)HMaster(相似於HDFS中NameNode,MapReduce中的JobTrackers)是用來管理HRegionServer的。它負責監控集羣中HRegionServer的狀態信息變化。主要功能點以下:數組

一、管理HRegionServer的負載均衡,調整Region分佈。這個經過HMaster的後臺線程LoadBalancer來完成。LoadBalancer會按期將Region進行移動,以使各個HRegionServer達到Load均衡。緩存

二、在Region Split後,負載新Region的分配。安全

三、HRegionServer的FailOver處理,當某一個HRegionServer出問題後,HMaster負責將其Region進行轉移。服務器

四、CatalogJanitor。 CatalogJanitor會按期檢查和清理.Meta.表。

在一個HBase集羣中會存在多個HMaster,不過zookeeper的Master Election機制會保證只有一個HMaster在運行。當運行的HMaster出問題後,其餘的HMaster就會馬上補上。

2)從圖中能夠看出,Hbase客戶端是隻與zookeeper和HRegion Server打交道。並不會跟HMaster交互。因此若是HMaster出問題了,Hbase集羣在短期內仍是能夠對外提供可靠服務的。可是,由於HMaster掌控了HRegionServer的一些功能,如:HRegion Server的FailOver操做,Region切分等,HMaster長時間不可用仍是會出問題的。

3)上面所說起的Catelog表有兩個:-Root-和.Meta.表。-Root-表中存儲了.Meta.表的位置。即.Meta.表的Region key。.Meta.表存儲了全部Region的位置及每一個Region所包含的RowKey的範圍。-Root-表的存儲位置記錄在zookeeper中,.Meta.表的存儲位置記錄在-Root-表中。

4)當客戶端發起一個查詢數據的請求後,首先,客戶端會先鏈接上zookeeper集羣,獲取-Root-表的存放在哪個HRegionServer上。接着找到對應的HRegionServer後,就可以獲取到-Root-表中對應的.Meta.表的位置。最後客戶端根據.Meta.表存儲的HRegion的位置到相應的HRegionServer中取對應的Hregion中的數據信息。通過一次查詢後,訪問Catalog表的過程就會被緩存起來,下次客戶端就能夠直接到相應的HRegion上獲取數據。

5)Hbase已經無縫集成了HDFS,其中全部的數據最終都會經過DFS客戶端API持久化到HDFS中。

6)一個Hbase集羣中有許多個HRegionServer(相似於HDFS中的DataNode,MapReduce中的TaskTrackers),由一個HMaster進行管理。每一個HRegionServer擁有一個WAL(write Ahead Log,日誌文件,用做數據恢復)和多個HRegion(能夠簡單認爲是用來存儲一個表中的某些行)。一個HRegion擁有多個Store(存儲一個ColumnFamily)。一個Store又由一個MemStore(持有對該Store的全部修改於內存中)和0至多個StoreFiles(HFile,數據存儲的地方)組成。詳細圖以下:

alt

 

 

2)基本元素

一、Row Key

行標示,相似於傳統數據庫表中的行號。Rowkeys具備不變性。除非該行別刪除或者被從新插入了新的數據。Hbase中支持基於RowKey的單行查詢和範圍掃描。在Hbase的Auto-Sharding中,也是基於RowKey進行自動切分的。

 

 

二、Column Family

在Hbase中最基本的單元就是列。而列族是由一個或者列組成。通常在使用時,儘可能將常常訪問的列做爲一個列族。由於Hbase是面向列族的存儲,也就是說一個列族中的全部列是存儲在一塊兒的。即上圖中的一個Store存儲一個列族。

 

不過有一點須要注意的是在一個表中列族被限定不能超過十個。

 

三、TimeStamp

Hbase中支持時間戳的概念。即容許Cell存儲多個版本值。版本之間經過時間戳來區分。也就是說可能存在某一列的某一行有多個值。通常默認是3,且最近版本在最上面。Hbase中有一個TTL(Time To Live)的配置,這個是基於列族維度的。一旦過時,列族就會自動刪除全部行。

 

 

四、HRegion Server

HRegionServer是負責服務和管理Region的。相似於咱們所說的主從服務器,HMaster就是主服務器,HRegionServer就是從服務器。當用戶執行CRUD等操做時,都須要經過HRegionServer定位到相應的Region上進行操做。

 

 

五、WAL

WAL全名是Write Ahead Log,相似於mysql中的Binary Log,WAL記錄了該HRegionServer上全部數據的變動。一旦這個HRegionServer死翹翹了,致使數據丟失後,WAL就是救命稻草。能夠經過WAL進行數據恢復。因此在平時WAL是沒什麼用的,只是爲了避免可預知的災難作準備。固然,WAL起做用的前提是保證變動日誌已經記錄到了WAL中。

 

WAL的實現類是HLog。由於在一個HRegionServer中持有一個WAL,因此對於該HRegionServer上的全部Region來講,WAL是全局,共享的。當HRegion實例建立的時候,在HRegionServer實例中的HLog就會被當作HRegion構造函數的參數傳遞到HRegion。當HRegion接收到一個變動操做時,HRegion就能直接經過HLog將變動日誌追加(append()方法)到共享WAL中。固然基於性能考慮,HBase還提供了一個setWriteToWAL(false)方法。一旦用戶調用了此方法。變動日誌就不會追加到WAL中。默認是須要寫入的,除非用戶本身保證數據不會丟失。

 

HLog還有一個重要的特性就是:跟蹤變動。在HLog類中有一個原子類型的變量,HLog會讀取StoreFiles中最大的sequence number(HLog中每一條變動日誌都有一個number號,由於對於一個HRegionServer中的全部HRegion是共享HLog的,因此會將變動日誌順序寫入WAL,StoreFiles中也持有該number),並存放到變量中。這樣HLog就知道已經已經存儲到哪個位置了。

 

WAL還有兩個比較重要的類,一個是LogSyncer,另外一個是LogRoller。

一、在建立表時,有一個參數設置:Deferred Log Flush,默認是false,表示log一旦更新就當即同步到filesystem。若是設置爲true,則HRegionServer會緩存那些變動,並由後臺任務LogSyncer定時將變動信息同步到filesystem。

二、WAL是有容量限制的,LogRoller是一個後臺線程,會定時滾動logfile,用戶能夠設定這個間隔時間(hbase.regionserver.logroll.period,默認是一小時)。當檢查到某個logfile文件中的全部sequence number均小於那個最大的sequence number時,就會將此logfile移到.oldLog目錄。

 

以下是WAL的文件結構,目前WAL採用的是Hadoop的SequenceFile,其存儲記錄格式是key/value鍵值對的形式。其中Key保存了HLogkey的實例,HLogKey包含數據所屬的表名及RegionName,timeStamp,sequenceNumber等信息。Value保存了WALEdit實例,WALEdit包含客戶端每一次發來的變動信息。

alt

 

六、Region

在Hbase中實現可擴展性和負載均衡的基本單元是Region。Region存儲着連續的RowKey的數據。剛開始時,一個表就只有一個Region,當一個表隨着數據增多而不斷變大時,若是達到指定的大小後就會根據Rowkey自動一分爲二成兩個Region。每一個Region中保存着一個【startkey,endkey】。隨着表的繼續增大,每一個Region又會自動split成更多的Region。每一個Region只會由一個HRegionServer服務。這就是所謂的Hbase的AutoSharding特性。固然,Region除了會spilt外,也可能進行合併以減小Region數目(這就是Hbase的compaction特性,後面會談到)。

 

既然Region是表的基本元素。那麼,用戶如何獲取到對應的Region呢??前面已經說起—經過Catalog表。

 

七、Store

Store是核心存儲單元。在一個HRegion中可能存在多個ColumnFamily,那麼Store中被指定只能存儲一個ColumnFamily。不一樣的ColumnFamily存儲在不一樣的Store中(因此在建立ColumnFamily時,儘可能將常常須要一塊兒訪問的列放到一個ColumnFamily中,這樣能夠減小訪問Store的數目)。一個Store由一個MemStore和0至多個StoreFile組成。

 

 

八、MemStore

Hbase在將數據寫入StoreFile以前,會先寫入MemStore。MemStore是一個有序的內存緩衝器。當MemStore中的數據量達到設定的大小時(Flush Size),HRegionServer就會執行Flush操做,將MemStore中的數據flush到StoreFile中。

 

當HRegionServer正在將MemStore中的數據Flush到StoreFile時,MemStore還能夠對外進行讀寫服務。這個是經過MemStore的滾動機制實現的。經過滾動MemStore,新的空的塊就能夠接收變動,而老的滿的塊就會執行flush操做。

 

九、StoreFile/HFile

StoreFile是HFile的實現,對HFile作了一層包裝。HFile是數據真正存儲的地方。HFile是基於BigTable的SSTable File和Hadoop的TFile。HFile是以keyvalue的格式存儲數據的。(Hbase以前使用過Hadoop得MapFile,由於其性能上至關糟糕而放棄。)下圖是HFile中版本1的格式,版本2稍有改變(詳見Hbase wiki):

 

alt

從上圖中看出,HFile是由多個數據塊組成。大部分數據塊是不定長的,惟一固定長度的只有兩個數據塊:File Info和Trailer。DataIndex和MetaIndex分別記錄了Data塊和Meta塊的起始位置。每一個data塊由一些kevalue鍵值對和Magic header組成。Data塊的大小能夠再建立表時經過HColumnDescriptor設定。Magic記錄了一串隨機的數字,防治數據丟失和損壞。

若是用戶想繞過Hbase直接訪問HFile時,好比檢查HFile的健康狀態,dump HFile的內容,能夠經過HFile.main()方法完成。

 

以下圖是KeyValue的格式:

alt

KeyValue是一個數組,對byte數組作了一層包裝。Key Length和Value Length都是固定長度的數值。Key包含的內容有行RowKey的長度及值,列族的長度及值,列,時間戳,key類型(Put, Delete, DeleteColumn, DeleteFamily)。

從上圖能夠看出,每個keyValue只包含一列,即便對於同一行的不一樣列數據,會建立多個KeyValue實例。此外KeyValue不能被Split,即便此KeyValue值超過Block的大小,好比:

Block大小爲16Kb,而KeyValue值有8Mb,那麼KeyValue會經過相連的多個Block進行存儲。

 

3)總結

以上對Hbase的基本元素作了一個大致的介紹。下圖是Hbase的存儲結構圖。記錄了客戶端發起變動或者新增操做時,Hbase內部的存儲流程。

 

alt

下面來分析下整個存儲流程:

1)當客戶端提交變動操做(如插入put,刪除delete,計數新增incr),首先客戶端會鏈接上Zookeeper找到-Root-表的存儲位置,而後根據-Root-表所提供的.Meta.表的位置找到對應的Region所在的HRegionServer。數據變動信息會先經過HRegionServer寫入一個commit log,也就是WAL。當寫入WAL成功後,數據變動信息會存到MemStore中。當MemStore達到設定的maximum value(hbase.hregion.memstore.flush.size,默認64MB)後,MemStore就會開始進行Flush操做,將其內容持久化到一個新的HFile中。在Flush操做過程當中,MemStore經過滾動機制繼續對用戶提供讀寫服務。隨着Flush操做的不斷進行,HFile文件愈來愈多。 當HFile文件超過設定的數量後,Hbase的HouseKeeping機制就會經過Compaction特性將HFile小文件合併成一個更大的HFile文件。在Compaction的過程當中,會進行版本的合併以及數據的刪除。因爲storeFiles是不變的,用戶執行刪除操做時,並不能簡單地經過刪除其鍵值對來刪除數據內容。Hbase提供了一個delete marker機制(也稱爲tombstone marker),會告訴HRegionServer那個指定的key已經被刪除了。這樣其它用戶檢索這個key的內容時,由於已經被標記爲刪除,因此也不會檢索出來。在進行Compaction操做中就會丟棄這些已經打標的記錄。通過屢次Compaction後,HFile文件會愈來愈大,當達到設定的值時,會觸發Split操做。將當前的Region根據RowKey對等切分紅兩個子Region,當期的那個Region被廢棄,兩個子Region會被分配到其餘HRegionServer上。因此剛開始時一個表只有一個Region,隨着不斷的split,會產生愈來愈多的Region,經過HMaster

的LoadBalancer調整,Region會均勻遍及到全部的HRegionServer中。

 

2)當HLog滿時,HRegionServer就會啓動LogRoller,經過執行rollWriter方法將那些全部sequence number均小於最大的那個sequence number的logfile移動到.oldLog目錄中等待被刪除。若是用戶設置了Deferred Log Flush爲true,HRegionServer會緩存有關此表的全部變動,並經過LogSyncer調用sync()方法定時將變動信息同步到filesystem。默認爲false的話,一旦有變動就會馬上同步到filesystem。

 

3)在一個HRegionServer中只有一個WAL,全部Region共享此WAL。HLog會根據Region提交變動信息的前後順序依次順序寫入WAL中。若是用戶設置了setWriteToWAL(false)方法,則有關此表的全部Region變動日誌都不會寫入WAL中。這也是上圖中Region將變動日誌寫入WAL的那個垂直向下的箭頭爲何是虛線的緣由。

 

 

3、Hbase基本操做

Hbase中主要的客戶端接口是HTable類,HTable提供了對數據的全部CRUD操做。須要注意的是因爲建立HTabe實例比較耗時, 因此在實際使用中最好建立單例模式的HTable實例,不過若是須要多個HTable實例的話,能夠考慮使用HBase的HTablePool特性(下面後講到)。Hbase不提供直接的update操做。因爲Hbase中數據存儲有版本支持。因此若是須要update一條記錄,通常是經過put操做,這樣歷史版本會在Compaction操做中被合併掉,這樣就間接實現了更新。(在MemStore中有一個變量MemstoreTS,該變量是隨put操做而遞增的。好比首先往列A,timeStamp爲T1上put一條數據data1,假設此時MemstoreTS爲1;以後若是想更新這條數據,只須要往列A,timeStamp爲T1上put一條數據data2,此時MemstoreTS爲2,Hbase會自動會將MemstoreTS大的排在前面。MemstoreTS小的在Compaction過程當中就被過濾掉了。)

 

 

1)put操做

Put操做就是講數據插入到Hbase中。有兩種模式,一種是對單行的操做(single put);還有一種是對多行的操做(List of put)。針對單行操做的方式以下:

 

一、建立put實例有以下構造函數:須要用戶指定某行,用戶也能夠設定時間戳做爲版本標示。此外,用戶還能夠加入自定義的行鎖,以防其它用戶或者其它線程在變動期間訪問此行的數據。

Put(byte[] row)

Put(byte[] row, RowLock rowLock)

Put(byte[] row, long ts)

Put(byte[] row, long ts, RowLock rowLock)

在Hbase中參數的傳遞大可能是byte數組類型。Hbase提供了許多靜態方法將java類型轉換成byte數組類型。以下:

static byte[] toBytes(ByteBuffer bb)

static byte[] toBytes(String s)

static byte[] toBytes(boolean b)

static byte[] toBytes(long val)

static byte[] toBytes(float f)

static byte[] toBytes(int val)

二、一旦建立好put實例後,就能夠經過put類提供的方法插入數據了。插入數據的操做須要指定列族,所在列等。以下:

Put add(byte[] family, byte[] qualifier, byte[] value)

Put add(byte[] family, byte[] qualifier, long ts, byte[] value)

Put add(KeyValue kv) throws IOException

三、put組裝完成後,就能夠經過HTable提供的void put(Put put)throws IOException完成數據的插入操做。

若是須要對多行進行put操做,能夠組裝一系列的put實例,而後調用HTable提供的void put(List puts) throws IOException來完成多行插入操做。不過須要指出的是:若是在這多個Put實例中存在一個put實例有誤(好比:往一個不存在的列族中插入數據),那麼該put實例會報錯,可是不影響其餘的put實例。跟後面的get操做有點區別。

 

此外,Hbase還提供了一個原子型的put操做:Atomic compare-and-set ,方法以下:boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,byte[] value, Put put) throws IOException。只有校驗成功後纔會完成put操做.

 

須要注意的是,由於每次的put操做至關於一個RPC,將數據從客戶端傳遞到服務端並返回。若是你的應用中RPC很是頻繁,好比一秒內成千上萬次,可能會有隱患。解決的辦法就是儘可能下降RPC次數,Hbase提供了一個嵌入的客戶端寫緩存器(Client-side Write Buffer)。它會緩存全部的put操做,而後再一次性提交。默認狀況下Client-side Write Buffer是沒有激活的。用戶能夠在建立HTable的時候經過調用table.setAutoFlush(false)方法來激活它。而且能夠經過isAutoFlush()來檢查是否已經激活。默認是true,表示一旦有put操做會當即發送到服務器端。當你想將全部put操做提交到服務器端時,能夠調用flushCommits()操做。它會將緩存器中全部變動提交到遠程服務器。Client-side Write Buffer還會自動對buffer中的全部變動進行分組,同一個HRegionServer的分到同一個組。這樣每一個HRegionServer經過一個RPC傳送.

 

 

2)get操做

Get操做就是從服務器端獲取數據。跟put操做同樣,get操做也分爲兩種模式,一種是對單行的get操做(single get),另外一種是對多行進行檢索操做(List of gets)。

 

一、HTable提供的get方法以下:其返回值爲Result類,該類包含了列族,列,keyvalue,

RowKey等信息。該類提供的豐富的方法供用戶獲取返回的各類信息。

Result get(Get get) throws IOException

二、Get類的構造函數以下,須要用戶傳入指定的行及行鎖等參數。

Get(byte[] row)

Get(byte[] row, RowLock rowLock)

三、 一旦建立的get實例後,用戶能夠調用Get類提供的以下方法來框定你須要檢索的數據。以下:用戶能夠指定列族,列,時間戳,最大版本號等。若是不設置版本號,默認是1,表示最大的版本。

Get addFamily(byte[] family)

Get addColumn(byte[] family, byte[] qualifier)

Get setTimeRange(long minStamp, long maxStamp) throws IOException

Get setTimeStamp(long timestamp)

Get setMaxVersions()

Get setMaxVersions(int maxVersions) throws IOException

跟List of put 相似,對於多行的檢索操做,HTable也提供了相似的以下方法:用戶只要建立多個get實例,就能夠經過以下方法獲取須要的數據。不過須要注意的是:跟List of put不一樣的是,若是Get實例列表中只要存在一個Get實例有誤(好比get一個不存在的列族的值),那麼總體就會拋出一個異常.

Result[] get(List gets) throws IOException

 

3)delete操做

Delete操做也相似,HTable提供了兩種方法,支持單個delete實例和多個delete實例的操做。以下:

 

void delete(Delete delete) throws IOException

void delete(List deletes) throws IOException

一、相應的delete實例構造函數有:

Delete(byte[] row)

Delete(byte[] row, long timestamp, RowLock rowLock)

二、若是你須要添加一些限制條件,可使用delete類提供的相關方法,支持指定列族,列,時間戳等。若是你指定了一個時間戳,則表示小於等於該時間戳的時間將被刪除。若是指定了列和行號,但沒有指定時間戳,則默認會刪掉版本號最大的那個值。

Delete deleteFamily(byte[] family)

Delete deleteFamily(byte[] family, long timestamp)

Delete deleteColumns(byte[] family, byte[] qualifier)

Delete deleteColumns(byte[] family, byte[] qualifier, long timestamp)

Delete deleteColumn(byte[] family, byte[] qualifier)

Delete deleteColumn(byte[] family, byte[] qualifier, long timestamp)

void setTimestamp(long timestamp)

三、當使用List of delete時,若是有一個delete實例出錯,那麼會拋出異常。並且delete的實例列表中只會存在那個出問題的delete實例。Delete也支持原子型的Compare-and- Delete,以下:

boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,byte[] value, Delete delete) throws IOException

 

4)Batch操做

Hbase還支持批量操做。其實上面所談到的List of puts,gets,deletes都是基於Batch操做來的。不過List of puts,gets,deletes逐漸會被廢棄。推薦使用Batch操做。HTable提供的batch操做方法以下:參數中Row類是Put,Delete,Get類的父類。表示用戶能夠同時傳入put,get及delete實例操做。不過在一個batch中,最好不要同時傳入針對同一行的put和delete實例。

 

(1) void batch(List actions, Object[] results) throws IOException, InterruptedException

(2) Object[] batch(List actions) throws IOException, InterruptedException上面這兩個batch方法比較相似,但有比較大的區別。第一個batch方法須要用戶傳遞一個數組,該數組用來填充batch操做中全部成功的操做的結果集。若是沒有指定這個數組,好比第二個方法。一旦batch操做中某一個實例出現問題,那麼Hbase只會拋出一個異常。那些成功的操做的結果並不會返回。而第一個方法則會將那些成功的操做的結果集返回給用戶。

此外Batch操做不支持Client-side write buffer,Batch方法是同步的,會直接將其包含的操做發往服務器。這點須要注意!

Batch操做返回的結果可能的結果有以下幾種:

一、null:表示那個操做操做鏈接遠程服務器失敗。

二、Empty Result:put和delete操做的返回結果,表示操做成功。

三、Result:get操做的返回結果集

四、Throwable:異常結果

 

5)Scan操做

Scan操做相似於傳統的RDBMS中的遊標的概念。其目的跟get同樣,也是檢索服務器端數據。Hbase也提供了一個Scan類。因爲Scans相似於迭代器,因此你須要經過getScanner()方法獲取。HTable提供了以下方法:若是你看了源碼就會知道,後面那兩個方法實際上是先建立一個scan實例,並加入傳入的參數,而後再調用第一個方法。

 

ResultScanner getScanner(Scan scan) throws IOException

ResultScanner getScanner(byte[] family) throws IOException

ResultScanner getScanner(byte[] family, byte[] qualifier) throws IOException

一、Scan類提供了多個構造函數,以下:startRow和stopRow是左閉右開的。從構造函數中能夠看出,用戶只須要指定rowKey的範圍,或者添加相應的過濾器,Hbase可以自動檢索你指定的RowKey的範圍的數據。若是沒有指定startRow,默認從第一行開始.

Scan()

Scan(byte[] startRow, Filter filter)

Scan(byte[] startRow)

Scan(byte[] startRow, byte[] stopRow)

二、當建立好Scan實例後,若是想添加更多的限制條件,能夠經過調用Scan提供的以下方法:容許添加列族,列,時間戳等.

Scan addFamily(byte [] family)

Scan addColumn(byte[] family, byte[] qualifier)

Scan setTimeRange(long minStamp, long maxStamp) throws IOException

Scan setTimeStamp(long timestamp)

Scan setMaxVersions()

Scan setMaxVersions(int maxVersions)

GetScanner()方法返回的是一個ResultScanner實例。須要注意的是:若是結果集存在多行,Scans並不會一次性將全部行在一個RPC裏面傳送給客戶端,而是基於一行一行傳送。這樣作主要是由於多行須要耗費大量時間。

ResultScanner類包裝了Result類將其每行結果以迭代的方式輸出,使得Scan操做相似於get操做。此外ResultScanner類提供了以下方法供用戶進行迭代使用:用戶能夠選擇一次返回一行或者多行。不過不要認爲是服務器端一次性返回多行。實際上是客戶端循環調用nbRows 次next()方法而已。服務器端在一個RPC裏面仍是隻傳送一行數據。這個確實有點影響心情,但Hbase就喜歡噁心下你,不過它也提供的相應的解決辦法:Scanner Caching,默認是關閉的。

Result next() throws IOException

Result[] next(int nbRows) throws IOException

void close()

close()方法表示釋放ResultScanner實例。由於ResultScanner實例持有了必定的資源,若是不及時釋放,可能隨着時間推移會佔用很大的內存空間。此外,close()操做最好放在finally模塊,緣由你懂得!

 

4、Hbase特性

HBase提供了許多賞心悅目的特性。如Filters,Counters,Coprocessors,Compaction,HTablePool等。

 

 

1)Filters

當你經過Scan或者Get操做檢索數據時,會發現Scan和Get只支持基於RowKey,列族,列,時間戳等粗粒度的檢索。若是用戶想基於Key或者Value或者正則表達式等做爲查詢條件進行查詢的話,Scan和Get是沒辦法作到的。而Filter就是幹這事的。Hbase提供了一系列的Filters,用戶只要實現Filter,也能夠自定義Filters。

 

須要說明的是Hbase提供的這些Filters都是配置在客戶端,但應用在服務器端,也叫作Predicate push-down。(好比用戶在進行Scan操做時能夠傳入Filter,序列化後傳送到服務器端,HRegionServer就會將其反序列化,並應用到內部Scanner)。這樣能夠有效減小數據傳輸帶來的網絡開銷。

須要注意的是:Filters的通用約定是過濾掉你不須要的數據,而不是用來指定你須要的數據。不過凡是繼承CompareFilter過濾器的Filter,其做用恰好相反,用來指定你須要的數據。

Hbase提供的Filters有:

Ⅰ. Comparison Filters

Compartison Filters是基於比較的過濾器。定義以下:

CompareFilter(CompareOp valueCompareOp,WritableByteArrayComparable valueComparator)

該構造器有兩個特定的參數,一個是比較運算符,另外一個是比較器。

A、常見的比較運算符有:

LESS,LESS_OR_EQUAL,EQUAL,NOT_EQUAL,GREATER_OR_EQUAL,GREATER,NO_OP。前面幾個運算符根據名字定義就能判斷其意思,最後一個是NO_OP,表示排除任何數據。

B、常見的比較器有:其中NullComparator是判斷給定的值是否爲空或者非空。最後三個比較器只能搭配使用EQUAL,NOT_EQUAL比較運算符,返回0表示匹配,1表示不匹配。

BinaryComparator

BinaryPrefixComparator

NullComparator

BitComparator

RegexStringComparator

SubstringComparator

C、基於Comparison Filter的過濾器有好多種,好比:

一、RowFilter

二、FamilyFilter

三、QualifierFilter

四、ValueFilter

五、DependentColumnFilter

(1) RowFilter過濾器顧名思義就是根據RowKey來過濾數據。因此RowFilter中的比較運算符和比較器參數都是基於RowKey來比較的。好比以下Filter表示RowKey包含-4的數據。

Filter filter = new RowFilter(CompareFilter.CompareOp.EQUAL,new SubstringComparator("-4"))。

(2) FamilyFilter過濾器跟RowFilter相似,不過FamilyFilter是基於ColumnFamily的比較。

QualifierFilter和ValueFilter過濾器也相似,分別是基於列和數值的比較。

(3) DependentColumnFilter過濾器稍微複雜一點。它能夠說是timeStamp Filter和ValueFilter的結合。由於DependentColumnFilter須要指定一個參考列,而後獲取跟改參考列有相同時間戳的全部列,再在此基礎上獲取知足ValueFilter的列值。構造函數以下:用戶能夠根據本身喜愛省略valueFilter或者經過設置dropDependentColumn爲true省略timestamp Filter。不過須要注意的是:此過濾器不能跟Scan中的Batch操做結合使用。

A、DependentColumnFilter(byte[] family, byte[] qualifier)

B、DependentColumnFilter(byte[] family, byte[] qualifier,boolean dropDependentColumn)

C、DependentColumnFilter(byte[] family, byte[] qualifier,boolean dropDependentColumn, CompareOp valueCompareOp,WritableByteArrayComparable valueComparator)

 

Ⅱ. Dedicated Filters

專有的一些過濾器,Hbase提供了許多個性化的專有過濾器。常見的Dedicated Filters有:

 

A、SingleColumnValueFilter

B、SingleColumnValueExcludeFilter

C、PrefixFilter

D、PageFilter

E、KeyOnlyFilter

F、FirstKeyOnlyFilter

G、InclusiveStopFilter

H、TimestampsFilter

I、ColumnCountGetFilter

J、ColumnPaginationFilter

K、ColumnPrefixFilter

L、RandomRowFilter

(1) 若是你想分頁獲取數據,能夠經過PageFilter來完成。ColumnPaginationFilter跟PageFilter相似,只不過PageFilter是基於行的分頁,而ColumnPaginationFilter是基於列的分頁。如:

ColumnPaginationFilter(int limit, int offset),表示獲取從offset列開始的連續limit列的數據。

(2) 若是隻想獲取每一行的第一列的值,那麼FirstKeyOnlyFilter是不錯的選擇。此外,由於前面提到的Scan操做須要用戶指定一個startRow和EndRow,其中這兩個參數時左閉右開區間的。若是想EndRow也包含,能夠經過InclusiveStopFilter來解決。以下:獲取從Row5至Row10的數據

。不過由於Hbase是字典排序的,因此獲得的結果中可能會包含Row51,Row52等這些行的數據。

Filter filter = new InclusiveStopFilter(Bytes.toBytes("row-9"));

Scan scan = new Scan();

scan.setStartRow(Bytes.toBytes("row-5"));

scan.setFilter(filter);

ResultScanner scanner = table.getScanner(scan);

(3) 若是想獲取某個版本的全部數據。能夠經過TimestampsFilter來設置,用戶須要傳入版本號。以下:

TimestampsFilter(List timestamps)

(4) PrefixFilter和ColumnPrefixFilter都是基於前綴的過濾器,不過PrefixFilter是基於行的前綴過濾,然後者是基於列的前綴過濾。

(5) RandomRowFilter是基於隨機行的過濾器,用戶須要指定一個在0到1之間的隨機數,構造函數以下:若是chance大於1,則會返回全部行。若是小於0,則過濾掉全部行。

RandomRowFilter(float chance)

 

Ⅲ. Decorating Filters

Decorating Filters稱爲裝飾型的過濾器。它的做用是爲其餘過濾器返回的結果提供一些附加的校驗操做。常見的Decorating Filters有:

 

A、SkipFilter

B、WhileMatchFilter

(1) SkipFilter包裝了其它的過濾器,只要被包裝的過濾器返回的結果中有一行的某一列或者某個KeyValue被過濾掉了,那麼SkipFilter會將該列或者KeyValue所處的整行所有過濾。被包裝的過濾器必須實現filterKeyValue()方法。由於SkipFilter會依靠filterKeyValue()返回的結果進行附加的處理。好比:

Filter filter = new ValueFilter(CompareFilter.CompareOp.NOT_EQUAL,new BinaryComparator(Bytes.toBytes("val-1")));

上面這樣一個filter,表示返回的結果中值不能等於val-1,這樣值爲val-1的那個列就不會展現,但該行的其餘列只要知足值不等於val-1都會返回。

不過一旦使用了SkipFilter,如:Filter filter2 = new SkipFilter(filter);只要存在某一行中的某個列的值等於val-1,那麼該行的全部數據都不會返回。

(2) WhileMatchFilter跟SkipFilter相似,不過區別之處在於WhileMatchFilter一旦找到某一行中的某些列值或者KeyValue不知足條件,那麼整個Scan操做就會被終止。SkipFilter只是會將此行過濾,不做爲返回值,但Scan操做會繼續。

 

Ⅳ. Custom Filters

若是想實現自定義的Filter,能夠實現Filter接口或者擴展FilterBase類。FilterBase類提供了基本的Filter實現。

 

 

若是用戶想在一次檢索數據的過程當中使用多個Filter,那麼可使用FilterList特性。其構造函數以下:

FilterList(List rowFilters)

FilterList(Operator operator)

FilterList(Operator operator, List rowFilters)

其參數operator其枚舉值有兩個:MUST_PASS_ALL(表示返回的結果集數據必須經過全部過濾器的過濾),MUST_PASS_ONE(表示返回的結果集數據只要經過了其中一個過濾器就行)。

 

2)Counters

Hbase提供了計數器Counters機制。它將列當作Counters,經過對列的操做來完成計數。在命令行下用戶能夠經過以下命令增長計數。

 

incr ‘

’,’’,’’,[]

 

若是想獲取當前計數器的值,能夠經過get命令或者get_counter或者incr命令。以下:

get ‘

’,’’;

 

get_counter ‘

’,’’;

 

incr ‘

’,’’,’’,0;

 

第一個和第二個的區別就是第一個返回的值是字節數組類型,用戶很難馬上知道到底表明什麼值。第二個返回的是可讀的值。第三個命令採用比較投機取巧的辦法,經過incr計數加0來返回當前值。若是將減小計數,能夠經過incr命令來增長一個負數的值。

HTable提供了單個計數器(Single Counters)和多個計數器(Multiple Counters)。對於單個的Counters,須要指定準確的列名,跟命令行的incr同樣,能夠經過增長正數和負數或者零來達到增長計數,減小計數以及訪問當期計數的目的。構造函數以下:

long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier,long amount) throws IOException

long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier,long amount, boolean writeToWAL) throws IOException

對於多重計數器,HTable提供的方法以下:

Result increment(Increment increment) throws IOException

一、用戶須要建立一個Increment實例,能夠採用以下構造函數:

Increment() {}

Increment(byte[] row)

Increment(byte[] row, RowLock rowLock)

二、若是想爲這個Increment實例添加必要的條件,如列名,或者時間戳範圍,能夠經過以下方法來完成。能夠在一個Increment實例中經過增長多列來實現多重計數器。

Increment addColumn(byte[] family, byte[] qualifier, long amount)

Increment setTimeRange(long minStamp, long maxStamp) throws IOException

 

3)Coprocessors

Coprocessors是Hbase提供的另外一大特性。能夠認爲是簡化的MapReduce組件。Coprocessors是一組內嵌於RegionServer和HMaster進程的框架(BigTable的coprocessors擁有獨立進程和地址空間),支持用戶請求在每一個Region上並行運行,相似於傳統數據庫中觸發器的功能。

 

一、Hbase提供的Coprocessors有兩種類型:observer和endpoint。其中observer相似於RDBMS中的觸發器,即鉤子函數,其代碼部署在服務器端運行,在真實的方法前添加pre(),實現後加入

post(),以實現對真實方法的輔助操做。而endpoint相似於存儲過程。

二、Coprocessors框架有三個模塊組成:Coprocessors,CoprocessorEnvironment,

CoprocessorHost。CoprocessorEnvironment提供Coprocessors實例運行的環境以及持有

Coprocessors實例的生命週期狀態。CoprocessorHost是用來維護Coprocessors實例和

Coprocessors運行環境的。

三元體類圖以下(Hbase94版本):用戶能夠經過繼承BaseRegionObserver, WALObserver,

BaseMasterObserver或者BaseEndpointCoprocessor來實現自定義的Coprocessors。

alt

 

A、coprocessors Load

Coprocessors有兩種加載方式:經過配置文件方式的靜態加載和動態加載方式。

a、配置文件加載

靜態加載方式就是經過hbase-site.xml配置文件配置指定的coprocessors來加載。配置方式以下,其執行順序就是按照配置文件指定的順序:

 

hbase.coprocessor.region.classes

coprocessor.RegionObserverExample,coprocessor.otherCoprocessor

 

 

 

hbase.coprocessor.master.classes

coprocessor.MasterObserverExample

 

 

 

hbase.coprocessor.wal.classes

coprocessor.WALObserverExample, bar.foo.MyWALObserver

 

 

須要注意的是:經過這種方式加載的RegionObserver是針對全部Region和表的。用戶沒法指定某一具體的Region或者table。

b、經過table description加載

經過這種方式的加載是細化到具體的表的維度。只有跟該表有關的Region操做纔會加載。因此這種方式的加載只能針對RegionCoprocessor。加載方法是:

 

HTableDescriptor.setValue(),其中key是Coprocessor,value是||

B、observer

 

observer又有三種實現類型:

a、RegionObserver

RegionObserver通常用來進行數據操做的coprocessor,好比數據訪問前的權限身份驗證,Filter,二級索引等。如:

void preFlush(...) / void postFlush(...) MemStore中內容flush到Storefile先後添加輔助型操做。

void preGet(...) / void postGet(...) 獲取數據的先後添加輔助操做

b、MasterObserver

MasterObserver是面向整個集羣的事件,好比基於管理員的操做和DDL類型的操做的監控。如:

void preCreateTable(...) / void postCreateTable(...) 建立表先後作些輔助操做

void preAddColumn(...) / void postAddColumn(...) 建立列先後作些輔助操做

void preMove(...) / void postMove(...) 移動Region的先後添加輔助操做

c、WALObserver

WALObserver則是提供鉤子函數對Write Ahead Log的的操做。

C、Endpoint

Endpoint動態擴展了RPC協議。只支持Region的操做,不支持Master和WAL的操做。用戶能夠經過Endpoint完成一些彙集函數的功能,如AVG,Count,SUM等。其原理是經過包裝客戶端的實現,相似於MapReduce,好比getSum()操做,Map端endpoint經過並行的scan完成對每一個Region的操做,每一個Region的scan結果彙總到endpoint包裝的客戶端,將每一個Region反饋的結果進行彙總便可獲得getSum()的結果。

D、小結

a、Coprocessors有兩種類型:observer和endpoint。observer相似於傳統的關係型數據庫中的觸發器,經過鉤子函數來完成對被鉤的方法的輔助功能,endpoint相似於關係型數據庫中的存儲過程,用來實現聚合函數的相關功能。

b、Coprocessors支持動態加載,擁有多種加載方式。

c、Coprocessors能夠將多個Coprocessor連接在一塊兒使用,相似於Servlet中的filters過濾器。

d、Coprocessors中有優先級的概念,SYSTEM級別的Coprocessor優先處理,USER級別的Coprocessor優先級更低。

 

4)Split And Comcaption

A、Region Split

當建立一個表時,此時該表只對應一個Region。隨着不斷了往表中插入記錄,表數據愈來愈多,當超過設定的值hbase.hregion.max.filesize時,該Region就會Split成兩個子Region。原來的那個Region就會被刪除。具體操做以下:

a、HRegionServer建立一個splits目錄,而且關閉其父Region以防接收其它請求。

b、HRegionServer會在splits目錄準備好兩個子Region,父Region的RowKey對半切。而後將其移動到表目錄下,而且更細.Meta.表的數據,指示該父Region正在被執行Split操做。

c、讀取父Region的數據到子Region中。更新.Meta.表。

d、清理父Region,通知HMaster將新的子Region遷移到其它RegionServer中。

Split過程核心代碼以下:若是想了解有關Split的詳細流程,能夠參考:

http://punishzhou.iteye.com/blog/1233802

alt

 

B、Compaction

當Hbase將MemStore中的內容flush到StoreFile中後,因爲每次flush都會產生一個新的HFile文件。隨着一次次的flush,HFile文件愈來愈多,當達到設定的閥值時,Hbase提供了Compaction特性,會經過此機制將HFile文件進行壓縮。

Compaction機制分爲兩種方式:minor compactions和major compactions 。minor compactions是將相鄰的一些小的HFile合併成一個稍大的HFile,表演一個多路合併的過程,其文件的數目由(hbase.hstore.compaction.min)指定;而major compactions會將一個Store中的全部HFile合併成一個HFile,而且在壓縮的過程當中會進行版本合併和刪除過濾操做。好比對於那些同一個Cell中且同一個時間戳的數據,只保留最新的那個值,其餘的值將被廢棄。此外標記了刪除樣式的數據以及過時的數據也將被過濾。

其實Compaction就是將多個有序的HFile文件合併成一個有序的HFile文件的一個過程。它會建立一個StoreFileScanner來包裝每個StoreFile,而後再經過一個StoreFileScanner實例來組裝StoreFile對應的StoreFileScanner列表。經過StoreFileScanner實例提供的next()和seek()方法獲取每一個storeFile中的數據,最後再將此數據append到一個新的HFile中。

 

 

5)HTablePool

若是用戶每次發起一個請求時都建立一個HTable實例,以下建立方式:

 

Configuration conf = HBaseConfiguration.create();

HTable table = new HTable(conf, "testtable");

這種方式雖然能夠知足要求,但對於請求數比較多的狀況或者要求響應時間比較快的狀況,如上建立HTable實例就比較落伍了。由於建立Htable是一個比較耗時的過程,此外,HTable並不能保證線程安全,在多線程處理下就可能產生莫名其妙的問題。

HBase提供了HTable池特性能夠解決此問題。用戶能夠直接從HTable池中獲取HTable實例。

一、能夠經過以下構造函數來建立HTablePool實例,以下:

HTablePool()

HTablePool(Configuration config, int maxSize)

HTablePool(Configuration config, int maxSize,HTableInterfaceFactory tableFactory)

上面的第一個構造函數會默認獲取classpath下的配置,而且建立無窮大的HTable個數。用戶能夠提供定製的建立的HTable實例的工廠來,這樣建立的HTablePool中的HTable就是用戶定製的

HTable實例。maxSize參數是指定HTable池中最大持有多少個HTable實例。好比若是此size爲5,

而用戶經過getTable獲取了10次引用,那麼當用戶經過putTable方法將實例放回HTable池中時,只能放回5個實例,另外的5次將被忽略掉了。

二、建立HTablePool實例後,就能夠經過getTable方法獲取對應的表的HTable實例了。以下:

HTableInterface getTable(String tableName)

HTableInterface getTable(byte[] tableName)

三、當使用完HTable實例後,須要將HTable實例關閉,能夠採用以下方法:

void closeTablePool(String tableName)

void closeTablePool(byte[] tableName)

void putTable(HTableInterface table)

closeTablePool(tableName)至關於直接將此Table實例關閉。建議使用此方法。PutTable(FilterBase)表示將此實例放回HTable池中供下次使用。建議不要使用此方法,目前此方法也在逐漸廢棄。須要注意的是以上操做最好放到finally模塊進行處理。

 

 

5、總結

A、總的來講Hbase由於其面向列族的key-value存儲特性使得其擁有列式數據庫的優點。分佈式的Hbase應用是由客戶端和服務端進程組成,經過HDFS做爲其持久層,採用Zookeeper來完成集羣的管理和狀態監控協調服務。對於全表掃描和大數據的加載經過MapReduce來完成。Hbase無縫集成了Apache的這幾大組件來實現可伸縮,面向列族的分佈式存儲系統。

 

B、Hbase是嚴格一致性的分佈式存儲系統,從兩個方面來保證嚴格一致性問題:它提供行鎖,但不提供多行鎖和事務,保證了讀寫的原子性。此外Hbase數據存儲支持多版本和時間戳的特性。

C、Hbase能夠認爲是BigTable的開源實現,但跟BigTable仍是有不少區別。好比:Hbase的Coprocessors跟BigTable不一樣。Hbase支持服務器端的Filter以減小網絡傳輸開銷。此外Hbase支持可插拔的文件系統,目前文件系統是HDFS,BigTable是GFS。

D、Hbase經過實現服務器端的鉤子(Coprocessors)來完成二級索引。這也是BigTable沒有實現的。

相關文章
相關標籤/搜索