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測試腳本