如何解決資料不一致、快取雪崩、快取擊穿和快取穿透問題
一、資料一致性(無法做到絕對一致)
快取分成讀寫快取和只讀快取
讀寫快取寫回策略有兩種
1、同步直寫策略:寫快取時,也同步寫資料庫,快取和資料庫中的資料一致;(要在業務應用中使用事務機制,來保證快取和資料庫的更新具有原子性)
2、非同步寫回策略:寫快取時不同步寫資料庫,等到資料從快取中淘汰時,再寫回資料庫。使用這種策略時,如果資料還沒有寫回資料庫,快取就發生了故障,那麼,此時,資料庫就沒有最新的資料了。
只讀快取
刪除快取值或者更新資料庫值失敗
解決方案:重試機制確保刪除或者更新操作成功
流程如下圖所示:
(1)更新資料庫資料
(2)資料庫會將操作資訊寫入binlog日誌當中
(3)訂閱程式提取出所需要的資料以及key
(4)另起一段非業務程式碼,獲得該資訊
(5)嘗試刪除快取操作,發現刪除失敗
(6)將這些資訊傳送至訊息佇列
(7)重新從訊息佇列中獲得該資料,重試操作。
刪除快取、更新資料庫時其他執行緒的併發讀操作
情況一:先刪除快取,再更新資料庫。
解決方案:延時雙刪策略(延時多久不太好確定)
情況二:先更新資料庫值,再刪除快取
一種場景是:(期間請求量不會很多,所以影響還好)
另一種場景是:一個請求A做查詢操作,一個請求B做更新操作,那麼會有如下情形產生(主從分離,讀應該比寫快,如果下列場景出現的概率極低)
(1)快取剛好失效
(2)請求A查詢資料庫,得一箇舊值
(3)請求B將新值寫入資料庫
(4)請求B刪除快取
(5)請求A將查到的舊值寫入快取
解決方案:延時雙刪策略(延時多久不太好確定,操作,刪key,延時,刪key)
注意:以上場景,mysql主從分離也可能會導致(主從延時),所以延時要加上主從延時時長
參考資料:https://blog.csdn.net/sufu1065/article/details/108459758
二、快取雪崩
快取雪崩是指大量的應用請求無法在 Redis 快取中進行處理,緊接著,應用將大量請求傳送到資料庫層,導致資料庫層的壓力激增
第一個原因是:快取中有大量資料同時過期,所有請求達到了資料庫上
解決方案:1.不要設定同樣的過期時間,可以加上一些隨機數值,2.服務降級:只讓核心資料去請求應用,非核心資料禁止訪問快取和資料庫 3、熱點資料不過期
第二個原因是:Redis 快取例項發生故障宕機了,無法處理請求,這就會導致大量請求一下子積壓到資料庫層,從而發生快取雪崩。(一個 Redis 例項可以支援數萬級別的請求處理吞吐量,而單個數據庫可能只能支援數千級別的請求處理吞吐量)
解決方案:1.在業務系統中實現服務熔斷或請求限流機制。2.主從節點(主節點宕機,從節點切換成主節點繼續提供服務)
三、快取擊穿
快取擊穿是指,針對某個訪問非常頻繁的熱點資料的請求,無法在快取中進行處理,緊接著,訪問該資料的大量請求,一下子都發送到了後端資料庫,導致了資料庫壓力激增,會影響資料庫處理其他請求。快取擊穿的情況,經常發生在熱點資料過期失效時,如下圖所示
解決方案:不給熱點資料設定過期時間,一直保留
快取雪崩與快取擊穿的區別
快取雪崩是因為大面積的快取失效,打崩了DB,而快取擊穿不同的是快取擊穿是指一個Key非常熱點,在不停的扛著大併發,大併發集中對這一個點進行訪問,當這個Key在失效的瞬間,持續的大併發就穿破快取,直接請求資料庫,就像在一個完好無損的桶上鑿開了一個洞。
四、快取穿透
快取穿透是指要訪問的資料既不在 Redis 快取中,也不在資料庫中,導致請求在訪問快取時,發生快取缺失,再去訪問資料庫時,發現數據庫中也沒有要訪問的資料
解決方案:1、介面前面加上校驗,去掉不合法的請求,2、快取空值或預設值。一旦發生快取穿透,我們就可以針對查詢的資料,在 Redis 中快取一個空值。查詢時就可以直接從 Redis 中讀取空值,返回給業務應用了,避免了把大量請求傳送給資料庫處理(時間要設定的短一些)。3、使用布隆過濾器快速判斷資料是否存在,避免從資料庫中查詢資料是否存在,減輕資料庫壓力。
- 事前:Redis高可用,主從+哨兵,Redis cluster,避免全盤崩潰。
- 事中:本地ehcache快取 +Hystrix限流+降級,避免MySQL被打死。
- 事後:Redis持久化RDB+AOF,一旦重啟,自動從磁碟上載入資料,快速恢復快取資料。
參考資料:https://segmentfault.com/a/1190000022029639
五、布隆過濾器
布隆過濾器可以檢查值是“可能在集合中”還是“絕對不在集合中”。“可能” 表示有一定的概率,也就是說可能存在一定為誤判率。
參考資料:https://juejin.cn/post/6844904007790673933