【專項測試系列】-快取擊穿、穿透、雪崩專項測試

2022-11-18 12:00:50

作者:劉須華

一、背景概述: R2M 快取的使用,極大的提升了應用程式的效能和效率,特別是資料查詢方面。而快取最常見的問題是快取穿透、擊穿和雪崩,在高並行下這三種情況都會有大量請求落到資料庫,導致資料庫資源佔滿,引起資料庫故障。平時對快取測試時除了關注增刪修改查詢等基本功能,應該要重點關注快取穿透、擊穿和雪崩三種異常場景的測試覆蓋,避免出現線上事故。

二、基本概念說明:

1、快取擊穿:是指在超級熱點資料突然過期,導致針對超級熱點的資料請求在過期期間直接打到資料庫,這樣資料庫伺服器會因為某一超熱資料導致壓力過大而崩掉。

 

2、快取穿透:是指查詢的資料在快取和資料庫中都不存在,導致每一次請求資料從快取中都獲取不到,而將請求打到資料庫伺服器,但資料庫中也沒有對應的資料,最後每一次請求都到資料庫;如果在高並行場景或有人惡意攻擊,就會導致後臺資料庫伺服器壓力增大,最終系統可能崩掉。

 

 

3、快取雪崩:是指突然快取層不可用,導致大量請求直接打到資料庫,最終由於資料庫壓力過大可能導致系統崩掉。快取層不可用指以下兩方面:快取伺服器宕機,系統將請求打到資料庫; 快取資料突然大範圍集中過期失效,導致大量請求打到資料庫重新載入資料,與快取擊穿的區別在於這裡針對很多 key 快取,前者則是某一個 key。

 

三、測試工具 (非必須):

1、使用 Titan 壓測平臺進行並行請求測試

2、使用 jmeter 工具模擬並行請求

四、測試方法舉例說明 (非必須):

環境:測試環境

工具:jmeter

(1)快取穿透場景

測試方法:查詢一個根本不存在的資料,快取層和儲存層都不會命中。

查詢介面相關程式碼實現:

 

通過 JMETER 模擬多次重複呼叫:單執行緒重複呼叫

 

檢視紀錄檔結果:從紀錄檔可以看出:執行並行請求後, 所有請求每次都走向了資料庫。

 

預防方案:

當資料庫查詢為空時,將快取賦值預設值,後續查詢都走快取,減少資料庫壓力。

上述介面,增加賦值為 empty,則第一次查詢到資料庫為空,後續查詢都查詢到快取中,快取值為 empty。

 

再次執行並行測試:從紀錄檔可以看出,可以看出每個 ID 都只執行了一次資料庫查詢並設定快取,之後請求都命中了快取,有效防止了快取穿透問題。

 

(2)快取擊穿場景

測試方法:對某個 Key 有大量的並行請求,這時從快取中刪除這個 key。模擬熱 key 過期失效的場景。這個時候大並行的請求可能會瞬間把後端 DB 壓垮。

介面相關部分程式碼實現:

 

操作步驟:

1、查詢 pin 為 liuxuhua 的請求,這時 pin 為 liuxuhua 的資料會載入到快取

2、再次查詢 pin 為 liuxuhua 的請求,命中快取

3、50 並行請求 pin 為 liuxuhua 的資料,這個時候請求全部命中快取

4、將 pin 為 liuxuhua 的快取手動刪除,模擬快取失效

5、50 並行請求 pin 為 liuxuhua 的資料,這個時候大量請求走向資料庫,pin 為 liuxuhua 的快取被擊穿

 

 

檢視紀錄檔結果:

 

預防方案:

在設定預設快取值的基礎上,進行加鎖處理。只有拿到鎖的第一個執行緒去請求資料庫,然後插入快取,當然每次拿到鎖的時候都要去查詢一下快取有沒有。

 

從紀錄檔記錄可以看到只有一個請求執行了資料庫查詢並設定快取,其他請求都命中了快取, 有效防止了快取的擊穿。

 

(3)快取雪崩

測試方法:對多個使用到快取的介面進行並行呼叫,設定這些快取時間已過期(即刪除快取),呼叫時這些介面查詢快取時無資料,去查詢資料庫,這些請求都指向資料庫,資料庫壓力增大,耗時增加。

模擬介面:

 

 

通過 JMETER 模擬多次重複呼叫:單執行緒多介面重複呼叫

 

檢視紀錄檔結果:可以看出大量請求到達資料庫,並且同一個 pin 或 id 執行了多次資料庫查詢

 

預防方案:

增加限流操作,即介面頻繁呼叫時,增加一個快取,設定時間為 3s,3s 內處理一定次數的請求,超過限制次數的請求直接返回結果,不做處理。

介面:3s 內處理 6 次請求,超過則不處理;

 

從紀錄檔可以看出:可以看到每個都只查詢了一次資料庫並設定快取,之後的請求都命中了快取

 

五、測試指標:(或者叫通過標準,包括關注點以及意義)

1、模擬快取穿透場景測試,每個不存在的資料都只執行了一次資料庫查詢並設定快取,之後請求都命中了快取,有效防止了快取穿透問題。

2、模擬快取雪崩場景測試,每個快取失效的資料都只執行了一次資料庫查詢並設定快取,之後請求都命中了快取。

3、模擬快取擊穿場景測試,快取失效的那個資料只有一個請求執行了資料庫查詢並設定快取,其他請求都命中了快取。

六、適用業務場景:

1、秒殺活動

2、熱門行銷活動

3、618 和雙 11 大促

七、研發側常見解決方案(參考):

1、快取穿透解決方案:

1、快取空值 之所以發生穿透,是因為快取中沒有儲存這些資料的 key,從而每次都查詢資料庫 我們可以為這些 key 在快取中設定對應的值為 null,後面查詢這個 key 的時候就不用查詢資料庫了 當然為了健壯性,我們要對這些 key 設定過期時間,以防止真的有資料

2、BloomFilter BloomFilter 類似於一個 hbase set 用來判斷某個元素(key)是否存在於某個集合中 我們把有資料的 key 都放到 BloomFilter 中,每次查詢的時候都先去 BloomFilter 判斷,如果沒有就直接返回 null 注意 BloomFilter 沒有刪除操作,對於刪除的 key,查詢就會經過 BloomFilter 然後查詢快取再查詢資料庫,所以 BloomFilter 可以結合快取空值用,對於刪除的 key,可以在快取中快取 null 快取擊穿

2、快取擊穿解決方案:

採用分散式鎖,只有拿到鎖的第一個執行緒去請求資料庫,然後插入快取,當然每次拿到鎖的時候都要去查詢一下快取有沒有

3、快取雪崩解決方案:

1、採用叢集,降低服務宕機的概率

2、ehcache 本地快取 + 限流 & 降級

3、均勻過期,通常可以為有效期增加隨機值