1. 概述
緩存穿透和緩存雪崩是在實際項目中,經常能遇到的問題。
今天我們就簡單聊聊緩存穿透和緩存雪崩的這兩個話題。
2.緩存穿透
2.1 什么是緩存穿透?
簡單說就是用戶發起請求時,始終匹配不到緩存中的數據,每次都直接通過關系型數據庫進行查詢,並得到數據。
如果這個請求的並發量非常的大,非常多的用戶在同一時刻去執行這個請求,那么會超出關系型數據庫的負載,從而導致數據庫的宕機。
2.2 解決方案一:優化代碼邏輯
其中一個解決方案,就是編寫代碼時,邏輯要嚴謹,反復自測,保證任何條件的查詢,都一定是先經過緩存進行查詢。
如果緩存中沒有查到數據,則進行一次關系型數據庫的查詢,然后將查詢結果存儲到緩存中(即使查詢結果是空值,也將空值存儲到緩存中),保證下一次查詢可以從緩存中獲取。
當然,由於數據庫中的數據會隨時變化,緩存是需要有過期時間的。
2.3 解決方案二:布隆過濾器
2.3.1 布隆過濾器簡介
布隆過濾器能夠很快的判斷某個元素是否已存在集合中。
布隆過濾器占用內存小,讀寫非常快。
布隆過濾器適合於緩存中存在過某key才去查詢緩存,沒存在過,就不去查詢直接返回空的場景。
布隆過濾器通常放在緩存前面執行,可以將緩存的key放進布隆過濾器中,讀取數據時,如果布隆過濾器判斷緩存的key存在,才會到緩存中去查詢,不存在就直接返回空,大大降低了緩存穿透的可能。
布隆過濾器要注意的兩個問題:
1)有一定的誤判率:由於布隆過濾器本身的機制,是會有一定的誤判率,也就是說這個key值其實在緩存中不存在,但布隆過濾器會返回其存在。
2)無法刪除:緩存的數據是會被刪除的,但布隆過濾器由於本身機制的限制,是不能執行刪除操作的。
由於以上兩個問題,會導致程序會在key值不存在的情況下去訪問緩存,也就是說會有多訪問緩存的情況,這個其實是沒有什么影響的,在緩存上再加一層判斷就可以了。
2.3.2 布隆過濾器的使用
1)添加依賴
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>30.1.1-jre</version> </dependency>
2)代碼示例
// 第一個參數是字符集 // 第二個參數是容器的長度,值越大,誤判率越低 // 第三個參數是誤判率,用於指定誤判率,值越小誤判率越低 BloomFilter<String> bloomFilter = BloomFilter.create( Funnels.stringFunnel(StandardCharsets.UTF_8), 100000, 0.001); // 放入元素,可以是緩存的key bloomFilter.put("user:1"); bloomFilter.put("user:2"); // 判斷元素是否存在 boolean isExist = bloomFilter.mightContain("user:3");
3.緩存雪崩
3.1 什么是緩存雪崩?
由於數據庫中的數據是隨時變化的,因此緩存中的key通常都是有過期時間的。
在某一時刻,緩存中的大面積的key都失效了,恰好此時,很多請求並發到來,直接訪問了關系型數據庫,從而導致數據庫宕機,這就是緩存雪崩。
3.2 如何預防緩存雪崩
1)緩存永不過期
緩存不設置過期時間,而是使用程序手動讓其過期。
2)錯開緩存的過期時間
在設置緩存過期時間時,加一個固定范圍的隨機數,從而錯開緩存過期時間。
4.綜述
今天簡單聊了一下緩存穿透和緩存雪崩,希望能對大家的工作有所幫助。
歡迎大家多多評論交流,共同成長。
關注追風人聊Java,每天更新Java干貨。