1、布隆過濾器
內容參考:https://www.jianshu.com/p/2104d11ee0a2
1、數據結構
布隆過濾器是一個BIT數組,本質上是一個數據,所以可以根據下標快速找數據
2、哈希映射
1、布隆需要記錄見過的數據,這里的記錄需要通過hash函數對數據進行hash操作,得到數組下標並存儲在BIT 數組里記為1。這樣的記錄一個數據只占用1BIT空間
2、判斷是否存在時:給布隆過濾器一個數據,進行hash得到下標,從BIT數組里取數據如果是1 則說明數據存在,如果是0 說明不存在
3、精確度
hash算法存在碰撞的可能,所以不同的數據可能hash為一個下標數據,故為了提高精確度就需要 使用多個hash 算法標記一個數據,和增大BIT數組的大小
也是因為如此,布隆過濾器判斷為【數據存在】 可能數據並不存在,但是如果判斷為【數據不存在】那么數據就一定是不存在的。
4、例子
下圖映射 baidu字樣到布隆過濾器中,用了三個不同的hash函數 3BIT 判斷一個數據,BIT數組大小為8
哈希函數返回 1、4、7
我們現在再存一個值 “tencent”,如果哈希函數返回 3、4、8 的話,圖繼續變為:
以下 4 位置發生了hash碰撞
5、如何選擇哈希函數個數和布隆過濾器長度
顯然,過小的布隆過濾器很快所有的 bit 位均為 1,那么查詢任何值都會返回“可能存在”,起不到過濾的目的了。布隆過濾器的長度會直接影響誤報率,布隆過濾器越長其誤報率越小。
另外,哈希函數的個數也需要權衡,個數越多則布隆過濾器 bit 位置位 1 的速度越快,且布隆過濾器的效率越低;但是如果太少的話,那我們的誤報率會變高。
k 為哈希函數個數,m 為布隆過濾器長度,n 為插入的元素個數,p 為誤報率。
6、不支持刪除
布隆過濾器只能插入數據判斷是否存在,不能刪除,而且只能保證【不存在】判斷絕對准確
以上不難看出如果給數組的每個BIT位上加一個計數器,插入的時候+1 刪除的時候 –1 就可以實現刪除。
但是加計數器的實現是有問題的:
由於hash碰撞問題,布隆過濾器不能准確判斷數據是否存在,就不能隨意刪除。其次計數器的回繞問題也需要考慮。
2、給redis安裝布隆過濾器模塊
1、下載:
地址:https://github.com/RedisBloom/RedisBloom
下載ZIP 文件,上傳到linux
RedisBloom-master.zip
2、解壓編譯
命令:
unzip RedisBloom-master.zip
cd RedisBloom-master
make
掃行完以上命令 后文件夾內生成一個文件名為:redisbloom.so
3、啟動redis 時加載該模塊
命令:
redis-server redis-6381.conf --loadmodule /zjl/software/RedisBloom-master/redisbloom.so
3、驗證
1、鏈接redis
命令:
redis-cli –a zjl123
2、測試布隆過濾
命令:
bf.add zjl 123
bf.exists zjl 123 #返回 1 ,說明存在值
bf.exists zjl 321 #返回 0, 說明不存在該值
3、准確率
Redis中有一個命令可以來設置布隆過濾器的准確率:
bf.reserve zjl 0.01 100
bf.reserve 有三個參數,分別是 key, error_rate 和 initial_size 。
錯誤率越低,需要的空間越大。
initial_size 參數表示預計放 入的元素數量,當實際數量超出這個數值時,誤判率會上升。
所以需要提前設置一個較大的數值避免超出導致誤判率升高。
如果不使用 bf.reserve,默認的 error_rate 是 0.01,默認的 initial_size 是 100。
布隆過濾器的 initial_size 估計的過大,會浪費存儲空間,估計的過小,就會 影響准確率,
用戶在使用之前一定要盡可能地精確估計好元素數量,還需要加上 一定的冗余空間以避免實際元素可能會意外高出估計值很多。
布隆過濾器的 error_rate 越小,需要的存儲空間就越大,對於不需要過於精確 的場合, error_rate 設置稍大一點也無傷大雅。
比如在新聞去重上而言,誤判 率高一點只會讓小部分文章不能讓合適的人看到,
文章的整體閱讀量不會因為這 點誤判率就帶來巨大的改變。
4、項目中使用
1、redis布隆過濾器
沒有找到jedis 支持bloom過濾器 命令的版本,只找到了另外一個JAR包的支持,但是也不太好用,沒弄明白如何添加密碼連接
引入包
<dependency> <groupId>com.redislabs</groupId> <artifactId>jrebloom</artifactId> <version>1.0.2</version> </dependency>JAR包里只有三個類,對連接方式 和 數據類型 的支持都不夠
代碼:
Client client = new Client(redisProperties.getHost(), redisProperties.getPort(), 10000, 100); client.add("zjl", "123"); boolean zjl = client.exists("zjl", "123"); System.out.println(zjl);2、Guava中的BloomFilter
google的guava包中提供了BloomFilter類,直接用的是服務器內存
導入包
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>22.0</version> </dependency>代碼:
private static int size = 1000000; private static BloomFilter<String> bloomFilter = BloomFilter.create(Funnels.stringFunnel(Charset.defaultCharset()), size, 0.0001); public void test2() { String aa = "zjl"; bloomFilter.put(aa); System.out.println(bloomFilter.mightContain(aa)); }3、自已實現布隆過濾器
java 有bitSet數組,hash函數可以自己手動實現
自己手寫是可以實現布隆過濾器的,在此不做研究。