1. HBase是什麼呢,都有哪些特點呢
Hbase是一種NoSQL資料庫,這意味著它不像傳統的RDBMS資料庫那樣支持SQL作為查詢語言。Hbase是一種分布式存儲的資料庫,技術上來講,它更像是分布式存儲而不是分布式資料庫,它缺少很多RDBMS系統的特性,比如列類型,輔助索引,觸發器,和高級查詢語言等待
那Hbase有什麼特性呢?如下:
強讀寫一致,但是不是「最終一致性」的數據存儲,這使得它非常適合高速的計算聚合
自動分片,通過Region分散在集群中,當行數增長的時候,Region也會自動的切分和再分配
自動的故障轉移
Hadoop/HDFS集成,和HDFS開箱即用,不用太麻煩的銜接
豐富的「簡潔,高效」API,Thrift/REST API,Java API
塊緩存,布隆過濾器,可以高效的列查詢優化
操作管理,Hbase提供了內置的web界面來操作,還可以監控JMX指標
什麼時候用Hbase?
Hbase不適合解決所有的問題:
首先資料庫量要足夠多,如果有十億及百億行數據,那麼Hbase是一個很好的選項,如果只有幾百萬行甚至不到的數據量,RDBMS是一個很好的選擇。因為數據量小的話,真正能工作的機器量少,剩餘的機器都處於空閑的狀態
其次,如果你不需要輔助索引,靜態類型的列,事務等特性,一個已經用RDBMS的系統想要切換到Hbase,則需要重新設計系統。
最後,保證硬體資源足夠,每個HDFS集群在少於5個節點的時候,都不能表現的很好。因為HDFS默認的復制數量是3,再加上一個NameNode。
Hbase在單機環境也能運行,但是請在開發環境的時候使用。
內部應用
存儲業務數據:車輛GPS信息,司機點位信息,用戶操作信息,設備訪問信息。。。
存儲日誌數據:架構監控數據(登錄日誌,中間件訪問日誌,推送日誌,簡訊郵件發送記錄。。。),業務操作日誌信息
存儲業務附件:UDFS系統存儲圖像,視頻,文檔等附件信息
不過在公司使用的時候,一般不使用原生的Hbase API,使用原生的API會導致訪問不可監控,影響系統穩定性,以致於版本升級的不可控。
HFile
HFile是Hbase在HDFS中存儲數據的格式,它包含多層的索引,這樣在Hbase檢索數據的時候就不用完全的載入整個文件。索引的大小(keys的大小,數據量的大小)影響block的大小,在大數據集的情況下,block的大小設置為每個RegionServer 1GB也是常見的。
探討資料庫的數據存儲方式,其實就是探討數據如何在磁碟上進行有效的組織。因為我們通常以如何高效讀取和消費數據為目的,而不是數據存儲本身。
Hfile生成方式
起初,HFile中並沒有任何Block,數據還存在於MemStore中。
Flush發生時,創建HFile Writer,第一個空的Data Block出現,初始化後的Data Block中為Header部分預留了空間,Header部分用來存放一個Data Block的元數據信息。
而後,位於MemStore中的KeyValues被一個個append到位於內存中的第一個Data Block中:
註:如果配置了Data Block Encoding,則會在Append KeyValue的時候進行同步編碼,編碼後的數據不再是單純的KeyValue模式。Data Block Encoding是HBase為了降低KeyValue結構性膨脹而提供的內部編碼機制。
2. HBase條件查詢(多條件查詢)
轉 https://blog.csdn.net/PirateLeo/article/details/7956965
文中可能涉及到的API:
Hadoop/HDFS: http://hadoop.apache.org/common/docs/current/api/
HBase: http://hbase.apache.org/apidocs/index.html?overview-summary.html
Begin!
HBase的查詢實現只提供兩種方式:
1、按指定RowKey獲取唯一一條記錄,get方法(org.apache.hadoop.hbase.client.Get)
2、按指定的條件獲取一批記錄,scan方法(org.apache.hadoop.hbase.client.Scan)
實現條件查詢功能使用的就是scan方式,scan在使用時有以下幾點值得注意:
1、scan可以通過setCaching與setBatch方法提高速度(以空間換時間);
2、scan可以通過setStartRow與setEndRow來限定范圍。范圍越小,性能越高。
通過巧妙的RowKey設計使我們批量獲取記錄集合中的元素挨在一起(應該在同一個Region下),可以在遍歷結果時獲得很好的性能。
3、scan可以通過setFilter方法添加過濾器,這也是分頁、多條件查詢的基礎。
下面舉個形象的例子:
我們在表中存儲的是文件信息,每個文件有5個屬性:文件id(long,全局唯一)、創建時間(long)、文件名(String)、分類名(String)、所有者(User)。
我們可以輸入的查詢條件:文件創建時間區間(比如從20120901到20120914期間創建的文件),文件名(「中國好聲音」),分類(「綜藝」),所有者(「浙江衛視」)。
假設當前我們一共有如下文件:
內容列表
ID CreateTime Name Category UserID
1 20120902 中國好聲音第1期 綜藝 1
2 20120904 中國好聲音第2期 綜藝 1
3 20120906 中國好聲音外卡賽 綜藝 1
4 20120908 中國好聲音第3期 綜藝 1
5 20120910 中國好聲音第4期 綜藝 1
6 20120912 中國好聲音選手采訪 綜藝花絮 2
7 20120914 中國好聲音第5期 綜藝 1
8 20120916 中國好聲音錄制花絮 綜藝花絮 2
9 20120918 張瑋獨家專訪 花絮 3
10 20120920 加多寶涼茶廣告 綜藝廣告 4
這里UserID應該對應另一張User表,暫不列出。我們只需知道UserID的含義:
1代表 浙江衛視; 2代表 好聲音劇組; 3代表 XX微博; 4代表 贊助商。
調用查詢介面的時候將上述5個條件同時輸入find(20120901,20121001,"中國好聲音","綜藝","浙江衛視")。
此時我們應該得到記錄應該有第1、2、3、4、5、7條。第6條由於不屬於「浙江衛視」應該不被選中。
我們在設計RowKey時可以這樣做:採用UserID + CreateTime + FileID組成rowKey,這樣既能滿足多條件查詢,又能有很快的查詢速度。
需要注意以下幾點:
1、每條記錄的RowKey,每個欄位都需要填充到相同長度。假如預期我們最多有10萬量級的用戶,則userID應該統一填充至6位,如000001,000002...
2、結尾添加全局唯一的FileID的用意也是使每個文件對應的記錄全局唯一。避免當UserID與CreateTime相同時的兩個不同文件記錄相互覆蓋。
按照這種RowKey存儲上述文件記錄,在HBase表中是下面的結構:
rowKey(userID 6 + time 8 + fileID 6) name category ....
00000120120902000001
00000120120904000002
00000120120906000003
00000120120908000004
00000120120910000005
00000120120914000007
00000220120912000006
00000220120916000008
00000320120918000009
00000420120920000010
怎樣用這張表?
在建立一個scan對象後,我們setStartRow(00000120120901),setEndRow(00000120120914)。
這樣,scan時只掃描userID=1的數據,且時間范圍限定在這個指定的時間段內,滿足了按用戶以及按時間范圍對結果的篩選。並且由於記錄集中存儲,性能很好。
然後使用SingleColumnValueFilter(org.apache.hadoop.hbase.filter.SingleColumnValueFilter),共4個,分別約束name的上下限,與category的上下限。滿足按同時按文件名以及分類名的前綴匹配。
(注意:使用SingleColumnValueFilter會影響查詢性能,在真正處理海量數據時會消耗很大的資源,且需要較長的時間。
在後續的博文中我將多舉幾種應用場景下rowKey的,可以滿足簡單條件下海量數據瞬時返回的查詢功能)
如果需要分頁還可以再加一個PageFilter限制返回記錄的個數。
以上,我們完成了高性能的支持多條件查詢的HBase表結構設計。
3. hbase shell 中有版本過濾器嗎
進入hbase shell console
$HBASE_HOME/bin/hbase shell
如果有kerberos認證,需要事先使用相應的keytab進行一下認證(使用kinit命令),認證成功之後再使用hbase shell進入可以使用whoami命令可查看當前用戶!
4. hbase的核心數據結構是什麼
hbase的核心數據結構為LSM樹。
LSM樹分為內存部分和磁碟部分。
內存部分是一個維護有序數據集合的數據結構。一般來講,內存數據結構可以選擇平衡二叉樹、紅黑樹、跳躍表(SkipList)等維護有序集的數據結構,由於考慮並發性能,HBase選擇了表現更優秀的跳躍表。
磁碟部分是由一個個獨立的文件組成,每一個文件又是由一個個數據塊組成。對於數據存儲在磁碟上的資料庫系統來說,磁碟尋道以及數據讀取都是非常耗時的操作(簡稱IO耗時)。為了避免不必要的IO耗時,可以在磁碟中存儲一些額外的二進制數據,這些數據用來判斷對於給定的key是否有可能存儲在這個數據塊中,這個數據結構稱為布隆過濾器(BloomFilter)。
LSM樹介紹:
LSM樹是一種磁碟數據的索引結構。LSM樹的索引對寫入請求更友好。因為無論是何種寫入請求,LSM樹都會將寫入操作處理為一次順序寫,而HDFS擅長的正是順序寫(且HDFS不支持隨機寫)。
一個LSM樹的索引內存部分是一個ConcurrentSkipListMap,Key是rowkey、column family、qualifier、type以及timestamp, Value是位元組數組。隨著數據不斷寫入MemStore,一旦內存超過閾值會將數據flush到磁碟,生產HFile;多個小HFile文件會compact成一個大HFile。
5. 深入理解HBASE(4)HFile
1)HFile由DataBlock、Meta信息(Index、BloomFilter)、Info等信息組成。
2)整個DataBlock由一個或者多個KeyValue組成。
3)在文件內按照Key排序。
這里只介紹V2版本的,HFileV1的數據格式在0.92版本升級到V2版本。
1)文件分為三部分:Scanned block section,Non-scanned block section,以及Opening-time data section
Scanned block section:表示順序掃描HFile時(包含所有需要被讀取的數據)所有的數據塊將會被讀取,包括Leaf Index Block和Bloom Block;
Non-scanned block section:HFile順序掃描的時候該部分數據不會被讀取,主要包括Meta Block即BloomFilter和Intermediate Level Data Index Blocks兩部分;
Load-on-open-section:這部分數據在HBase的region server啟動時,需要載入到內存中。包括FileInfo、Bloom filter block、data block index和meta block index;
Trailer:這部分主要記錄了HFile的基本信息、各個部分的偏移值和定址信息。
Leaf index block具體存儲了DataBlock的offset、length、以及firstkey的信息。
RootDataIndex 存儲的是每個Leaf index block的offset、length、Leaf index Block記錄的第一個key,以及截至到該Leaf Index Block記錄的DataBlock的個數。
假定DataBlock的個數足夠多,HFile文件又足夠大的情況下,默認的128KB的長度的ROOTDataIndex仍然存在超過chunk大小的情況時,會分成更多的層次。這樣最終的可能是ROOT INDEX –> IntermediateLevel ROOT INDEX(可以是多層) —〉Leaf index block
在ROOT INDEX中會記錄Mid Key所對應的信息,幫助在做File Split或者折半查詢時快速定位中間Row的信息。
HFile V2的寫操作流程:
1)Append KV到 Data Block。在每次Append之前,首先檢查當前DataBlock的大小是否超過了默認的設置,如果不超出閾值,寫入輸出流。如果超出了閾值,則執行finishBlock(),按照Table-CF的設置,對DataBlock進行編碼和壓縮,然後寫入HFile中。//以Block為單位進行編碼和壓縮,會有一些性能開銷,可以參考 HBase實戰系列1—壓縮與編碼技術
2)根據數據的規模,寫入Leaf index block和Bloom block。
Leaf index Block,每次Flush一個DataBlock會在該Block上添加一條記錄,並判斷該Block的大小是否超過閾值(默認128KB),超出閾值的情況下,會在DataBlock之後寫入一個Leaf index block。對應的控制類:HFileBlockIndex,內置了BlockIndexChunk、BlockIndexReader和BlockIndexWriter(實現了InlineBlockWriter介面)。
Bloom Block設置:默認使用MURMUR hash策略,每個Block的默認大小為128KB,每個BloomBlock可以接收的Key的個數通過如下的公式計算,接收的key的個數 與block的容量以及errorRate的之間存在一定的關系,如下的計算公式中,可以得到在系統默認的情況下,每個BloomBlock可以接納109396個Key。
注意:影響BloomBlock個數的因素,顯然受到HFile內KeyValue個數、errorRate、以及BlockSize大小的影響。可以根據應用的需求合理調整相關控制參數。
每一個BloomBlock會對應index信息,存儲在Meta Index區域。
這樣在載入數據的時候,只需載入不超過128KB的RootDataIndex以及IntermediateLevelRootIndex,而避免載入如HFile V1的所有的Leaf index block信息,同樣,也只需要載入BloomBlockIndex信息到內存,這樣避免在HFile V1格式因為載入過大的DataBlockIndex造成的開銷,加快Region的載入速度。
在HFile中根據一個key搜索一個data的過程:
1、先內存中對HFile的root index進行二分查找。如果支持多級索引的話,則定位到的是leaf/intermediate index,如果是單級索引,則定位到的是data block
2、如果支持多級索引,則會從緩存/hdfs(分布式文件系統)中讀取leaf/intermediate index chunk,在leaf/intermediate chunk根據key值進行二分查找(leaf/intermediate index chunk支持二分查找),找到對應的data block。
3、從緩存/hdfs中讀取data block
4、在data block中遍歷查找key。
1、 首先讀取文件尾的4位元組Version信息(FileTrailer的version欄位)。
2、 根據Version信息得到Trailer的長度(不同版本有不同的長度),然後根據trailer長度,載入FileTrailer。
3、 載入load-on-open部分到內存中,起始的文件偏移地址是trailer中的loadOnOpenDataOffset,load-on-open部分長度等於(HFile文件長度 - HFileTrailer長度)
Load-on-open各個部分的載入順序如下:
依次載入各部分的HFileBlock(load-on-open所有部分都是以HFileBlock格式存儲):data index block、meta index block、FileInfo block、generate bloom filter index、和delete bloom filter。HFileBlock的格式會在下面介紹。
在hfile中,所有的索引和數據都是以HFileBlock的格式存在在hdfs中,
HFile version2的Block格式如下兩圖所示,有兩種類型,第一種類型是沒有checksum;第二種是包含checksum。對於block,下圖中的綠色和淺綠色的內存是block header;深紅部分是block data;粉紅部分是checksum。
第一種block的header長度= 8 + 2 * 4 + 8;
第二種block的header長度=8 + 2 * 4 + 8 + 1 + 4 * 2;
BlockType:8個位元組的magic,表示不同的block 類型。
CompressedBlockSize:表示壓縮的block 數據大小(也就是在HDFS中的HFileBlock數據長度),不包括header長度。
UncompressedBlockSize:表示未經壓縮的block數據大小,不包括header長度。
PreBlockOffset:前一個block的在hfile中的偏移地址;用於訪問前一個block而不用跳到前一個block中,實現類似於鏈表的功能。
CheckSumType:在支持block checksum中,表示checksum的類型。
bytePerCheckSum:在支持checksum的block中,記錄了在checksumChunk中的位元組數;records the number of bytes in a checksum chunk。
SizeDataOnDisk:在支持checksum的block中,記錄了block在disk中的數據大小,不包括checksumChunk。
DataBlock是用於存儲具體kv數據的block,相對於索引和meta(這里的meta是指bloom filter)DataBlock的格式比較簡單。
在DataBlock中,KeyValue的分布如下圖,在KeyValue後面跟一個timestamp。
HFile中的index level是不固定的,根據不同的數據類型和數據大小有不同的選擇,主要有兩類,一類是single-level(單級索引),另一類是multi-level(多級索引,索引block無法在內存中存放,所以採用多級索引)。
HFile中的index chunk有兩大類,分別是root index chunk、nonRoot index chunk。而nonRoot index chunk又分為interMetadiate index chunk和leaf index chunk,但intermetadiate index chunk和leaf index chunk在內存中的分布是一樣的。
對於meta block和bloom block,採用的索引是single-level形式,採用single-level時,只用root index chunk來保存指向block的索引信息(root_index-->xxx_block)。
而對於data,當HFile的data block數量較少時,採用的是single level(root_index-->data_block)。當data block數量較多時,採用的是multi-level,一般情況下是兩級索引,使用root index chunk和leaf index chunk來保存索引信息(root_index-->leaf_index-->data_block);但當data block數量很多時,採用的是三級索引,使用root index chunk、intermetadiate index chunk和leaf index chunk來保存指向數據的索引(root_index-->intermediate_index-->leaf_index-->data_block)。
所有的index chunk都是以HFileBlock格式進行存放的,首先是一個HFileBlock Header,然後才是index chunk的內容。
Root index適用於兩種情況:
1、作為data索引的根索引。
2、作為meta和bloom的索引。
在Hfile Version2中,Meta index和bloom index都是single-level,也都採用root索引的格式。Data index可以single-level和multi-level的這形式。Root index可以表示single-level index也可以表示multi-level的first level。但這兩種表示方式在內存中的存儲方式是由一定差別,見圖。
對於multi-level root index,除了上面index entry數組之外還帶有格外的數據mid-key的信息,這個mid-key是用於在對hfile進行split時,快速定位HFile的中間位置所使用。Multi-level root index在硬碟中的格式見圖3.4。
Mid-key的信息組成如下:
1、Offset:所在的leaf index chunk的起始偏移量
2、On-disk size:所在的leaf index chunk的長度
3、Key:在leaf index chunk中的位置。
6. hbase中rowkey設置問題。
主鍵設計成:現有的主鍵+頻度+列,即h+1+hi,但是最好將每個都格式化成定長的字元串,當你需要取前5個記錄時使用過濾器取出前5條記錄即可。大體如此,具體細節可能還需要好好設計
7. hbase(一) : HTable
hbase1.3
HTable 是我們對數據讀取,操作的入口, implements HTableInterface, RegionLocator
內部構造
有一個檢查 的動作待詳細查看.
關於BufferedMutator, 是用來緩存客戶端的操作的, hbase 將客戶端的DML抽象成了 Mutation , 子類有: Append, Delete, Increment, Put 操作.
put方法將Put對象包裝成Mutation,交給BufferedMutator, 到達設置的大小限制,或者主動調用flush操作, 會觸發 backgroundFlushCommits(boolean synchronous) 操作, 然後Mutation由 AsyncProcess 提交,詳細查看 BufferedMutatorImpl 類.
由 AscncProcess 提交後, (注釋:Action類是將行與對應操作結合的類), 由connection去尋找每一行對應的region位置, 包裝action, server, region等信息添加到 MutiAction 中去, 這個類持有按照region分組的actions,
然後會對每個action都創建 SingleServerRequestRunnable (rpc caller 和rpc callable, caller call callable), 交給線程池去運行.
刪除操作很簡單: 創建 RegionServerCallable , 然後rpc工廠類創建rpc caller來調用它
get和scan都是繼承了Query
get很簡單:首先檢查,這個get是否只是檢查數據存在否, 並且檢查是否指定了一致性等級(默認 (Consistency.STRONG) ), 之後創建rpc請求Request, 如果 不是強一致性Consistency.TIMELINE , 則調用 , 它可以從replica上讀取, 返回的數據被標記為stale(讀操作是通過 Consistency.TIMELINE ,然後讀RPC將會首先發送到主region伺服器上,在短時間內(hbase.client.primaryCallTimeout.get默認為10ms),如果主region沒有響應RPC會被發送到從region。 之後結果會從第一個完成RPC的返回。如果響應是來自主region副本,我們就會知道數據是最新的,Result.isStale() API是檢查過期數據,如果結果是 從region返回,那麼Result.isStale()為true,然後用戶就可以檢查關於過期數據可能的原因。).
當replica_id=0的regin不可以時候, 給所有的replica region發送請求,獲取第一個從這些replica返回的數據, 客戶端可以 Result.isStale()檢查是否是來自副本的數據
Scan 類可以設置一系列的屬性, startkey,endkey, 過濾器, 版本,緩存,最大取回大小等等, 但是獲取數據是由 getScanner(Scan)返回的 ResultScanner 操作的.
返回的 ResultScanner 有small, Reversed,big和純client 的不同,
什麼是small scan?
見 https://issues.apache.org/jira/browse/HBASE-7266
hbase裡面有兩種讀操作:pread and seek+read.
pread是一個函數,用於帶偏移量地原子的從文件中讀取數據。
讀路徑: https://yq.aliyun.com/articles/602377/
seek + read is fast but can cause two problem:
(1) resource contention
(2) cause too much network io
另外,一些其他的解釋:前者適合小於一個數據塊(默認64k)的smallScan(openScanner, next, closeScanner將在同一次rpc中實現);而後者則會使用hdfs預讀取,在DN中緩存一些數據(HBASE-7266、HBASE-9488)
查看最普通的 ClientScanner , 初始化的時候調用 , 這個方法去調用 nextScanner() , 獲取為下一個region准備的scanner , 這個scanner會發起rpc調用, 參數是 ScannerCallable 對象, 這是scanner 的動作的抽象, reversed 和 small 都有對應的 ScannerCallable 子類
ScannerCallable 在初始化後, 的prepare階段, 會從對應的region,獲取對應的server, 獲取這個server的ClientService.BlockingInterface介面, 並設置, 以便被調用的時候知道該向誰發起rpc請求
call階段, 在創建 RequestScan的時候, 參數nextCallSeq在client和server端都維持,,每次調用都會增加, 是為了client能正確獲取下一批的數據 .