JAVA面試題之緩存擊穿、緩存穿透、緩存雪崩的三者區別

前端發起一個請求,經歷過三次握手後連接到服務器,想要獲取相應的數據,那麼服務器接入瞭緩存中間件後,從接收到Request到最後的Response,到底是怎樣的一個流程呢?以下探討忽略掉參數校驗等邏輯,直接講最核心的鏈路。

調用鏈路 

一個請求Request過來,服務器首先和緩存中間件建立連接,傳輸對應key到緩存中間件中獲取相對應的數據,服務器拿到返回的結果後,判斷返回的結果是否有數據,如果有數據,則返回從緩存中拿到的結果。如果緩存中間件中沒有數據,則建立數據庫連接,訪問數據庫服務器,按照相應邏輯拿到返回結果,判斷結果中是否有數據,如果有則返回對應數據,如果沒有則按照業務場景要求,返回對應結果(一般為null或者new一個空對象)。

緩存擊穿

含義:

​ 什麼是緩存擊穿?通俗的講指的是緩存中沒有數據,但數據庫中有數據的場景。那為什麼緩存中會沒有數據呢?一般是由於設置瞭緩存時間導致緩存過期,所以沒有數據。那緩存找不到數據去數據庫查詢就好瞭呀,為啥又叫擊穿?是因為要查詢這個key對應的數據是一個熱點數據,並發訪問的量大,同時去查詢數據庫,導致數據庫壓力驟增,嚴重會打崩數據庫。

解決方案:

1、如果是不改變的數據,如一些常量值,則可以設置對應熱點key永不過期。

2、加上互斥鎖,防止同一臺服務器同一時間有多個連接訪問數據庫。

// 偽代碼
public class Main {
    // 雙重檢測鎖
    public static String getHotData(String key) {
        // 先從緩存中間件獲取對應熱點key數據
        String response = redis.get(key);
        // 緩存沒有數據
        if(Objects.isNull(response)) {
            // 保證一臺服務器同一時間隻有一個線程訪問
            synchronized (Main.class) {
                // 假設A線程訪問進synchronized裡,線程B, C阻塞在synchronsized外面
                // 線程A退出synchronized後,線程B和C應該從redis中拿而不是再訪問數據庫
                response = redis.get(key);
                // 訪問數據庫 拿到數據後 寫進redis中
                if(Objects.isNull(response)) {
                    response = loadDataFromMySQL(key);
                    redis.set(key, response);
                }
            }
        }
        return response;
    }
}

3、加上分佈式鎖,全局保證隻有一個線程訪問數據庫。

// 偽代碼
public class Main {

    // 分佈式唯一key
    public static String getHotData(String key, int tryTime) throws InterruptedException {
        if(tryTime >= 4) {
            return "";
        }

        // 先從緩存中間件獲取對應熱點key數據
        String response = redis.get(key);
        // 緩存沒有數據
        if(Objects.isNull(response)) {
            // 保證整個服務集群同一時間隻有一個線程訪問
            if (redis.tryLock()) {
                try {
                    // 訪問數據庫 拿到數據後 寫進redis中
                    if(Objects.isNull(response)) {
                        response = loadDataFromMySQL(key);
                        redis.set(key, response);
                    }
                } finally {
                    redis.unlock();
                }
            } else {
                TimeUnit.MILLISECONDS.sleep(100);
                getHotData(key, tryTime + 1);
            }
        }

        return response;
    }
}

緩存穿透

含義:

緩存穿透指的是緩存中間件和數據庫都沒有對應的數據,但是不斷接收到請求獲取該key的數據,導致數據庫壓力過大,甚至崩潰。

解決方案:

1、訪問數據庫也拿不到數據後,可以按照具體業務要求,在緩存層加上一個該key的值,設置一個過期時間,比如10s或者1min等。那為什麼不設不過期呢?第一個是說因為該key可能有對應的業務含義,有可能隻是該時間點還沒有數據,所以不能設置不過期;第二個是說如果真的是惡意訪問,那麼可能過一段時間就沒有類似請求,那麼我們沒有必要一直把該數據留在緩存裡。

2、增加校驗,如果是不符合預期的請求可以直接過濾,比如說緩存中存放瞭用戶信息,對應的緩存key是和id有關系,那麼如果你的id都是大於等於0的,對於小於0的id可以直接做過濾。

@Controller
public class Controller {
	@RequestMapping(value="/test")
	public String printHello(Integer id) {
        if(Objects.isNull(id) || id < 0) {
            return null;
        }
	
        // 處理對應邏輯
	}
}

緩存雪崩

含義:

緩存雪崩指的是在同一個時間點,緩存中的大批量數據過期,並且還都是熱點數據,導致同一時間並發壓力都打到瞭數據庫中,導致數據庫壓力驟增,甚至宕機。有的人就會問瞭,這和緩存擊穿不是一個意思嗎?緩存擊穿指的是並發查詢某條熱點key數據,緩存雪崩指的是大批量。出現場景之一是在某些核心頁面,該頁面的內容都放入瞭緩存,並且都設置瞭同樣的緩存時間。

解決方案:

1、最簡單的就是設置熱點數據不過期,但要結合對應業務場景來看。

2、在給每個熱點key設置過期時間時,加上一個隨機值,使得熱點數據離散開來,不會同一時間大批量過期。

3、使用緩存擊穿場景講到的互斥鎖、分佈式鎖。

以上就是JAVA面試題之緩存擊穿、緩存穿透、緩存雪崩的三者區別的詳細內容,更多關於JAVA 的資料請關註WalkonNet其它相關文章!

推薦閱讀: