快取擊穿和快取雪崩的區別在於:雪崩針對很多 key,而擊穿只針對於某一個熱點 key。
快取雪崩的實現方案有不少,有一些能夠解決快取擊穿,有一些卻不能,例如:
設定快取永不過期,這個方法雖然很暴力,但是確實能解決大部分的問題,當然,大部分場景也不太適用;
設定隨機過期時間,這個方案對於快取擊穿來說就不太適用了,因為擊穿只針對一個熱點 key,只要它一失效,大量的訪問就會擊垮資料庫;
其餘的方案比如使用互斥鎖、雙快取機制,也都可以解決快取擊穿的問題,讓我們看看這些方案的具體實現。
這個方案很容易理解,但是實現起來卻很複雜,但凡需要使用快取的資料,都需要增加雙寫資料庫和快取的程式碼,並且雙寫過程中,還需要保持資料一致性。
快取依然保持設定過期時間,每次 get 快取的時候,都和資料的過期時間和當前時間進行一下對比,當間隔時間小於一個閾值的時候,主動更新快取。
比如(快取過期時間 - 當前系統時間)小於 5 分鐘,那麼就重新整理一次快取,並且重置快取過期時間;
不過這個方法也有個致命的問題:如果一個數據,恰好在快取失效前五分鐘,一次訪問都沒有,那麼就不會觸發檢查更新,當快取失效後有大量請求訪問,那麼也會造成快取擊穿。
在快取失效後,透過互斥鎖或者佇列,控制讀資料庫和寫快取的執行緒數量。
第一種方法:整個方法是 synchronized 的,這樣做雖然可以防止大量請求落到 DB 上,但是就算是快取沒有失效,需要從 DB 中查詢資料也需要排隊,無疑是降低了系統的吞吐量。
第二種方法:當快取失效時,只對查詢資料庫的操作進行加鎖,這樣對於快取沒有失效的情況也非常友好,但是查詢操作這裡加鎖,也只是會阻塞掉住其他呼叫,第一其他執行緒要等待,對呼叫方不友好,第二這些請求被阻塞的請求最終還是會落到 DB 上的。
第三種方法:使用互斥鎖,搶到鎖的話讀資料庫並寫入快取,搶不到鎖的話也不阻塞,而是直接去讀快取,如果快取中依然讀不到資料(搶到鎖的可能還沒有將快取寫入成功),就等一會再試試讀快取。
設定一級快取和二級快取,一級快取過期時間短,二級快取過期時間長或者不過期,一級快取失效後訪問二級快取,同時重新整理一級快取和二級快取。
雙快取的方式,說白了就是不能將一級快取和二級快取中資料同時變成失效,當一級快取失效後,有多個請求訪問,彼此之間依然是競爭鎖,搶到鎖的執行緒查詢資料庫並重新整理快取,而其他沒有搶到鎖的執行緒,直接訪問二級快取(程式碼可以參考上文中的互斥鎖),如圖:
快取擊穿和快取雪崩的區別在於:雪崩針對很多 key,而擊穿只針對於某一個熱點 key。
快取雪崩的實現方案有不少,有一些能夠解決快取擊穿,有一些卻不能,例如:
設定快取永不過期,這個方法雖然很暴力,但是確實能解決大部分的問題,當然,大部分場景也不太適用;
設定隨機過期時間,這個方案對於快取擊穿來說就不太適用了,因為擊穿只針對一個熱點 key,只要它一失效,大量的訪問就會擊垮資料庫;
其餘的方案比如使用互斥鎖、雙快取機制,也都可以解決快取擊穿的問題,讓我們看看這些方案的具體實現。
主動重新整理快取這個方案很容易理解,但是實現起來卻很複雜,但凡需要使用快取的資料,都需要增加雙寫資料庫和快取的程式碼,並且雙寫過程中,還需要保持資料一致性。
檢查更新快取依然保持設定過期時間,每次 get 快取的時候,都和資料的過期時間和當前時間進行一下對比,當間隔時間小於一個閾值的時候,主動更新快取。
比如(快取過期時間 - 當前系統時間)小於 5 分鐘,那麼就重新整理一次快取,並且重置快取過期時間;
不過這個方法也有個致命的問題:如果一個數據,恰好在快取失效前五分鐘,一次訪問都沒有,那麼就不會觸發檢查更新,當快取失效後有大量請求訪問,那麼也會造成快取擊穿。
使用鎖在快取失效後,透過互斥鎖或者佇列,控制讀資料庫和寫快取的執行緒數量。
第一種方法:整個方法是 synchronized 的,這樣做雖然可以防止大量請求落到 DB 上,但是就算是快取沒有失效,需要從 DB 中查詢資料也需要排隊,無疑是降低了系統的吞吐量。
第二種方法:當快取失效時,只對查詢資料庫的操作進行加鎖,這樣對於快取沒有失效的情況也非常友好,但是查詢操作這裡加鎖,也只是會阻塞掉住其他呼叫,第一其他執行緒要等待,對呼叫方不友好,第二這些請求被阻塞的請求最終還是會落到 DB 上的。
第三種方法:使用互斥鎖,搶到鎖的話讀資料庫並寫入快取,搶不到鎖的話也不阻塞,而是直接去讀快取,如果快取中依然讀不到資料(搶到鎖的可能還沒有將快取寫入成功),就等一會再試試讀快取。
雙快取設定一級快取和二級快取,一級快取過期時間短,二級快取過期時間長或者不過期,一級快取失效後訪問二級快取,同時重新整理一級快取和二級快取。
雙快取的方式,說白了就是不能將一級快取和二級快取中資料同時變成失效,當一級快取失效後,有多個請求訪問,彼此之間依然是競爭鎖,搶到鎖的執行緒查詢資料庫並重新整理快取,而其他沒有搶到鎖的執行緒,直接訪問二級快取(程式碼可以參考上文中的互斥鎖),如圖: