開發一個電商項目,由於數據量一直在增長(已達億級),因此須要重構以前開發好的秒殺功能,爲了更好的支持高併發,在驗證用戶是否重複購買的環節,就考慮用布隆過濾器。redis
也順便更加深刻的去了解下布隆過濾器的原理,感受仍是蠻有意思的,這一連串的公式不靜下心來思考,很容易被繞暈。算法
##<font color=#FFD700> 1、概述</font>數據庫
####一、什麼是布隆過濾器網頁爬蟲
本質上布隆過濾器是一種數據結構,比較巧妙的機率型數據結構
,特色是高效地插入和查詢
。根據查詢結果能夠用來告訴你 某樣東西必定不存在或者可能存在
這句話是該算法的核心。segmentfault
相比於傳統的 List、Set、Map 等數據結構,它更高效、佔用空間更少,可是缺點是其返回的結果是機率性的,而不是確切的,同時布隆過濾器還有一個缺陷就是數組
數據只能插入不能刪除
。緩存
####二、數據如何存入布隆過濾器服務器
布隆過濾器是由一個很長的bit數組和一系列哈希函數組成的
。數據結構
數組的每一個元素都只佔1bit空間,而且每一個元素只能爲0或1。併發
布隆過濾器還擁有k個哈希函數,當一個元素加入布隆過濾器時,會使用k個哈希函數對其進行k次計算,獲得k個哈希值,而且根據獲得的哈希值,在維數組中把對應下標的值置位1。
判斷某個數是否在布隆過濾器中,就對該元素進行k次哈希計算,獲得的值在位數組中判斷每一個元素是否都爲1,若是每一個元素都爲1,就說明這個值在布隆過濾器中。
####三、布隆過濾器爲何會有誤判
當插入的元素愈來愈多時,當一個不在布隆過濾器中的元素,通過一樣規則的哈希計算以後,獲得的值在位數組中查詢,有可能這些位置由於其餘的元素先被置1了。
因此布隆過濾器存在誤判的狀況,可是若是布隆過濾器判斷某個元素不在布隆過濾器中,那麼這個值就必定不在。
若是對布隆過濾器的概念還不是很理解的話,推薦一篇博客,圖文並茂好理解不少。詳解布隆過濾器的原理、使用場景和注意事項
####四、使用場景
<br>
##<font color=#FFD700>2、實際應用場景</font>
背景
如今有個100億個黑名單網頁數據,每一個網頁的URL佔用64字節。如今想要實現一種網頁過濾系統,能夠根據網頁的URL判斷該網站是否在黑名單上,請設計該系統。
需求
能夠容許有0.01%如下的判斷失誤率,而且使用的總空間不要超過200G。
這裏一共有4個常量:
100億條黑名單數據
,每條數據佔64個字節
,萬分之一的失誤率
,總空間不要超過200G
。
若是不考慮不攏過濾器,那麼這裏存儲100億條數據就須要 100億 * 64字節 = 596G 顯然超過300G
解題
在知足有 100億條數據 而且容許 萬分之一的失誤率 的布隆過濾器須要多大的bit數組呢?
布隆過濾器的大小m公式
<img src="https://img2018.cnblogs.com/blog/1090617/201907/1090617-20190724110212412-507904854.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="700" height="140">
求得 m = 19.19n,向上取整爲 20n。因此2000億bit,約爲186G。
算完m,咱們順便來算下m,n已知,這時知足最小偏差的k是幾個。
哈希函數的個數k公式
<img src="https://img2018.cnblogs.com/blog/1090617/201907/1090617-20190724110226165-1630835298.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="700" height="125">
求得 k = 14,即須要14個哈希函數。
經過經過 m = 20n, k = 14咱們再來算下真實的失誤率。
布隆過濾器真實失誤率p公式
<img src="https://img2018.cnblogs.com/blog/1090617/201907/1090617-20190724122111713-66384509.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="700" height="116">
求得 p = 0.006%,即布隆過濾器的真實失誤率爲0.006%。
經過布隆過濾器公式也能夠看出:
單個數據的大小不影響布隆過濾器大小,由於樣本會經過哈希函數獲得輸出值
。
就比如上面的 每一個網頁的URL佔用64字節 這個數據大小 跟布隆過濾器大小沒啥關係。
這三個公式就是有關布隆過濾器已經推倒出的公式,下面咱們來推下這個公式是如何推導出來的。
<br>
##<font color=#FFD700>3、公式推導 </font>
講公式,應該先知道幾個關鍵的常量。
誤判率p
、布隆過濾器長度m
、元素個數n
、哈希函數個數k
咱們再來一步一步由簡單到難推導公式。
前提條件
:就是假設每一個元素哈希獲得的值分佈到m數組上的每個數組節點的機率是相等的。
1) 假設布隆過濾器長度爲m,元素個數n爲1,哈希函數個數k也爲1。那麼在插入時某一數組節點沒有被置爲1的機率。
<img src="https://img2018.cnblogs.com/blog/1090617/201907/1090617-20190724122142722-1027187439.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="700" height="76">
這個應該很好理解。
2)若是上面其它不變,而哈希函數個數變成k個,那麼在插入時某一數組節點沒有被置爲1的機率。
<img src="https://img2018.cnblogs.com/blog/1090617/201907/1090617-20190724122216316-167739319.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="700" height="73">
好理解!
3)若是元素個數變成n個,而哈希函數個數變成k個,那麼在插入時某一數組節點沒有被置爲1的機率。
<img src="https://img2018.cnblogs.com/blog/1090617/201907/1090617-20190724122236217-1599705827.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="700" height="88">
4)從上面推導出的是: 當布隆過濾器長度爲m,元素個數變成n個,哈希函數個數變成k個的時候,某一節點被置爲1的機率爲
<img src="https://img2018.cnblogs.com/blog/1090617/201907/1090617-20190724122252469-1917497462.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="700" height="87">
到這裏應該也好理解,第三步是該位置從未被置爲1,那麼1去減去它就是至少有一次被置爲1,那麼只要存在一次被置1,那麼該位置的bit標示就是1,由於布隆過濾器是不能刪除的。
5)這個還須要考慮到,一個元素經過hash會生成多個k,放入m數組中,因此須要這k個值都爲1纔會認爲該該元素已經存在。因此是這樣的。
<img src="https://img2018.cnblogs.com/blog/1090617/201907/1090617-20190724122302318-1240426802.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="700" height="99">
上面這個公式推導在轉換下就成了
<img src="https://img2018.cnblogs.com/blog/1090617/201907/1090617-20190724122313210-1596776636.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="700" height="111">
思考
爲何上面這個公式的值就是最終的偏差率?
由於當一個布隆過濾器中不存在的元素進來的是的時候,首先經過hash算法產生k個哈希值,分佈在m數組上都爲1的的機率不就是上面推導出的這個公式嗎,那不就是偏差嗎?
由於明明是不存在的值,卻有這個機率代表已經存在。
思考
給定的m和n,思考k值爲多少偏差會最小。
爲何k值的大小不合理會影響偏差呢?
咱們來思考下,一個元素最終生成k個hash值,那麼會在數組m上的k個位置標記爲1。
假設k爲1,那麼每次進來只在m上的某一個位置標記爲1,這樣的話若是一個新元素進來恰好hash值也在這裏,而不用其它位置來判斷是否爲1,這個偏差就會比較大。
假設k爲m,那麼第一個元素進來,在m上全部位置上都表爲1了 ,之後只要進來一個元素就會標記爲已存在。這個偏差也太大了。
上面只是舉了兩個極端的例子,但也說明k值太大、過小都很差,它的最優值必定跟m、n存在某種關係。
至於完整公式的推導,我這裏就不在寫了,後面會貼一我的家怎麼推導的博客。
它們之間的關係只要記住下面這個公式就能夠了。
<img src="https://img2018.cnblogs.com/blog/1090617/201907/1090617-20190724122323843-1991371748.jpg" style="border: 1px dashed rgb(204, 200, 200);" width="700" height="125">
這篇博客就到這裏了,後面會整理經過谷歌的guava工具 和 redis 實現布隆過濾器的示例。
<br>
###<font color=#FFD700> 參考</font>
<br> <br> ``` 只要本身變優秀了,其餘的事情纔會跟着好起來(上將9) ```