兩級快取實現分析之快取設定

qixiaobo發表於2018-01-05

title: 兩級快取實現分析之快取設定 tags:

  • J2Cache
  • 快取
  • 雪崩
  • 快取擊穿
  • 快取失效 categories: 工作日誌 date: 2017-06-25 18:18:54

上篇描述了快取過期的程式碼兩級快取實現分析之快取過期

本次討論一下兩級快取如何獲取快取設定,移除,獲取

正如文章描述快取讀取順序

讀取順序  -> L1 -> L2 -> DB
複製程式碼

先描述一下快取讀取程式碼:

    /**
     * 獲取快取中的資料
     *
     * @param region : Cache Region name
     * @param key    : Cache key
     * @return cache object
     */
    public CacheObject get(String region, Object key) {
        CacheObject obj = new CacheObject();
        obj.setRegion(region);
        obj.setKey(key);
        if (region != null && key != null) {
            obj.setValue(CacheManager.get(LEVEL_1, region, key));
            if (obj.getValue() == null) {
                obj.setValue(CacheManager.get(LEVEL_2, region, key));
                if (obj.getValue() != null) {
                    obj.setLevel(LEVEL_2);
                    CacheManager.set(LEVEL_1, region, key, obj.getValue());
                }
            } else
                obj.setLevel(LEVEL_1);
        }
        return obj;
    }
複製程式碼

首先在localcache中查詢,如果可以獲得快取直接返回,

如果不能則去remotecache獲取如果可以獲得快取 將localcache設定快取後直接返回,

如果不能則返回空(CacheObject中的value為null)===》可以實現更多級快取

這邊隱含一個條件(高一級快取必定擁有比第一級快取更多並且準確的資料)

此處存在一個快取擊穿的問題:

我們在專案中使用快取通常都是先檢查快取中是否存在,如果存在直接返回快取內容,如果不存在就直直接查詢然後再快取查詢結果返回。這個時候如果我們查詢的某一個資料在快取中一直不存在,就會造成每一次請求都查詢DB,這樣快取就失去了意義,在流量大時,可能DB就掛掉了。

無論多級快取都存在此問題,對於此處的解決方案建議對使用快取的返回結果包裝(Optional)可以使用google或者java8的可選物件,如果db查詢或者計算結果為空,顯示的返回Absent物件,可以避免大量無效的key導致的快取擊穿問題

下面介紹快取的設定實現

    public void set(String region, Object key, Object value, Integer expireInSec) {
        if (region != null && key != null) {
            if (value == null)
                evict(region, key);
            else {
                // 分幾種情況
                // Object obj1 = CacheManager.get(LEVEL_1, region, key);
                // Object obj2 = CacheManager.get(LEVEL_2, region, key);
                // 1. L1 和 L2 都沒有
                // 2. L1 有 L2 沒有(這種情況不存在,除非是寫 L2 的時候失敗
                // 3. L1 沒有,L2 有
                // 4. L1 和 L2 都有
                _sendEvictCmd(region, key);// 清除原有的一級快取的內容
                CacheManager.set(LEVEL_1, region, key, value, expireInSec);
                CacheManager.set(LEVEL_2, region, key, value, expireInSec);
            }
        }
        // log.info("write data to cache region="+region+",key="+key+",value="+value);
    }
複製程式碼

正如上文所述,高階快取(L2)必然承載更多低階快取所不存在的資料

首先發起命令清除所有一級快取的對應key(保證其他一級快取的值已被清除,不會出現快取不一致)

分別在各級快取設定對應的key和value

對於db或者其他計算資源來說如果過載會發生較為嚴重的後果,比如無返回,超時甚至當機。

快取雪崩就是可能出現的原因之一。

如果出現某個快取在一段時間後過期了,同時出現高併發獲取該快取將會出現快取雪崩,

首先查詢該快取發現不存在後立刻去向db獲取資料,該處可能將一瞬間的流量完全打到db上

導致db負債過高使得應用超時乃至當機

通常解決方案只能通過getCache時如果key不存在就增加一把鎖,使得其他執行緒必須等待到鎖釋放

當然還有快取失效問題

使用者在初次使用系統時,可能要針對該使用者儲存大量快取,設想場景均為24h,那麼當24h後所有快取過期

同樣可能出現大量流量打到db出現異常。

通常解決方案是在過期時間上加上一定長度的隨機數,使得快取不至於一剎那全部過期導致db流量過大

相關文章