緩存穿透、緩存雪崩、緩存擊穿場景模擬、解決方案

cache-best-practice

 

一、緩存穿透、緩存雪崩、緩存擊穿場景模擬

查詢方法代碼

    /**
     * 緩存穿透、緩存擊穿、緩存雪崩問題
     *
     * @param id
     * @return
     */
    public DataObject getData1(Long id) {
        //從緩存讀取數據
        DataObject result = getDataFromCache(id);
        if (result == null) {
            // 從數據庫查詢數據
            result = getDataFromDB(id);
            if (result != null) {
                // 將查詢到的數據寫入緩存
                setDataToCache(id, result);
            }
        }
        return result;
    }

 

1、緩存穿透

緩存穿透是指查詢一個根本不存在的數據,緩存層和存儲層都不會命中,通常處於容錯的考慮,如果從緩存層查詢不到數據則不寫入緩存層

緩存穿透將導致不存在的數據每次請求都要到存儲層去查詢,失去了緩存保護後端存儲層的意義

緩存穿透模擬:

數據庫中存在ID 1至100 的數據,我們併發查詢數據庫中不存在的記錄ID 101至110

採用JMeter模擬併發請求,打開“場景模擬-緩存穿透.jmx”執行請求

執行併發請求後,查詢日誌可以看出,所有請求每次都走向了數據庫,以下爲日誌片段

......
從【緩存】中獲取數據, id: 110, DataObject: null
從【數據庫】中獲取數據, id: 110, DataObject: null
從【緩存】中獲取數據, id: 106, DataObject: null
從【緩存】中獲取數據, id: 103, DataObject: null
從【數據庫】中獲取數據, id: 103, DataObject: null
從【緩存】中獲取數據, id: 102, DataObject: null
從【數據庫】中獲取數據, id: 102, DataObject: null
從【緩存】中獲取數據, id: 108, DataObject: null
從【數據庫】中獲取數據, id: 108, DataObject: null
從【緩存】中獲取數據, id: 106, DataObject: null
從【數據庫】中獲取數據, id: 106, DataObject: null
從【緩存】中獲取數據, id: 103, DataObject: null
從【數據庫】中獲取數據, id: 103, DataObject: null
從【緩存】中獲取數據, id: 107, DataObject: null
從【數據庫】中獲取數據, id: 107, DataObject: null
從【緩存】中獲取數據, id: 102, DataObject: null
從【數據庫】中獲取數據, id: 102, DataObject: null
從【緩存】中獲取數據, id: 110, DataObject: null
從【數據庫】中獲取數據, id: 110, DataObject: null
從【數據庫】中獲取數據, id: 106, DataObject: null
從【緩存】中獲取數據, id: 107, DataObject: null
從【緩存】中獲取數據, id: 109, DataObject: null
從【數據庫】中獲取數據, id: 109, DataObject: null
從【數據庫】中獲取數據, id: 107, DataObject: null
從【緩存】中獲取數據, id: 110, DataObject: null
從【數據庫】中獲取數據, id: 110, DataObject: null
......

 

2、緩存雪崩

在普通的緩存系統中一般例如redis、memcache等中,我們會給緩存設置一個失效時間,但是如果所有的緩存的失效時間相同,那麼在同一時間失效時,所有系統的請求都會發送到數據庫層,db可能無法承受如此大的壓力導致系統崩潰。

 

緩存雪崩模擬:

  • 1、同時設置ID 1至100 的緩存
  • 2、查詢1~100的數據,命中緩存
  • 3、手動刪除ID 1至10 的緩存,模擬緩存失效
  • 4、100併發請求ID 1至10 的數據30次

採用JMeter模擬併發請求,打開“場景模擬-緩存雪崩.jmx”執行請求

執行第4步後,查詢日誌可以看出大量請求到達數據庫,並且同一個ID執行了多次數據庫查詢,日誌片段如下

......
從【緩存】中獲取數據, id: 1, DataObject: null
從【緩存】中獲取數據, id: 5, DataObject: null
從【緩存】中獲取數據, id: 4, DataObject: null
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 6, DataObject: null
從【緩存】中獲取數據, id: 3, DataObject: null
從【緩存】中獲取數據, id: 7, DataObject: null
從【緩存】中獲取數據, id: 8, DataObject: null
從【緩存】中獲取數據, id: 9, DataObject: null
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 1, DataObject: null
從【緩存】中獲取數據, id: 4, DataObject: null
從【緩存】中獲取數據, id: 1, DataObject: null
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 6, DataObject: null
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 7, DataObject: null
從【緩存】中獲取數據, id: 6, DataObject: null
從【緩存】中獲取數據, id: 7, DataObject: null
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 9, DataObject: null
從【緩存】中獲取數據, id: 4, DataObject: null
從【緩存】中獲取數據, id: 4, DataObject: null
從【緩存】中獲取數據, id: 5, DataObject: null
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 8, DataObject: null
從【緩存】中獲取數據, id: 4, DataObject: null
從【緩存】中獲取數據, id: 8, DataObject: null
從【緩存】中獲取數據, id: 8, DataObject: null
從【緩存】中獲取數據, id: 5, DataObject: null
從【緩存】中獲取數據, id: 5, DataObject: null
從【緩存】中獲取數據, id: 3, DataObject: null
從【緩存】中獲取數據, id: 8, DataObject: null
從【數據庫】中獲取數據, id: 1, DataObject: DataObject(id=1, desc=name:1)
設置數據到【緩存】, DataObject: DataObject(id=1, desc=name:1)
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 3, DataObject: null
從【數據庫】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 5, DataObject: null
從【數據庫】中獲取數據, id: 5, DataObject: DataObject(id=5, desc=name:5)
設置數據到【緩存】, DataObject: DataObject(id=5, desc=name:5)
設置數據到【緩存】, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 7, DataObject: null
從【數據庫】中獲取數據, id: 7, DataObject: DataObject(id=7, desc=name:7)
設置數據到【緩存】, DataObject: DataObject(id=7, desc=name:7)
從【緩存】中獲取數據, id: 10, DataObject: null
從【數據庫】中獲取數據, id: 10, DataObject: DataObject(id=10, desc=name:10)
設置數據到【緩存】, DataObject: DataObject(id=10, desc=name:10)
......

 

3、緩存擊穿

對於一些設置了過期時間的key,如果這些key可能會在某些時間點被超高併發地訪問,是一種非常“熱點”的數據。這個時候,需要考慮一個問題:緩存被“擊穿”的問題,這個和緩存雪崩的區別在於這裏針對某一key緩存,前者則是很多key。
緩存在某個時間點過期的時候,恰好在這個時間點對這個Key有大量的併發請求過來,這些請求發現緩存過期一般都會從後端DB加載數據並回設到緩存,這個時候大併發的請求可能會瞬間把後端DB壓垮。

 

緩存擊穿模擬:

  • 1、查詢ID爲50的請求,這時id爲50的數據會加載到緩存
  • 2、再次查詢ID爲50的請求,命中緩存
  • 3、300併發請求ID爲50的數據,這個時候請求全部命中緩存
  • 4、將ID爲50的緩存手動刪除,模擬緩存失效
  • 5、300併發請求ID爲50的數據,這個時候大量請求走向數據庫,ID爲50的緩存被擊穿
curl http://127.0.0.1:8080/data/getData1?id=50
curl http://127.0.0.1:8080/data/getData1?id=50

採用JMeter模擬併發請求,打開“場景模擬-緩存擊穿.jmx”執行請求

curl http://127.0.0.1:8080/data/deleteCache?id=50

再次執行JMeter併發請求

執行第3步後,查詢日誌可以看出請求全部命中緩存,以下爲日誌片段

......
從緩存中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從緩存中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從緩存中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從緩存中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從緩存中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從緩存中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從緩存中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從緩存中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
......

執行第5步後,查詢日誌可以看出ID爲50的請求多次查詢了數據庫並設置緩存,之後才命中緩存,以下爲日誌片段

......
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: null
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: null
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: null
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: null
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: null
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: null
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
......

 

二、最佳實踐方案測試

 

優化後的查詢方法代碼

public DataObject getData(Long id) {
        //從緩存讀取數據
        DataObject result = getDataFromCache(id);
        if (result == null) {
            //緩存不存在,從數據庫查詢數據的過程加上鎖,避免緩存擊穿導致數據庫壓力過大
            RLock lock = redissonClient.getLock(DATA_LOCK_NAME + id);
            lock.lock(15, TimeUnit.SECONDS);
            if (lock.isLocked()) {
                try {
                    //雙重判斷,第二個以及之後的請求不必去找數據庫,直接命中緩存
                    //再次查詢緩存
                    result = getDataFromCache(id);
                    if (result == null) {
                        // 從數據庫查詢數據
                        result = getDataFromDB(id);
                        // 將查詢到的數據寫入緩存
                        setDataToCache(id, result);
                    }
                } finally {
                    //鎖只能被擁有它的線程解鎖
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            }
        } else {
            if (result.getId() == ID_NOT_EXISTS) {
                return null;
            }
        }
        return result;
    }

1、緩存穿透

測試計劃:線程數100,重複執行1次

併發查詢數據庫中不存在的ID 101至110 的數據,可以看出每個ID都只執行了一次數據庫查詢並設置緩存,之後請求都命中了緩存,有效防止了緩存穿透問題,以下爲日誌片段

......
從【緩存】中獲取數據, id: 110, DataObject: null
從【緩存】中獲取數據, id: 107, DataObject: null
從【緩存】中獲取數據, id: 107, DataObject: null
從【緩存】中獲取數據, id: 106, DataObject: null
從【緩存】中獲取數據, id: 110, DataObject: null
從【緩存】中獲取數據, id: 104, DataObject: null
從【緩存】中獲取數據, id: 109, DataObject: null
從【緩存】中獲取數據, id: 108, DataObject: null
從【緩存】中獲取數據, id: 107, DataObject: null
從【緩存】中獲取數據, id: 102, DataObject: null
從【緩存】中獲取數據, id: 110, DataObject: null
從【緩存】中獲取數據, id: 106, DataObject: null
從【緩存】中獲取數據, id: 101, DataObject: null
從【緩存】中獲取數據, id: 103, DataObject: null
從【緩存】中獲取數據, id: 105, DataObject: null
從【數據庫】中獲取數據, id: 109, DataObject: null
設置數據到【緩存】, DataObject: null
從【緩存】中獲取數據, id: 109, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 109, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 109, DataObject: DataObject(id=-1, desc=null)
從【數據庫】中獲取數據, id: 105, DataObject: null
設置數據到【緩存】, DataObject: null
從【數據庫】中獲取數據, id: 110, DataObject: null
設置數據到【緩存】, DataObject: null
從【數據庫】中獲取數據, id: 106, DataObject: null
設置數據到【緩存】, DataObject: null
從【數據庫】中獲取數據, id: 107, DataObject: null
設置數據到【緩存】, DataObject: null
從【緩存】中獲取數據, id: 109, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 105, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 110, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 106, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 109, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 107, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 105, DataObject: DataObject(id=-1, desc=null)
從【數據庫】中獲取數據, id: 102, DataObject: null
設置數據到【緩存】, DataObject: null
從【數據庫】中獲取數據, id: 103, DataObject: null
設置數據到【緩存】, DataObject: null
從【緩存】中獲取數據, id: 110, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 106, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 109, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 107, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 105, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 102, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 103, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 110, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 106, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 109, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 107, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 105, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 102, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 110, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 103, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 106, DataObject: DataObject(id=-1, desc=null)
從【數據庫】中獲取數據, id: 104, DataObject: null
設置數據到【緩存】, DataObject: null
從【緩存】中獲取數據, id: 107, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 109, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 105, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 102, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 103, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 110, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 106, DataObject: DataObject(id=-1, desc=null)
從【數據庫】中獲取數據, id: 108, DataObject: null
設置數據到【緩存】, DataObject: null
從【緩存】中獲取數據, id: 104, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 107, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 109, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 105, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 102, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 103, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 110, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 106, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 104, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 108, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 107, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 105, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 103, DataObject: DataObject(id=-1, desc=null)
從【緩存】中獲取數據, id: 102, DataObject: DataObject(id=-1, desc=null)
從【數據庫】中獲取數據, id: 101, DataObject: null
設置數據到【緩存】, DataObject: null
......

 

加入布隆過濾器

public DataObject getData(Long id) {
        //布隆過濾器中不存在,則直接返回空
        if (!clusterClient.exists(DATA_BF_NAME, ObjectUtils.nullSafeToString(id))) {
            System.out.println("布隆過濾器中不存在, id: " + id);
            return null;
        }
        //從緩存讀取數據
        DataObject result = getDataFromCache(id);
        if (result == null) {
            //緩存不存在,從數據庫查詢數據的過程加上鎖,避免緩存擊穿導致數據庫壓力過大
            RLock lock = redissonClient.getLock(DATA_LOCK_NAME + id);
            lock.lock(15, TimeUnit.SECONDS);
            if (lock.isLocked()) {
                try {
                    //雙重判斷,第二個以及之後的請求不必去找數據庫,直接命中緩存
                    //再次查詢緩存
                    result = getDataFromCache(id);
                    if (result == null) {
                        // 從數據庫查詢數據
                        result = getDataFromDB(id);
                        // 將查詢到的數據寫入緩存
                        setDataToCache(id, result);
                    }
                } finally {
                    //鎖只能被擁有它的線程解鎖
                    if (lock.isHeldByCurrentThread()) {
                        lock.unlock();
                    }
                }
            }
        } else {
            if (result.getId() == ID_NOT_EXISTS) {
                return null;
            }
        }
        return result;
    }

再次執行jmeter測試,可以看到ID在布隆過濾器中不存在,直接返回了(由於布隆過濾器會存在概率誤判,所以有的ID可能會執行後面的緩存和數據庫查詢)。日誌片段如下

......
布隆過濾器中不存在, id: 107
布隆過濾器中不存在, id: 101
布隆過濾器中不存在, id: 104
布隆過濾器中不存在, id: 106
布隆過濾器中不存在, id: 103
布隆過濾器中不存在, id: 101
布隆過濾器中不存在, id: 102
布隆過濾器中不存在, id: 103
布隆過濾器中不存在, id: 101
布隆過濾器中不存在, id: 105
布隆過濾器中不存在, id: 101
布隆過濾器中不存在, id: 103
布隆過濾器中不存在, id: 106
布隆過濾器中不存在, id: 107
布隆過濾器中不存在, id: 110
布隆過濾器中不存在, id: 108
布隆過濾器中不存在, id: 109
布隆過濾器中不存在, id: 110
布隆過濾器中不存在, id: 102
布隆過濾器中不存在, id: 101
布隆過濾器中不存在, id: 104
布隆過濾器中不存在, id: 102
布隆過濾器中不存在, id: 109
布隆過濾器中不存在, id: 110
布隆過濾器中不存在, id: 101
布隆過濾器中不存在, id: 107
布隆過濾器中不存在, id: 110
布隆過濾器中不存在, id: 109
......

2、緩存雪崩

測試計劃:線程數爲100,重複執行30次

併發請求失效緩存ID爲 1至10 的記錄,可以看到每個ID都只查詢了一次數據庫並設置緩存,之後的請求都命中了緩存

從【緩存】中獲取數據, id: 9, DataObject: null
從【緩存】中獲取數據, id: 1, DataObject: null
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 5, DataObject: null
從【緩存】中獲取數據, id: 7, DataObject: null
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 6, DataObject: null
從【緩存】中獲取數據, id: 3, DataObject: null
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 6, DataObject: null
從【緩存】中獲取數據, id: 5, DataObject: null
從【緩存】中獲取數據, id: 9, DataObject: null
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 9, DataObject: null
從【緩存】中獲取數據, id: 5, DataObject: null
從【緩存】中獲取數據, id: 1, DataObject: null
從【數據庫】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
設置數據到【緩存】, DataObject: DataObject(id=4, desc=name:4)
從【數據庫】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
設置數據到【緩存】, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 1, DataObject: null
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【數據庫】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
設置數據到【緩存】, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【緩存】中獲取數據, id: 6, DataObject: null
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【緩存】中獲取數據, id: 5, DataObject: null
從【緩存】中獲取數據, id: 7, DataObject: null
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 9, DataObject: null
從【數據庫】中獲取數據, id: 5, DataObject: DataObject(id=5, desc=name:5)
設置數據到【緩存】, DataObject: DataObject(id=5, desc=name:5)
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【緩存】中獲取數據, id: 1, DataObject: null
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 2, DataObject: null
從【數據庫】中獲取數據, id: 7, DataObject: DataObject(id=7, desc=name:7)
設置數據到【緩存】, DataObject: DataObject(id=7, desc=name:7)
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【緩存】中獲取數據, id: 5, DataObject: DataObject(id=5, desc=name:5)
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【數據庫】中獲取數據, id: 9, DataObject: DataObject(id=9, desc=name:9)
設置數據到【緩存】, DataObject: DataObject(id=9, desc=name:9)
從【緩存】中獲取數據, id: 5, DataObject: DataObject(id=5, desc=name:5)
從【緩存】中獲取數據, id: 6, DataObject: null
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 7, DataObject: DataObject(id=7, desc=name:7)
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 9, DataObject: DataObject(id=9, desc=name:9)
從【緩存】中獲取數據, id: 1, DataObject: null
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 7, DataObject: DataObject(id=7, desc=name:7)
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【緩存】中獲取數據, id: 5, DataObject: DataObject(id=5, desc=name:5)
從【緩存】中獲取數據, id: 6, DataObject: null
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 7, DataObject: DataObject(id=7, desc=name:7)
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【緩存】中獲取數據, id: 10, DataObject: null
從【數據庫】中獲取數據, id: 6, DataObject: DataObject(id=6, desc=name:6)
設置數據到【緩存】, DataObject: DataObject(id=6, desc=name:6)
從【緩存】中獲取數據, id: 5, DataObject: DataObject(id=5, desc=name:5)
從【緩存】中獲取數據, id: 9, DataObject: DataObject(id=9, desc=name:9)
從【緩存】中獲取數據, id: 9, DataObject: DataObject(id=9, desc=name:9)
從【緩存】中獲取數據, id: 1, DataObject: null
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 7, DataObject: DataObject(id=7, desc=name:7)
從【緩存】中獲取數據, id: 5, DataObject: DataObject(id=5, desc=name:5)
從【緩存】中獲取數據, id: 6, DataObject: DataObject(id=6, desc=name:6)
從【緩存】中獲取數據, id: 7, DataObject: DataObject(id=7, desc=name:7)
從【緩存】中獲取數據, id: 9, DataObject: DataObject(id=9, desc=name:9)
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 1, DataObject: null
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【緩存】中獲取數據, id: 5, DataObject: DataObject(id=5, desc=name:5)
從【緩存】中獲取數據, id: 7, DataObject: DataObject(id=7, desc=name:7)
從【數據庫】中獲取數據, id: 1, DataObject: DataObject(id=1, desc=name:1)
設置數據到【緩存】, DataObject: DataObject(id=1, desc=name:1)
從【緩存】中獲取數據, id: 6, DataObject: DataObject(id=6, desc=name:6)
從【緩存】中獲取數據, id: 9, DataObject: DataObject(id=9, desc=name:9)
從【緩存】中獲取數據, id: 5, DataObject: DataObject(id=5, desc=name:5)
從【緩存】中獲取數據, id: 9, DataObject: DataObject(id=9, desc=name:9)
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 1, DataObject: null
從【數據庫】中獲取數據, id: 2, DataObject: DataObject(id=2, desc=name:2)
設置數據到【緩存】, DataObject: DataObject(id=2, desc=name:2)
從【緩存】中獲取數據, id: 6, DataObject: DataObject(id=6, desc=name:6)
從【緩存】中獲取數據, id: 2, DataObject: null
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【緩存】中獲取數據, id: 5, DataObject: DataObject(id=5, desc=name:5)
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 7, DataObject: DataObject(id=7, desc=name:7)
從【緩存】中獲取數據, id: 6, DataObject: DataObject(id=6, desc=name:6)
從【緩存】中獲取數據, id: 8, DataObject: DataObject(id=8, desc=name:8)
從【緩存】中獲取數據, id: 9, DataObject: DataObject(id=9, desc=name:9)
從【數據庫】中獲取數據, id: 10, DataObject: DataObject(id=10, desc=name:10)
設置數據到【緩存】, DataObject: DataObject(id=10, desc=name:10)
從【緩存】中獲取數據, id: 10, DataObject: null
從【緩存】中獲取數據, id: 1, DataObject: DataObject(id=1, desc=name:1)
從【緩存】中獲取數據, id: 4, DataObject: DataObject(id=4, desc=name:4)
從【緩存】中獲取數據, id: 5, DataObject: DataObject(id=5, desc=name:5)
從【緩存】中獲取數據, id: 7, DataObject: DataObject(id=7, desc=name:7)
從【緩存】中獲取數據, id: 2, DataObject: DataObject(id=2, desc=name:2)
從【緩存】中獲取數據, id: 3, DataObject: DataObject(id=3, desc=name:3)
從【緩存】中獲取數據, id: 6, DataObject: DataObject(id=6, desc=name:6)
從【緩存】中獲取數據, id: 7, DataObject: DataObject(id=7, desc=name:7)

3、緩存擊穿

測試計劃:線程數爲100,重複執行1次

先讓ID爲50的緩存失效

curl http://127.0.0.1:8080/data/deleteCache?id=50

併發請求失效緩存ID爲50的記錄,可以看到只有一個請求執行了數據庫查詢並設置緩存,其他請求都命中了緩存,日誌片段如下:

......
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【緩存】中獲取數據, id: 50, DataObject: null
從【數據庫】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
設置數據到【緩存】, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
從【緩存】中獲取數據, id: 50, DataObject: DataObject(id=50, desc=name:50)
......

 

完整項目代碼參考: cache-best-practice
內含jmeter測試腳本

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