redis快取穿透穿透解決方案-布隆過濾器
redis快取穿透穿透解決方案-布隆過濾器
我們先來看一段程式碼
cache_key = "id:1" cache_value = GetValueFromRedis(cache_key); //判斷快取是否有資料 if cache_value != nil{ //如果有 直接返回資料 return cache_value } db_value = GetValueFromDb(cache_key) // 從資料庫中查詢資料 if db_value == nil{ return db_value } expire_time = 300 SetRedisValue(cache_key, db_value, expire_time) //將資料庫的結果更新到快取中,並直接返回結果 return db_value
相信絕大多數同學都是這麼處理請求的,這樣用redis能夠給mysql抵擋住大部分的請求。其實這樣是存在一定的問題的
問題1
我在請求的時候,用id=-1來請求
id=-1這條記錄在資料庫中是不存在的,當然對應的redis中也是沒有的。那麼就需要去請求資料庫然後把資料寫入到redis中,這樣就會造成沒有必要的資料庫請求,一兩個請求無所謂,但是如果從-∞到-1 無限的高頻率的請求,就會給線上造成很大的壓力。
針對問題1的解決方案
我們可以通過程式來限制id的合法性,判斷id<1的情況都直接在介面層面攔截,這個方式的確可以解決上面說的那種情況,但是咱們接下來往下看
問題2
比如現在資料庫id的最大值為1000,我們用比1000大的數字去請求
這種情況原理和問題1是一樣的,這次我們就沒法通過引數判斷來攔截住請求了,所以我們就得用接下來一種經典的方式,布隆過濾器
布隆過濾器其實就是一種比較巧妙的概率型資料結構,它可以告訴你某種東西一定不存在或者可能存在。從而達到對髒資料過濾的效果。他存在的位置如圖
其實對布隆過濾器比較陌生的同學可以先想想,作為一個過濾器需要滿足什麼條件?
- 速度得快,得從記憶體查,如果從硬碟查的話還不如直接查資料庫
- 因為過濾器裡面得存入資料庫所有的資料,所以記憶體勢必是比較緊張的,所以記憶體要做到絕對的節省,說到節省記憶體,大家應該很容易能想到 redis裡面的setbit操作
布隆過濾器的實現
寫入過程
- 通過bit陣列來標識資料
- 比如id=10的資料,通過hash演算法算出來結果為1
- 把bit陣列下表為1的位置的值標記為1
查詢過程
- 將id=10做hash運算,得到結果1
- 看bit陣列下表為1的資料標識為1,則說明資料存在
其實我們看上面的演算法是存在一定的問題的
1:只要是hash運算,就會存在hash碰撞問題,比如id=10 和id=100可能經過hash運算之後結果都為1,那麼id=10寫入之後查詢id=100是否存在會誤判為id=100也存在
2:當bit陣列滿了之後,查詢的錯誤率肯定是百分之百,因為每個資料都存在
這些其實都是導致錯誤率的原因,錯誤率是不可能避免的,但是咱們可以減少錯誤率,減少錯誤率的方法有兩個
1:加大bit陣列的長度,對於bit陣列的長度的增加是不用擔心的,因為是bit操作,所以可以加到很大的值
2:增加hash函式的個數,hash函式的個數增加了,說明標識一個數組需要的位置就會變多。這樣會降低發生hash碰撞的概率。但是hash的函式也不是越多越好,需要參照陣列的長度來定
hash錯誤率:
布隆演算法說資料存在,那麼實際有可能不存在
如果資料不存在。那麼一定不存在
布隆過濾器redis中的使用方法
1.下載redisbloom外掛(redis官網下載即可)
wget https://github.com/RedisLabsModules/rebloom/archive/v1.1.1.tar.gz
2:解壓並安裝,生成.so檔案
[root@redis]# tar -zxvf v1.1.1.tar.gz
[root@redis]# cd Redisbloom-1.1.1/
[[email protected]]# make
[[email protected]]# ls
contrib Dockerfile docs LICENSE Makefile mkdocs.yml ramp.yml README.md rebloom.so src tests
3:在redis配置檔案(redis.conf)中加入該模組即可
[root@redis]# vim redis.conf
#####################MODULES################## Load modules at startup. If the server is not able to load modules
# it will abort. It is possible to use multiple loadmodule directives.
loadmodule /usr/local/redis/redisbloom-1.1.1/rebloom.so
4:重新啟動redis
redis-server ./redis.conf
5:測試安裝是否成功
127.0.0.1:6379> bf.add users user2 //寫入資料user2
(integer) 1
127.0.0.1:6379> bf.add users user1 //寫入資料user1
(integer) 1
127.0.0.1:6379> bf.exists users user1 //查詢user1存在
(integer) 1
127.0.0.1:6379> bf.exists users user3 //查詢user3不存在
(integer) 0
上面說過布隆過濾器存在誤判的情況,在 redis 中有兩個值決定布隆過濾器的準確率:
- error_rate :允許布隆過濾器的錯誤率,這個值越低過濾器的位陣列的大小越大,佔用空間也就越大。
- initial_size :布隆過濾器可以儲存的元素個數,當實際儲存的元素個數超過這個值之後,過濾器的準確率會下降。
redis 中有一個命令可以來設定這兩個值:
bf.reserve users 0.01 100
三個引數的含義:
第一個值是過濾器的名字。
第二個值為 error_rate 的值。
第三個值為 initial_size 的值。
關注我的技術公眾號,每週都有優質技術文章推送。
微信掃一掃下方二維碼即可關注: