某一個值是否是已經在 HyperLogLog 結構裏面了,它就無能爲力了,它只提供了 pfadd 和 pfcount 方法,沒有提供 pfcontains 這種方法git
使用場景: github
1. 好比咱們在使用新聞客戶端看新聞時,它會給咱們不停地推薦新的內容,它每次推薦時要去重,去掉那些已經看過的內容。問題來了,新聞客戶端推薦系統如何實現推送去重的?redis
2. 緩存擊穿數組
布隆過濾器(redis 4.0)能夠理解爲一個不怎麼精確的 set 結構,當你使用它的 contains 方法判斷某個對象是否存在時,它可能會誤判。可是布隆過濾器也不是特別不精確,只要參數設置的合理,它的精確度能夠控制的相對足夠精確,只會有小小的誤判機率。緩存
當布隆過濾器說某個值存在時,這個值可能不存在;當它說不存在時,那就確定不存在。打個比方,當它說不認識你時,確定就不認識;當它說見過你時,可能根本就沒見過面,不過由於你的臉跟它認識的人中某臉比較類似 (某些熟臉的係數組合),因此誤判之前見過你。數據結構
原理:函數
每一個布隆過濾器對應到 Redis 的數據結構裏面就是一個大型的位數組和幾個不同的無偏 hash 函數。所謂無偏就是可以把元素的 hash 值算得比較均勻。網站
向布隆過濾器中添加 key 時,會使用多個 hash 函數對 key 進行 hash 算得一個整數索引值而後對位數組長度進行取模運算獲得一個位置,每一個 hash 函數都會算得一個不一樣的位置。再把位數組的這幾個位置都置爲 1 就完成了 add 操做。(多個hash函數是爲了減小碰撞,視爲狀況而定,hash太多,消耗CPU)spa
向布隆過濾器詢問 key 是否存在時,跟 add 同樣,也會把 hash 的幾個位置都算出來,看看位數組中這幾個位置是否都爲 1,只要有一個位爲 0,那麼說明布隆過濾器中這個 key 不存在。若是都是 1,這並不能說明這個 key 就必定存在,只是極有可能存在,由於這些位被置爲 1 多是由於其它的 key 存在所致。若是這個位數組比較稀疏,判斷正確的機率就會很大,若是這個位數組比較擁擠,判斷正確的機率就會下降。具體的機率計算公式比較複雜,感興趣能夠閱讀擴展閱讀,很是燒腦,不建議讀者細看。指針
使用時不要讓實際元素遠大於初始化大小,當實際元素開始超出初始化大小時,應該對布隆過濾器進行重建,從新分配一個 size 更大的過濾器,再將全部的歷史元素批量 add 進去 (這就要求咱們在其它的存儲器中記錄全部的歷史元素)。由於 error_rate 不會由於數量超出就急劇增長,這就給咱們重建過濾器提供了較爲寬鬆的時間。
布隆過濾器的空間佔用有一個簡單的計算公式,可是推導比較繁瑣,這裏就省去推導過程了,直接引出計算公式,感興趣的讀者能夠點擊「擴展閱讀」深刻理解公式的推導過程。
布隆過濾器有兩個參數,第一個是預計元素的數量 n,第二個是錯誤率 f。公式根據這兩個輸入獲得兩個輸出,第一個輸出是位數組的長度 l,也就是須要的存儲空間大小 (bit),第二個輸出是 hash 函數的最佳數量 k。hash 函數的數量也會直接影響到錯誤率,最佳的數量會有最低的錯誤率。
k=0.7*(l/n) # 約等於 f=0.6185^(l/n) # ^ 表示次方計算,也就是 math.pow
從公式中能夠看出
你也許會想,若是一個元素須要佔據 15 個 bit,那相對 set 集合的空間優點是否是就沒有那麼明顯了?這裏須要明確的是,set 中會存儲每一個元素的內容,而布隆過濾器僅僅存儲元素的指紋。元素的內容大小就是字符串的長度,它通常會有多個字節,甚至是幾十個上百個字節,每一個元素自己還須要一個指針被 set 集合來引用,這個指針又會佔去 4 個字節或 8 個字節,取決於系統是 32bit 仍是 64bit。而指紋空間只有接近 2 個字節,因此布隆過濾器的空間優點仍是很是明顯的。
若是讀者以爲公式計算起來太麻煩,也沒有關係,有不少現成的網站已經支持計算空間佔用的功能了,咱們只要把參數輸進去,就能夠直接看到結果,好比 布隆計算器。