這一節,我們來學習一下快取異常。快取異常有四種型別,分別是快取和資料庫的資料不一致、快取雪崩、快取擊穿和快取穿透。
下面通過了解這四種快取異常的原理和應對方法。
快取和資料庫的資料不一致
快取和資料庫的資料一致性包含兩種情況:
- 快取中有資料,快取的資料值需要和資料庫中的值相同;
- 快取中沒有資料,資料庫中的值必須是最新值。
資料不一致是如何發生的?
在第1講中關於快取的型別那節,介紹了快取有兩種不同型別,分別是隻讀快取和讀寫快取。不同型別的快取資料不一致的發生情況不一樣,應對方法也不一樣。
讀寫快取:有兩種寫回策略,同步直寫和非同步寫回。如果要保證資料一致,就要採用同步直寫策略。但需要保證快取和資料庫的更新具有原子性,即要麼都成功,要麼都失敗。
只讀快取:分新增資料和刪改資料兩種情況說明。
新增資料
資料直接寫到資料庫中,不對快取做任何操作,符合一致性的第2種情況。
刪改資料
發生刪改操作時,既要更新資料庫,也要在快取裡刪除資料。因為快取和資料庫是不同的系統,這裡分兩種情況:
- 先刪除快取,再更新資料庫:資料庫更新失敗,導致請求再次訪問快取時,發現快取失敗,再讀資料庫時,從資料庫中讀取舊值。
- 先更新資料庫,再刪除快取:快取刪除失敗,導致請求再次訪問快取時,發現快取命中,並從快取中讀取到舊值。
如何解決資料不一致?
使用重試機制,指把刪除的快取值或者是要更新的資料庫值暫存到訊息佇列中(例如使用Kafka訊息佇列)。
當應用沒有能夠成功地刪除快取值或者是更新資料庫值時,從訊息佇列中重新讀取這些值,然後再次進行刪除或更新。
如果成功刪除,就從訊息佇列中刪除,以免重複操作。否則就要進行重試,如果重試超過一定次數,就要向業務層傳送報錯資訊。
具體情況如下圖所示:
總結一下,對於只讀快取來說,建議優先使用先更新資料庫,再刪除快取。
快取雪崩
快取雪崩,指大量的應用請求無法在Redis快取中進行處理,然後應用將大量請求傳送到資料庫層,導致資料庫層的壓力激增。
導致快取雪崩的兩個原因:
1. 快取中有大量資料同時過期,導致大量請求無法得到處理。
解決方案有兩個,一是避免給大量的資料設定相同的過期時間,增加一個較小的隨機數(例如,隨機增加1~3分鐘)。
另一個是服務降級,服務降級指發生快取雪崩時,針對不同的資料採取不同的處理方式:
- 非核心資料,暫時停止從快取中查詢,直接返回預定義資訊、空值或者錯誤資訊;
- 核心資料,允許查詢快取,如果快取缺失,繼續通過資料庫讀取。
2. Redis快取例項發生故障當機了,無法處理請求。
有兩個建議,一是在業務系統中實現服務熔斷或請求限流機制。
服務熔斷是指在發生快取雪崩時,為了防止引發連鎖的資料庫雪崩,暫停業務應用對快取系統的介面訪問。
具體點,就是業務應用呼叫快取介面時,快取客戶端並不把請求發給Redis快取例項,而是直接返回,等Redis快取例項重新恢復服務後,再允許傳送。
服務熔斷會暫停了整個快取系統的訪問,對業務應用的影響範圍大。而請求限流相比服務熔斷造成的影響沒那麼大。
請求限流是指業務系統的請求入口前端控制每秒進入系統的請求數,避免過多的請求被髮送到資料庫。
二是事前預防,通過主從節點構建Redis快取高可靠叢集。
快取擊穿
快取擊穿,指標對某個訪問非常頻繁的熱點資料的請求,無法在快取中進行處理,大量請求傳送到後端資料庫,導致資料庫壓力激增,影響資料庫處理其他請求。
解決方案是,對於訪問特別頻繁的熱點資料,不設定過期時間。
快取穿透
快取穿透,指要訪問的資料既不在Redis快取中,也不在資料庫中,導致請求在訪問快取時,發生快取缺失,再去訪問資料庫時,也發現沒有資料。
如果有大量請求訪問資料,就會同時給快取和資料庫帶來巨大壓力。
發生快取穿透有兩種情況:
- 業務層誤操作:快取中的資料和資料庫中的資料被誤刪除;
- 惡意攻擊:專門訪問資料庫中沒有的資料。
為了避免快取穿透,有三種應對方案。
第一種方案是,快取空值或預設值
一旦發生快取穿透,就針對查詢的資料,在Redis中快取一個空值或是和業務層協商確定的預設值。
第二種方案是,使用布隆過濾器快速判斷資料是否存在,避免從資料庫中查詢資料是否存在,減輕資料庫壓力
布隆過濾器由一個初值都為0的bit陣列和N個雜湊函式組成,可以用來快速判斷某個資料是否存在。
通過三個操作完成標記:
- 使用N個雜湊函式,分別計算這個資料的雜湊值,得到N個雜湊值
- 把這N個雜湊值對bit陣列的長度取模,得到每個雜湊值的位置
- 把對應的位置的bit位設定為1
這樣一來,即使發生快取穿透,大量請求只會查詢Redis和布隆過濾器。
第三種方案是,在請求入口的前端進行請求檢測
例如對請求進行合法性檢測,把惡意的請求(例如請求引數不合理、請求引數是非法值、請求欄位不存在)直接過濾掉
總結
另外還有三個建議:
- 針對快取雪崩,合理地設定資料過期時間,以及搭建高可靠快取叢集。
- 針對快取擊穿,在快取訪問非常頻繁的熱點資料時,不要設定過期時間。
- 針對快取穿透,提前在入口前端實現惡意請求檢測,或者規範資料庫的資料刪除操作,避免誤刪除。