首頁技術(shù)文章正文

緩存穿透及解決方案

更新時間:2020-07-14 來源:黑馬程序員 瀏覽量:

緩存只是為了緩解數(shù)據(jù)庫壓力而添加的一層保護層,當(dāng)從緩存中查詢不到我們需要的數(shù)據(jù)就要去數(shù)據(jù)庫中查詢了。如果被黑客利用,頻繁去訪問緩存中沒有的數(shù)據(jù),那么緩存就失去了存在的意義,瞬間所有請求的壓力都落在了數(shù)據(jù)庫上,這樣會導(dǎo)致數(shù)據(jù)庫連接異常。

針對緩存穿透的常見解決方案有以下兩種:

方案1: 對于數(shù)據(jù)庫中不存在的數(shù)據(jù), 也對其在緩存中設(shè)置默認值Null,為避免占用資源,一般過期時間會比較短;

方案2: 可以設(shè)置一些過濾規(guī)則, 如布隆過濾器

方案1:相對簡單, 但是也容易破解, 比如 攻擊者通過分析數(shù)據(jù)格式, 不重復(fù)的請求數(shù)據(jù)庫不存在數(shù)據(jù), 那這樣方案1就等于失效的. 相對而言, 方案2則更加穩(wěn)定, 接下來就主要講解一下方案2的實現(xiàn)方式。

1594722461371_緩存穿透01.jpg


方案2的設(shè)計思路是通過設(shè)置過濾規(guī)則, 在數(shù)據(jù)庫查詢之前將數(shù)據(jù)進行過濾, 如果發(fā)現(xiàn)數(shù)據(jù)不存在, 則不再進行數(shù)據(jù)庫查詢, 以此來減小數(shù)據(jù)庫的訪問壓力。

方案2中過濾規(guī)則目前主流的一種的載體就是布隆過濾器. 布隆過濾器是一種概率型數(shù)據(jù)結(jié)構(gòu),特點是高效地插入和查詢,可以用來告訴你 “某樣?xùn)|西一定不存在或者可能存在”.

相比于傳統(tǒng)的 List、Set、Map 等數(shù)據(jù)結(jié)構(gòu),布隆過濾器是一個bit數(shù)組, 它更高效、占用空間更少,但是缺點是其返回的結(jié)果是概率性的,而不是確切的。

1594722485580_緩存穿透02.jpg


如果我們要映射一個值到布隆過濾器中,我們需要使用多個不同的哈希函數(shù)生成多個哈希值,并對每個生成的哈希值指向的 bit 位置 1,例如針對值 “zhangsan” 和三個不同的哈希函數(shù)分別生成了哈希值 1、4、7

1594722507245_緩存穿透03.jpg


我們現(xiàn)在再存一個值 “l(fā)isi”,如果哈希函數(shù)返回 4、5、8 的話,圖繼續(xù)變?yōu)椋?br/>

1594722544253_緩存穿透04.jpg


當(dāng)我們想要判斷布隆過濾器是否記錄了某個數(shù)據(jù)時,布隆過濾器會先對該數(shù)據(jù)進行同樣的哈希處理, 比如 “wangwu”的哈希函數(shù)返回了 2、5、8三個值,結(jié)果我們發(fā)現(xiàn) 2 這個 bit 位上的值為 0,說明沒有任何一個值映射到這個 bit 位上,因此我們可以很確定地說 “wangwu” 這個數(shù)據(jù)不存在。

但是同時我們會發(fā)現(xiàn),4 這個 bit 位由于”zhangsan”和”lisi”的哈希函數(shù)都返回了這個 bit 位,因此它被覆蓋了。那么隨著布隆過濾器保存的數(shù)據(jù)不斷增多, 重復(fù)的概率就會不斷增大, 所以當(dāng)我們過濾某個數(shù)據(jù)時, 如果發(fā)現(xiàn)其三個哈希值都在過濾器中進行了記錄, 那么也只能說明過濾器中可能包含了該數(shù)據(jù), 并不能絕對肯定, 因為可能是其他數(shù)據(jù)的哈希值對結(jié)果產(chǎn)生了影響.這也就解釋了上文所說的 布隆過濾器只能說明“某樣?xùn)|西一定不存在或者可能存在”.至于為什么采用三種不同的哈希函數(shù)取值, 因為三個哈希值只要有一個不存在就說明數(shù)據(jù)一定不在過濾器中, 這樣做是可以減小因哈希碰撞(兩個數(shù)據(jù)的哈希值相同)產(chǎn)生的錯誤概率.

布隆過濾器在很多語言中都有封裝的工具包, 下邊以python的工具包pybloomfiltermmap3 為例演示一下代碼:

import pybloomfilter

 

# 創(chuàng)建過濾器(數(shù)據(jù)容量, 錯誤率, 存儲文件)   錯誤率越低, 文件越大

filter = pybloomfilter.BloomFilter(1000000, 0.01, 'words.bloom')

 

# 添加數(shù)據(jù)

filter.update(('bj', 'sh', 'gz'))

 

# 判斷是否包含

if 'bj' in filter:

    print('包含')

else:

     print('不包含')


猜你喜歡:

python培訓(xùn)課程

分享到:
在線咨詢 我要報名
和我們在線交談!