聊聊緩存穿透

在我們的項目中多少都會使用緩存,因爲有些數據我們沒有必要每次都去查詢數據庫,特別是高QPS的系統,每次都去查詢數據庫會影響數據庫性能。

業務系統一般調用流程:

我們一般的做法是先從緩存中查詢,如果緩存中查詢到了則直接返回,如果緩存中沒有,則查詢數據庫,如果從數據庫中查詢到了,則先將數據寫入緩存,然後返回數據給調用方。

public Result<Post> queryPostFromCache(Long id) {
    // POST:id
    final String key = Constant.Cache.POST + Constant.COLON + id;

    // 從緩存中查詢
    Object post = cacheService.get(key);
    if(post != null) {
        return Result.success((Post) post);
    }

    // 從數據庫中查詢
    post = postMapper.queryPostById(id);
    if(post != null) {
        cacheService.set(key, post, Duration.ofHours(1));
    }
    return Result.success((Post) post);
}

正常情況下,我們查詢的數據都是存在的,比如從文章列表頁面跳轉到文章詳情頁查詢文章詳細信息,此時根據文章ID查詢,文章的ID是真實存在的。不正常的情況是如果請求查詢的文章ID是一條根本不存在的數據,也就是說緩存和數據庫中都不會有值,這會導致請求每次都會到數據庫中查詢。這種查詢不存在數據的現象我們稱之爲緩存穿透

緩存穿透是指用戶查詢的數據在數據庫中沒有,那麼在緩存中也不會有,也就是說查詢的是一條根本不存在的記錄。這樣就會導致用戶首先在緩存中找不到 ,則每次都要去數據庫再查詢一遍,然後返回空。這樣就相當於進行了兩次無用的查詢。要是有人利用這種不存在的key頻繁攻擊我們的系統,很可能導致數據庫壓力增大,甚至導致數據庫掛掉。

解決方案

1.將未在數據庫中查詢到值的key也寫入緩存,緩存的值爲空對象,同時設置一個較短的過期時間,以防止後面真的有數據。

2.採用布隆過濾器(Bloom Filter),使用一個足夠大的bitmap,用於存儲數據庫中可能訪問的key。布隆過濾器可以理解爲一個不太精確的set集合,當你使用它的contains方法判斷某個對象是否存在時,它可能會誤判。它的特點是當布隆過濾器判斷某個值存在時,這個值可能不存在(誤判),當它判斷某個值不存在時,那就肯定不存在。這個特點可以用在判斷要查詢的key是否存在,如果判斷不存在時,那就肯定不存在,這個時候就不用再查詢數據庫了,直接返回給調用方值不存在。

使用空對象解決緩存穿透問題的示例代碼


public Result<Post> queryPostFromCache(Long id) {
    // POST:id
    final String key = Constant.Cache.POST + Constant.COLON + id;

    // 從緩存中查詢
    Object post = cacheService.get(key);
    if(post != null) {
        // 判斷緩存中的值是否爲默認對象
        if(post instanceof NullObjectValue) {
            return Result.fail(ResultCode.RESOURCES_NOT_FOUND);
        }
        return Result.success((Post) post);
    }

    // 從數據庫中查詢
    post = postMapper.queryPostById(id);
    if(post != null) {
        cacheService.set(key, post, Duration.ofHours(1));
    }else {
        // 數據庫中沒有對應記錄,給這個key緩存設置一個默認值
        cacheService.set(key, new NullObjectValue(), Duration.ofHours(1));
    }

    return Result.success((Post) post);
}

有關使用布隆過濾器來解決緩存穿透問題的具體實現,我將會在下篇文章中介紹。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章