Redis如何实现刷抖音不重复-布隆过滤器(Bloom Filter)
刷抖音的時候是否曾想過,我們刷過的視頻很難在重復刷到那么它到底是如何實現的呢?
如果說我們每刷一個視頻并且把視頻id和用戶的id組合成一條數據保存到數據庫中每次推薦視頻的時候都去數據檢測是否已經刷過了,嗯,這樣可以實現這個功能,但是存在多個問題,頻繁操作數據表對數據庫造成很大的負擔,每次推薦視頻時都得保存數據,人流量一多,數據庫很快就扛不住。
那么我們可否使用緩存redis中的set來實現呢?當然是可以的,redis4.0版本給我們提供了更加快捷更加節省空間的數據結構--布隆過濾器(Bloom Filter)
布隆過濾器的簡介
布隆過濾器(BloomFilter)是一個很長的二進制向量和一系列隨機映射函數,我們也可以簡單的理解為它是一個不怎么精確的set結構。本質上布隆過濾器是一種數據結構,比較巧妙的概率型數據結構(probabilistic data structure),特點是高效地插入和查詢,可以用來告訴你 “某樣東西一定不存在或者可能存在”。相比于傳統的List、Set、Map等數據結構,它更高效、占用空間更少,但是缺點是其返回的結果是概率性的,但是我們不必過于擔心它不夠精確,只要參數設置合理,它的精度可以控制到足夠的精確。
適用場景:
-
大數據是否存在的問題,比如上述的刷抖音去重問題
-
解決緩存擊穿問題,如果數據請求一直是一個不存在的內容,那么它會越過緩存直接請求數據庫,造成緩存擊穿,布隆過濾器也可以解決此類問題
-
解決爬蟲爬到重復url內容等等
布隆過濾器基本使用
布隆過濾器有二個基本指令,bf.add添加元素,bf.exists查詢元素是否存在,它的用法和set集合的sadd和sismember差不多。注意bf.add只能一次添加一個元素,如果想要一次添加多個,就需要用到bf.madd指令。同樣如果需要一次查詢多個元素是否存在,就需要用到bf.mexists指令。
> bf.add user user1(integer) 1> bf.add user user2(integer) 1> bf.add user user3(integer) 1> bf.exists user user1(integer)?1>?bf.exists?user user4(integer) 0> bf.madd user user4 user5 user61) (integer) 12) (integer) 13) (integer) 1> bf.mexists user user4 user5 user6 user71) (integer) 12) (integer) 13) (integer) 14) (integer) 0上面使用的布隆過過濾器只是默認參數的布隆過濾器,它在我們第一次add的時候自動創建。Redis也提供了可以自定義參數的布隆過濾器,只需要在add之前使用bf.reserve指令顯式創建就好了。如果對應的key已經存在,bf.reserve會報錯。bf.reserve有三個參數,分別是key、error_rate(錯誤率)和initial_size:
error_rate越低,需要的空間越大
initial_size表示預計放入的元素數量,當實際數量超過這個值時,誤判率就會提升,所以需要提前設置一個較大的數值避免超出導致誤判率升高;
如果不適用bf.reserve,默認的error_rate是0.01,默認的initial_size是100。
布隆過濾器的實現原理
add操作
每個布隆過濾器對應到Redis的數據結構里面就是一個大型的位數組和幾個不一樣的無偏 hash 函數。所謂無偏就是能夠把元素的 hash 值算得比較均勻。向布隆過濾器中添加key時,會使用多個hash函數對key進行hash算得一個整數索引值然后對位數組長度進行取模運算得到一個位置,每個hash函數都會算得一個不同的位置。再把位數組的這幾個位置都置為1就完成了add操作。
exists操作
exists操作跟add一樣,也會把hash的幾個位置都算出來,看看位數組中這幾個位置是否都是1,只要有一個位為0,那么說明布隆過濾器中這個key不存在。如果都是1,這并不能說明這個key就一定存在,只是極有可能存在,因為這些位被置為1可能是因為其它的key存在所致。
如果這個位數組比較稀疏,這個概率就會很大,如果這個位數組比較擁擠,這個概率就會降低。使用時不要讓實際元素遠大于初始化大小,當實際元素開始超出初始化大小時,應該對布隆過濾器進行重建,重新分配一個size更大的過濾器,再將所有的歷史元素批量add進去 (這就要求我們在其它的存儲器中記錄所有的歷史元素)。因為error_rate不會因為數量超出就急劇增加,這就給我們重建過濾器提供了較為寬松的時間。
空間占用估算
計算公式:k=0.7*(l/n)??????????#約等于f=0.6185^(l/n) #^表示次方計算,也就是 math.pow布隆過濾器有兩個參數,第一個是預計元素的數量n,第二個是錯誤率f。公式根據這兩個輸入得到兩個輸出,第一個輸出是位數組的長度l,也就是需要的存儲空間大小(bit),第二個輸出是hash函數的最佳數量k。hash函數的數量也會直接影響到錯誤率,最佳的數量會有最低的錯誤率。
從公式中可以看出
-
1.位數組相對越長(l/n),錯誤率f越低,這個和直觀上理解是一致的
-
位數組相對越長(l/n),hash函數需要的最佳數量也越多,影響計算效率
-
當一個元素平均需要1個字節(8bit)的指紋空間時(l/n=8),錯誤率大約為 2%
| 錯誤率 | 一個元素所需平均空間 | 空間數 |
| 10% | 4.792個bit | 5bit |
| 1% | 9.585個bit | 10bit |
| 0.1% | 14.377個bit | 15bit |
有人會問如果實際的元素超過了預算元素,錯誤率會如何變化,會不會錯誤率非常高?我們引入一個公式
f=(1-0.5^t)^k#極限近似,?k?是?hash?函數的最佳數量#t表示實際元素和預計元素的倍數錯誤率為 10% 時,倍數比為 2 時,錯誤率就會升至接近 40%
錯誤率為 1% 時,倍數比為 2 時,錯誤率升至 15%
錯誤率為 0.1%,倍數比為 2 時,錯誤率升至 5%
?
?
?
一名正在搶救的coder
筆名:mangolove
CSDN地址:https://blog.csdn.net/mango_love
GitHub地址:https://github.com/mangoloveYu
?
總結
以上是生活随笔為你收集整理的Redis如何实现刷抖音不重复-布隆过滤器(Bloom Filter)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OpenFiler 配置iscsi共享式
- 下一篇: 小记6月19