谈谈布隆过滤器
介紹
先來一段官方的介紹:
布隆過濾器(Bloom Filter)是1970年由布隆提出的。它實際上是一個很長的二進制向量和一系列隨機映射函數。布隆過濾器可以用于檢索一個元素是否在一個集合中。它的優點是空間效率和查詢時間都遠遠超過一般的算法,缺點是有一定的誤識別率和刪除困難。
嗯,你好像沒聽懂。
圖例
現在這里有一個長度為m的大數組,所有位置初始值都是0。
下圖時是當m為8時的結構。(實際上,m是很大很大的)
有k個哈希函數(Hash1,Hash2,Hash3...),每個函數都會將一個輸入映射成一個值,這個值便是數組上的下標。
現在輸入一個值s1,用k個哈希函數分別進行運算,得到k個不同的值(r1,r2,r3...),將數組上下標為(r1,r2,r3...)的值置為1。
我們現在輸入baidu,經過3個哈希函數運算后,得到1,4,7,則將數組上第1,4,7的位置上的元素置為1。
好的,我們現在繼續輸入一個值,例如輸入tencent。同理,將3,4,8上的元素置為1。(此時第4個位置形成了覆蓋)
現在,我們要測試test與yang這兩個字符串,是否在(baidu,tencent)這個集合中。
我們輸入test,經過三個哈希函數運算后,得到1,3,8。這個時候可以發現,這個數組上的第1,3,8位置已經被置為1了,那我們可以說,test值可能在(baidu,tencent)這個集合中。
最后,我們輸入yang。此時,經過運算后,得到1,2,3。我們可以發現,第2個位置上,元素依然是0。那么,我們可以斬釘截鐵得說,yang必然不在這個集合中。
缺陷
當然,如果我們增大集合,繼續往(baidu,tencent)中增加更多的字符串,到最后,數組上的所有位置可能都為1。這個時候,不管我們查找任意一個字符串,得出的結果都是該字符串可能存在于集合中,這樣的結果沒有任何意義。
因此,在實際運用中,數組的長度將會很大,哈希函數也會很多,從而提高匹配的準確性,但絕不是100%的精度,存在誤判。
講到這里,你也許對布隆過濾器有了新認識。原來布隆過濾器可以告訴我們一個字符串可能存在于一個集合中,也可以告訴我們一個字符串絕對不存在于一個集合中。注意這里的可能和絕對!
布隆過濾器說的存在,那是可能存在。但是說的不存在,那就是一定不存在。他是存在一定誤判率的。
適用場景
既然布隆過濾器存在誤判率,那我們為啥要用它?
不同的數據結構有不同的適用場景和優缺點,我們需要仔細權衡自己的需求之后妥善適用它們,布隆過濾器就是踐行這句話的代表。
事實上,布隆過濾器被廣泛用于網頁黑名單系統、垃圾郵件過濾系統、爬蟲的網址判重系統以及解決緩存穿透問題。通過介紹已經知曉布隆過濾器的作用是檢索一個元素是否在集合中。
可能有人認為這個功能非常簡單,直接放在redis中或者數據庫中查詢就好了。
又或者當數據量較小,內存又足夠大時,使用hashMap或者hashSet等結構就好了。
但是如果當這些數據量很大,數十億甚至更多,內存裝不下且數據庫檢索又極慢的情況,我們應該如何去處理?這個時候我們不妨考慮下布隆過濾器,因為它是一個空間效率占用極少和查詢時間極快的算法,但是需要業務可以忍受一個判斷失誤率。
支持新增,那支持刪除嗎
現在我們知道,布隆過濾器可以支持 add 和 isExist 操作,那么 delete 操作可以么?
答案是不可以,例如在上面的圖例中,第4個元素上,?被baidu和tencent共同覆蓋。
一旦你刪除其中一個值例如 tencent?,而將第4個元素置為?0,那么下次判斷另一個值例如 baidu”是否存在的話,會直接返回 false,和預期結果不符。
如何解決這個問題,答案是計數刪除。但是計數刪除需要存儲統計數,而不是簡單的全部置為1,需要進行自增操作。這樣的話,增加一個值就是將對應索引槽上存儲的值加一,刪除則是減一,判斷是否存在則是看值是否大于0。
在計數刪除下,即使我們刪除tencent,并將第4個元素減1,此時第四個元素還是1,依然可以判斷出baidu可能存在于集合中。
避免緩存穿透
什么是緩存穿透?
我們常在應用層和數據層中間加上緩存層,用于提高查詢效率,保證服務可靠性等。
但是,如果此時有大量請求瞬間涌進來,查詢一個自增id為負數的數據,那顯然,緩存無法命中,直接打到數據庫,數據庫此時可能就掛掉了。
如果,我們在緩存上加一個布隆過濾器。數據庫首先將數據復制一份進緩存中,將所有id通過布隆過濾器映射進一個大數組中。那么,在布隆過濾器的作用下,大量無效的請求直接被返回,無法穿透緩存。
當我們使用布隆過濾器,確定這個值必定不存在時,則完全不需要執行后續成本較高的查詢操作。
?
總結
- 上一篇: 计算机无法装会声会影,会声会影“应用程序
- 下一篇: 百度网盘下载文件损坏 定位和解决方案