緩存穿透與緩存雪崩

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乾貨。