Redis作為緩存可能會出現的問題及解決方案
- 2020 年 7 月 16 日
- 筆記
Redis是個大話題,只要是去面試Java開發,幾乎必問。基礎一點的問Redis是什麼東西?用來做什麼?Redis支持哪些數據類型?Redis的性能為什麼那麼好?複雜一點的就會問到緩存穿透、緩存擊穿、緩存雪崩等問題。而我在面試的時候也被問到了Redis為什麼用來做緩存的問題。
所以我覺得很有必要總結一下Redis作為緩存使用,可能會引發的問題。以達到溫故而知新的效果
ps:在本文章中,就不討論Redis能用來幹啥?這種基礎問題了
在討論Redis作為緩存使用,可能會引發的問題之前,我們得了解官方是怎麼定義Redis
Redis是一個開源的內存中的數據結構存儲系統,它可以用作:數據庫、緩存和消息中間件。
它支持多種類型的數據結構,如字符串(Strings),散列(Hash),列表(List),集合(Set),有序集合(Sorted Set或者是ZSet)。
Redis 內置了複製(Replication),LUA腳本(Lua scripting), LRU驅動事件(LRU eviction),事務(Transactions) 和不同級別的磁盤持久化(Persistence),並通過 Redis哨兵(Sentinel)和自動分區(Cluster)提供高可用性(High Availability)。
Redis也提供了兩種持久化策略,這些策略可以讓用戶將自己的數據保存到磁盤上面進行存儲。根據實際情況,可以每隔一定時間將數據集以快照的形式保存在磁盤(RDB策略),或者將所有操作成功的命令追加到命令日誌中(AOF策略),它會在執行命令時,將被執行的命令複製到硬盤裏面,實現實時持久化數據的效果。當然,根據實際開發的需求,你也可以關閉持久化功能,單純的將Redis作為一個高效的網絡的緩存數據功能使用。
二、Redis作為緩存使用,可能會引發的問題(重點)
Redis由C語言開發,並且將數據存儲在內存中,可以說Redis完全是基於內存進行操作,對數據讀寫的速度極快、性能極好。官方提供的數據是可以達到每秒100000+的吞吐量(每秒內查詢次數),如此優秀的機制使Redis極其適合作為緩存使用。
1.緩存穿透
程序在處理緩存時,一般是先從緩存查詢,如果緩存沒有這個key(理解為數據),則會從數據庫中查詢,並將查詢到的數據保存到緩存中去。
好,問題來了,如果有個壞心眼的人向服務器發起請求,去查詢一個一定不存在的數據,由於緩存中沒有查到對應的數據時需要從數據庫查詢,查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到數據庫去查詢,緩存形同虛設,這就是緩存穿透。
解決方案:
1)最粗暴也是最常用的方法就是,如果一個查詢的數據為空(不管是數據不存在還是系統故障),我們就把這個空結果進行緩存,但要把它的過期時間設置得很短,最長不超過5分鐘,這樣能有效的解決緩存穿透問題。
2)其次,可以採用布隆過濾器,也能解決緩存穿透問題
2.緩存擊穿
緩存擊穿和緩存穿透在本質上很相似,都是查詢數據時緩存失去了作用,導致請求直接去數據庫查詢數據,但是造成緩存失效的原因卻是天差地別。
大量用戶在同一時間內訪問某熱點數據時,存儲在緩存中的熱點數據卻突然失效(過期時間),結果就是大量的請求直接訪問數據庫,使數據庫的壓力變大,甚至導致數據庫宕機,這就是緩存擊穿。
解決方案:比較常用的方法是加互斥鎖(mutex)保證數據的一致性。簡單地來說,就是在緩存失效的時候(判斷拿出來的值為空),不是立即去訪問數據庫,而是先使用緩存工具的某些帶成功操作返回值的操作(比如Redis的SETNX或者Memcache的ADD)去set另一個請求所需要的數據,當操作返回成功時,再進行訪問數據庫的操作並回設緩存;否則,就重試整個get緩存的方法。
3.緩存雪崩
如果緩存的數據集中在一段時間內大批失效,而不巧的是在這段時間內又有大量用戶發起請求訪問數據,這樣就會造成大量的緩存擊穿,所有的請求都會直接去訪問數據庫,導致數據庫在短時間內宕機,這就是緩存雪崩。
ps:這裡我使用了誇張的修辭手法。緩存雪崩不一定會造成數據庫宕機,但緩存如果發生雪崩現象,那肯定是很嚴重的
解決方案:
1)加鎖排隊。加互斥鎖,添加信息隊列
2)數據預熱。可以通過緩存Reload機制,預先去更新緩存,再即將發生大並發訪問前手動觸發加載緩存不同的key,設置不同的過期時間,讓緩存失效的時間點盡量均勻
3)設置熱點緩存永不過時
筆者: 以上問題及解決方案純粹個人見解,如果有錯誤的地方,還請指正