BloomFilter 簡介及在 Hadoop reduce side join 中的應用 1、BloomFilter能解決什麼問題? 以少量的記憶體空間判斷一個元素是否屬於這個集合, 代價是有一定的錯誤率
2、工作原理
1. 初始化一個數組, 所有位標為0, A={x1, x2, x3,…,xm} (x1, x2, x3,…,xm 初始為0)
2. 將已知集合S中的每一個數組, 按以下方式映射到A中
2.0 選取n個互相獨立的hash函數 h1, h2, … hk
2.1 將元素通過以上hash函數得到一組索引值 h1(xi), h2(xi),…,hk(xi)
2.2 將集合A中的上述索引值標記為1(如果不同元素有重複, 則重複覆蓋為1, 這是一個覓等操作)
3. 對於一個元素x, 將其根據2.0中選取的hash函數, 進行hash, 得到一組索引值 h1(x), h2(x), …,hk(x)
如果集合A中的這些索引位置上的值都是1, 表示這個元素屬於集合S, 否則則不屬於S
舉例說明:
建立一個容量為500萬的Bit Array結構(Bit Array的大小和keyword的數量決定了誤判的幾率),將集合中的每個keyword通過32個hash函數分別計算出32個數字,然後對這32個數字分別用500萬模數,然後將Bit Array中對應的位置為1,我們將其稱為特徵值。簡單的說就是將每個keyword對應到Bit Array中的32個位置上,見下圖:
當需要快速尋找某個keyword時,只要將其通過同樣的32個hash函數運算,然後映射到Bit Array中的對應位,如果Bit Array中的對應位全部是1,那麼說明該keyword匹配成功(會有誤判的幾率)。
3、幾個前提
1. hash函數的計算不能效能太差, 否則得不償失
2. 任意兩個hash函數之間必須是獨立的.
即任意兩個hash函數不存在單一相關性, 否則hash到其中一個索引上的元素也必定會hash到另一個相關的索引上, 這樣多個hash沒有意義
4、錯誤率
工作原理的第3步, 的出來的結論, 一個是絕對靠譜的, 一個是不能100%靠譜的。在判斷一個元素是否屬於某個集合時,有可能會把不屬於這個集合的元素誤認為屬於這個集合(false positive)。因此,Bloom Filter不適合那些“零錯誤”的應用場合。而在能容忍低錯誤率的應用場合下,Bloom Filter通過極少的錯誤換取了儲存空間的極大節省。關於具體的錯誤率,這和最優的雜湊函數個數以及位元組的大小有關,而這是可以估算求得一個最優解的:
雜湊函數個數k、位元組大小m及字串數量n之間存在相互關係。相關文獻證明了對於給定的m、n,當 k = ln(2)* m/n 時出錯的機率是最小的。 具體的請看:http://blog.csdn.net/jiaomeng/article/details/1495500
5、基本特徵
從以上對基本原理和數學基礎的分析,我們可以得到Bloom filter的如下基本特徵,用於指導實際應用。
(1)存在一定錯誤率,發生在正向判斷上(存在性),反向判斷不會發生錯誤(不存在性);
(2)錯誤率是可控制的,通過改變位元組大小、hash函數個數或更低碰撞率的hash函數來調節;
(3)保持較低的錯誤率,位元組空位至少保持在一半以上;
(4)給定m和n,可以確定最優hash個數,即k = ln2 * (m/n),此時錯誤率最小;
(5)給定允許的錯誤率E,可以確定合適的位元組大小,即m >= log2(e) * (n * log2(1/E)),繼而確定hash函數個數k;
(6)正向錯誤率無法完全消除,即使不對位元組大小和hash函數個數進行限制,即無法實現零錯誤率;
(7)空間效率高,僅儲存“存在狀態”,但無法儲存完整資訊,需要其他資料結構輔助儲存;
(8)不支援元素刪除操作,因為不能保證刪除的安全性。
6、應用情境舉例:
(1)拼字檢查、資料庫系統、檔案系統
(2)假設要你寫一個網路蜘蛛(web crawler)。由於網路間的連結錯綜複雜,蜘蛛在網路間爬行很可能會形成“環”。為了避免形成“環”,就需要知道蜘蛛已經訪問過那些URL。給一個URL,怎樣知道蜘蛛是否已經訪問過呢。
(3)網路應用
P2P網路中尋找資源操作,可以對每條網路通路儲存Bloom Filter,當命中時,則選擇該通路訪問。
廣播訊息時,可以檢測某個IP是否已發包。
檢測廣播訊息包的環路,將Bloom Filter儲存在包裡,每個節點將自己添加入Bloom Filter。
資訊隊列管理,使用Counter Bloom Filter管理資訊流量。
(4)垃圾郵件地址過濾
像網易,QQ這樣的公眾電子郵件(email)供應商,總是需要過濾來自發送垃圾郵件的人(spamer)的垃圾郵件。一個辦法就是記錄下那些發垃圾郵件的email 地址。由於那些寄件者不停地在註冊新的地址,全世界少說也有幾十億個發垃圾郵件的地址,將他們都存起來則需要大量的網路伺服器。如果用雜湊表,每儲存一億個 email 地址,就需要1.6GB 的記憶體(用雜湊表實現的具體辦法是將每一個email 地址對應成一個八位元組的資訊指紋,然後將這些資訊指紋存入雜湊表,由於雜湊表的儲存效率一般只有50%,因此一個email 地址需要佔用十六個位元組。一億個地址大約要1.6GB, 即十六億位元組的記憶體)。因此存貯幾十億個郵件地址可能需要上百GB 的記憶體。而Bloom Filter只需要雜湊表1/8 到1/4 的大小就能解決同樣的問題。Bloom Filter決不會漏掉任何一個在黑名單中的可疑地址。而至於誤判問題,常見的補救辦法是在建立一個小的白名單,儲存那些可能別誤判的郵件地址。
(5)Bloomfilter在HBase中的作用
HBase利用Bloomfilter來提高隨機讀(Get)的效能,對於順序讀(Scan)而言,設定Bloomfilter是沒有作用的(0.92以後,如果設定了bloomfilter為ROWCOL,對於指定了qualifier的Scan有一定的最佳化,但不是那種直接過濾檔案,排除在尋找範圍的形式)
Bloomfilter在HBase中的開銷。
Bloomfilter是一個列族(cf)層級的配置屬性,如果你在表中設定了Bloomfilter,那麼HBase會在產生StoreFile時包含一份bloomfilter結構的資料,稱其為MetaBlock;MetaBlock與DataBlock(真實的KeyValue資料)一起由LRUBlockCache維護。所以,開啟bloomfilter會有一定的儲存及記憶體cache開銷。
Bloomfilter如何提高隨機讀(Get)的效能。
對於某個region的隨機讀,HBase會遍曆讀memstore及storefile(按照一定的順序),將結果合并返回給用戶端。如果你設定了bloomfilter,那麼在遍曆讀storefile時,就可以利用bloomfilter,忽略某些storefile。
注意:hbase的bloom filter是惰性載入的,在寫壓力比較大的情況下,會有不停的compact併產生storefile,那麼新的storefile是不會馬上將bloom filter載入到記憶體的,等到讀請求來的時候才載入。
這樣問題就來了,第一,如果storefile設定的比較大,max size為2G,這會導致bloom filter也比較大;第二,系統的讀寫壓力都比較大。這樣或許會經常出現單個 GET請求花費3-5秒的逾時現象。
7、reduce side join + BloomFilter 在hadoop中的應用舉例:
在某些情況下,SemiJoin抽取出來的小表的key集合在記憶體中仍然存放不下,這時候可以使用BloomFiler以節省空間的。將小表中的key儲存到BloomFilter中,在map階段過濾大表,可能有一些不在小表中的記錄沒有過濾掉(但是在小表中的記錄一定不會過濾掉),這沒關係,只不過增加了少量的網路IO而已。最後再在reduce階段做表間join即可。
這個過程其實需要先對小表的資料做BloomFilter訓練,構造一個BloomFilter樣本檔案(二進位的),放到分布式緩衝,然後在map階段被讀入用來過濾大表。而hadoop早已經支援 BloomFilter 了,我們只需調相應的API即可,ok 下面上代碼了。
| 01 |
import java.io.BufferedReader; |
| 02 |
import java.io.IOException; |
| 03 |
import java.io.InputStreamReader; |