【每日五分鐘搞定大數據】系列,HBase第五篇。上一篇咱們落下了Bloom Filter,此次咱們來聊聊這個東西。
算法
先簡單的介紹下Bloom Filter(布隆過濾器)是1970年由布隆提出的。它其實是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器能夠用於檢索一個元素是否在一個集合中。它的優勢是空間效率和查詢時間都遠遠超過通常的算法,缺點是有必定的誤識別率和刪除困難。數組
在計算機科學中,咱們經常會碰到時間換空間或者空間換時間的狀況,即爲了達到某一個方面的最優而犧牲另外一個方面。Bloom Filter在時間空間這兩個因素以外又引入了另外一個因素:錯誤率。在使用Bloom Filter判斷一個元素是否屬於某個集合時,會有必定的錯誤率。也就是說,有可能把不屬於這個集合的元素誤認爲屬於這個集合(False Positive),但不會把屬於這個集合的元素誤認爲不屬於這個集合(False Negative)。在增長了錯誤率這個因素以後,Bloom Filter經過容許少許的錯誤來節省大量的存儲空間。簡單地說就是寧肯放過也不殺錯。ide
它的用法實際上是很容易理解的,咱們拿個HBase中應用的例子來講下,咱們已經知道rowKey存放在HFile中,那麼爲了從一系列的HFile中查詢某個rowkey,咱們就能夠經過 Bloom Filter 快速判斷 rowkey 是否在這個HFile中,從而過濾掉大部分的HFile,減小須要掃描的Block。函數
BloomFilter對於HBase的隨機讀性能相當重要,對於get操做以及部分scan操做能夠剔除掉不會用到的HFile文件,減小實際IO次數,提升隨機讀性能。在此簡單地介紹一下Bloom Filter的工做原理,Bloom Filter使用位數組來實現過濾,初始狀態下位數組每一位都爲0,以下圖所示:性能
假如此時有一個集合S = {x1, x2, … xn},Bloom Filter使用k個獨立的hash函數,分別將集合中的每個元素映射到{1,…,m}的範圍。對於任何一個元素,被映射到的數字做爲對應的位數組的索引,該位會被置爲1。好比元素x1被hash函數映射到數字8,那麼位數組的第8位就會被置爲1。下圖中集合S只有兩個元素x和y,分別被3個hash函數進行映射,映射到的位置分別爲(0,3,6)和(4,7,10),對應的位會被置爲1:
大數據
如今假如要判斷另外一個元素是不是在此集合中,只須要被這3個hash函數進行映射,查看對應的位置是否有0存在,若是有的話,表示此元素確定不存在於這個集合,不然有可能存在。下圖所示就表示z確定不在集合{x,y}中:
google
從上面的內容咱們能夠得知,Bloom Filter有兩個很重要的參數「」 哈希函數個數、位數組的大小 這兩個code
咱們來理一下 HFile 中和 Bloom Filter 相關的Block,blog
Scanned Block Section(掃描HFile時被讀取):Bloom Block索引
Load-on-open-section(regionServer啓動時加載到內存):BloomFilter Meta Block、Bloom Index Block
HBase中每一個HFile都有對應的位數組,KeyValue在寫入HFile時會先通過幾個hash函數的映射,映射後將對應的數組位改成1,get請求進來以後再進行hash映射,若是在對應數組位上存在0,說明該get請求查詢的數據不在該HFile中。
HFile中的Bloom Block中存儲的就是上面說得位數組,當HFile很大時,Data Block 就會不少,同時KeyValue也會不少,須要映射入位數組的rowKey也會不少,因此爲了保證準確率,位數組就會相應越大,那Bloom Block也會越大,爲了解決這個問題就出現了Bloom Index Block,做用和 Data Index Block 相似,一個HFile中有多個Bloom Block(位數組),根據rowKey拆分,一部分連續的Key使用一個位數組。這樣查詢rowKey就要先通過Bloom Index Block(在內存中)定位到Bloom Block,再把Bloom Block加載到內存,進行過濾。
謂詞下推
1.Bloom Filter 在小表處生成;
2.廣播到大表處;
3.大表根據 Bloom Filter 進行過濾;
4.剩下的數據傳入 JoinNode 進行關聯。
注意:Bloom Filter 處理 join 並非老是有效地,若是JOIN兩邊的表並不能過濾到不少數據,例如左表和右表中Join鍵的差集並不大,這種狀況下反而浪費了資源計算Bloom Filter和應用Bloom Filter
Google Guava library爲咱們提供了Bloom Filter的實現,直接用就能夠啦:com.google.common.hash.BloomFilter
private final BloomFilter<String> bloomFilter = BloomFilter.create(new Funnel<String>() { private static final long serialVersionUID = 1L; @Override public void funnel(String arg0, PrimitiveSink arg1) { arg1.putString(arg0, Charsets.UTF_8); } }, 1024*1024*32); public synchronized boolean contains(String id){ if(StringUtils.isEmpty(id)){ return true; } boolean exists = bloomFilter.mightContain(id); //布隆過濾器是否包含這個id if(!exists){ bloomFilter.put(id); //添加進布隆過濾器 } return exists; }
關於Bloom Filter 的內容就講到這啦,接下來咱們講回StoreFile的合併過程,敬請持續關注。