這一節,咱們來學習一下緩存異常。緩存異常有四種類型,分別是緩存和數據庫的數據不一致、緩存雪崩、緩存擊穿和緩存穿透。html
下面經過了解這四種緩存異常的原理和應對方法。前端
緩存和數據庫的數據一致性包含兩種狀況:數據庫
在第1講中關於緩存的類型那節,介紹了緩存有兩種不一樣類型,分別是隻讀緩存和讀寫緩存。不一樣類型的緩存數據不一致的發生狀況不同,應對方法也不同。後端
讀寫緩存:有兩種寫回策略,同步直寫和異步寫回。若是要保證數據一致,就要採用同步直寫策略。但須要保證緩存和數據庫的更新具備原子性,即要麼都成功,要麼都失敗。數組
只讀緩存:分新增數據和刪改數據兩種狀況說明。緩存
新增數據異步
數據直接寫到數據庫中,不對緩存作任何操做,符合一致性的第2種狀況。函數
刪改數據學習
發生刪改操做時,既要更新數據庫,也要在緩存裏刪除數據。由於緩存和數據庫是不一樣的系統,這裏分兩種狀況:指針
使用重試機制,指把刪除的緩存值或者是要更新的數據庫值暫存到消息隊列中(例如使用Kafka消息隊列)。
當應用沒有可以成功地刪除緩存值或者是更新數據庫值時,從消息隊列中從新讀取這些值,而後再次進行刪除或更新。
若是成功刪除,就從消息隊列中刪除,以避免重複操做。不然就要進行重試,若是重試超過必定次數,就要向業務層發送報錯信息。
具體狀況以下圖所示:
總結一下,對於只讀緩存來講,建議優先使用先更新數據庫,再刪除緩存。
緩存雪崩,指大量的應用請求沒法在Redis緩存中進行處理,而後應用將大量請求發送到數據庫層,致使數據庫層的壓力激增。
致使緩存雪崩的兩個緣由:
1. 緩存中有大量數據同時過時,致使大量請求沒法獲得處理。
解決方案有兩個,一是避免給大量的數據設置相同的過時時間,增長一個較小的隨機數(例如,隨機增長1~3分鐘)。
另外一個是服務降級,服務降級指發生緩存雪崩時,針對不一樣的數據採起不一樣的處理方式:
2. Redis緩存實例發生故障宕機了,沒法處理請求。
有兩個建議,一是在業務系統中實現服務熔斷或請求限流機制。
服務熔斷是指在發生緩存雪崩時,爲了防止引起連鎖的數據庫雪崩,暫停業務應用對緩存系統的接口訪問。
具體點,就是業務應用調用緩存接口時,緩存客戶端並不把請求發給Redis緩存實例,而是直接返回,等Redis緩存實例從新恢復服務後,再容許發送。
服務熔斷會暫停了整個緩存系統的訪問,對業務應用的影響範圍大。而請求限流相比服務熔斷形成的影響沒那麼大。
請求限流是指業務系統的請求入口前端控制每秒進入系統的請求數,避免過多的請求被髮送到數據庫。
二是事前預防,經過主從節點構建Redis緩存高可靠集羣。
緩存擊穿,指針對某個訪問很是頻繁的熱點數據的請求,沒法在緩存中進行處理,大量請求發送到後端數據庫,致使數據庫壓力激增,影響數據庫處理其餘請求。
解決方案是,對於訪問特別頻繁的熱點數據,不設置過時時間。
緩存穿透,指要訪問的數據既不在Redis緩存中,也不在數據庫中,致使請求在訪問緩存時,發生緩存缺失,再去訪問數據庫時,也發現沒有數據。
若是有大量請求訪問數據,就會同時給緩存和數據庫帶來巨大壓力。
發生緩存穿透有兩種狀況:
爲了不緩存穿透,有三種應對方案。
第一種方案是,緩存空值或缺省值
一旦發生緩存穿透,就針對查詢的數據,在Redis中緩存一個空值或是和業務層協商肯定的缺省值。
第二種方案是,使用布隆過濾器快速判斷數據是否存在,避免從數據庫中查詢數據是否存在,減輕數據庫壓力
布隆過濾器由一個初值都爲0的bit數組和N個哈希函數組成,能夠用來快速判斷某個數據是否存在。
經過三個操做完成標記:
這樣一來,即便發生緩存穿透,大量請求只會查詢Redis和布隆過濾器。
第三種方案是,在請求入口的前端進行請求檢測
例如對請求進行合法性檢測,把惡意的請求(例如請求參數不合理、請求參數是非法值、請求字段不存在)直接過濾掉
另外還有三個建議: