應用緩存的常見問題及解決

使用緩存一些常見的套路問題。html

 

緩存穿透前端

  • 場景:大量請求訪問某個不存在的KEY

在緩存設計中,查詢緩存 -> key不存在 -> 回源DB -> 更新緩存,這是一個典型的方案。web

緩存穿透是指查詢一個必定不存在的Key,因爲緩存層不存在,將致使這個不存在的數據每次請求都要到存儲層去查詢,直接對DB形成影響。在惡意攻擊和失敗回調中可能會出現這種狀況。shell

  • 解決方案

1.對空對象進行緩存。對查詢結果爲空的狀況也進行緩存,如當此查詢結果爲空,設置Key對應對象爲NULL,緩存時間設置短一點,存儲層中有數據後及時更新。數據庫

2.對全部可能查詢的參數Key以hash形式存儲,在控制層先進行校驗,不符合則丟棄。最多見的是採用布隆過濾器,將全部可能存在的數據哈希到一個足夠大的bitmap中,一個必定不存在的數據會被這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。比較適合命中不高,可是更新不頻繁的數據。後端

 

緩存失效緩存

  • 場景:緩存中大量的Key集中在一段時間內失效,數據庫的壓力凸顯
  • 解決方案

1.能夠分析用戶行爲,儘可能讓失效時間點均勻分佈。針對失效時間相同的key,在設置失效時間時不設置固定的時間,而在原有基礎上加上一個隨機的值,好比1分鐘-5分鐘,這樣就能夠有效分散開緩存失效的時間。安全

2.考慮用加鎖或者隊列的方式保證緩存的單線程(進程)寫,從而避免失效時大量的併發請求落到底層存儲系統上。併發

 

緩存併發分佈式

  • 場景:在高併發場景下,某些業務有可能多個請求併發的去從數據庫獲取數據

有時候若是網站併發訪問高,一個緩存若是失效,可能出現多個進程同時查詢DB,同時設置緩存的狀況,若是併發確實很大,這也可能形成DB壓力過大。

  • 解決方案

1.添加分佈式鎖,在緩存更新或者過時的狀況下,先嚐試獲取到鎖,當更新或者從數據庫獲取完成後再釋放鎖,其餘的請求只須要犧牲必定的等待時間,便可直接從緩存中繼續獲取數據。

2.按期從DB裏查詢數據,再刷到緩存裏面,確保緩存裏面的數據一直能夠讀到。

 

緩存雪崩

  • 場景:當發生大量的緩存穿透,例如緩存掛掉,或者對某個失效的緩存的大併發訪問

因爲緩存扛了大量的請求,有效保護了數據庫的安全。可是當緩存雪崩,所用請求就會瞬間所有打到DB上,可能會致使數據庫崩潰。

  • 解決方案

1.保證緩存服務的高可用性,當一個實例掛掉的時候,請求也能夠轉移到集羣的其餘實例上。緩存失效時的雪崩效應對底層系統的衝擊很是大,這時候可使用雙緩存機制,在工做緩存以外另外維護一層災備緩存。

2.使用降級策略,當緩存服務出現問題時,能夠暫時對用戶展現一份固定的數據,避免系統的崩潰,等待緩存服務的恢復。前端也應該有此機制,好比當後端接口返回非正常數據時,將以前保存的舊數據固定展現給用戶,避免頁面崩潰的問題。

 

緩存數據的淘汰

 

緩存淘汰的策略有兩種:

1.定時去清理過時的緩存

2.當有用戶請求過來時,再判斷這個請求所用到的緩存是否過時,過時的話就去底層系統獲得新數據並更新緩存

二者各有優劣,第一種的缺點是維護大量緩存的key是比較麻煩的,第二種的缺點就是每次用戶請求過來都要判斷緩存失效,邏輯相對比較複雜,具體用哪一種方案,根據本身的應用場景來權衡。

 

更新緩存仍是淘汰緩存

 

什麼是更新緩存:數據不但寫入數據庫,還會寫入緩存

什麼是淘汰緩存:數據只會寫入數據庫,不會寫入緩存,只會把數據淘汰掉

更新緩存的優勢:緩存不會增長一次miss,命中率高

淘汰緩存的優勢:簡單

 

先操做數據庫仍是先操做緩存

 

 

假設先寫數據庫,再淘汰緩存:第一步寫數據庫操做成功,第二步淘汰緩存失敗,則會出現DB中是新數據,Cache中是舊數據,數據不一致。

假設先淘汰緩存,再寫數據庫:第一步淘汰緩存成功,第二步寫數據庫失敗,則只會引起一次Cache miss。

因此結論是:先淘汰緩存,再寫數據庫

 

參考:

緩存與數據庫一致性保證

緩存更新的套路

相關文章
相關標籤/搜索