Redis深度歷險筆記10 分佈式緩存可能出現的問題

一、緩存雪崩

我們設置緩存時採用了相同的過期時間,在同一時刻出現大面積的緩存過期。所有原本應該訪問緩存的請求都去查詢數據庫了,而對數據庫CPU和內存造成巨大壓力,嚴重的會造成數據庫宕機。從而形成一系列連鎖反應,造成整個系統崩潰。
解決:

  1. 使用鎖。來保證不會有大量的線程對數據庫一次性進行讀寫,從而避免失效時大量的併發請求落到底層存儲系統上。(不推薦,線程阻塞,用戶體驗差)
  2. 爲key設置不同的緩存失效時間。將緩存失效時間分散開,比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個緩存的過期時間的重複率就會降低,就很難引發集體失效的事件。
  3. 設置過期標誌更新緩存。給每一個緩存數據增加相應的緩存標記,記錄緩存的是否失效,如果緩存標記失效,則更新數據緩存。
  4. 將熱key分佈在不同的redis上。
  5. 設置熱點數據永不過期。
  6. 定期查一下緩存。

二、緩存穿透

緩存穿透是指用戶查詢數據,在數據庫沒有,自然在緩存中也不會有。這樣就導致用戶查詢的時候,在緩存中找不到,每次都要去數據庫再查詢一遍,然後返回空(相當於進行了兩次無用的查詢)。這樣請求就繞過緩存直接查數據庫,這也是經常提的緩存命中率問題。
解決:

  1. 採用布隆過濾器,將所有可能存在的數據哈希到一個足夠大的bitmap中,一個一定不存在的數據會被這個bitmap攔截掉,從而避免了對底層存儲系統的查詢壓力。
  2. 如果一個查詢返回的數據爲空(不管是數據不存在,還是系統故障),我們仍然把這個空結果進行緩存,但它的過期時間會很短,最長不超過五分鐘。通過這個直接設置的默認值存放到緩存,這樣第二次到緩衝中獲取就有值了,而不會繼續訪問數據庫。

三、緩存預熱

緩存預熱就是系統上線後,將相關的緩存數據直接加載到緩存系統。這樣就可以避免在用戶請求的時候,先查詢數據庫,然後再將數據緩存的問題!用戶直接查詢事先被預熱的緩存數據!
解決:

  1. 直接寫個緩存刷新頁面,上線時手工操作下;
  2. 數據量不大,可以在項目啓動的時候自動進行加載;
  3. 定時刷新緩存;

四、緩存更新

除了緩存服務器自帶的緩存失效策略之外(Redis默認的有6中策略可供選擇),我們還可以根據具體的業務需求進行自定義的緩存淘汰,常見的策略有兩種:

(1)定時去清理過期的緩存;

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

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

五、數據庫緩存如何保證一致性

1.先新數據庫再更新緩存

併發問題:更新數據庫後更新緩存可能會因爲多線程下導致寫入髒數據(比如線程A先更新數據庫成功,接下來要取更新緩存,接着線程B更新數據庫,但B又更新了緩存,接着B的時間片用完了,線程A更新了緩存)

2.先刪除緩存,然後再更新數據庫

併發問題:試想,兩個併發操作,一個是更新操作,另一個是查詢操作,更新操作刪除緩存後,查詢操作沒有命中緩存,先把老數據讀出來後放到緩存中,然後更新操作更新了數據庫。於是,在緩存中的數據還是老的數據,導致緩存中的數據是髒的,而且還一直這樣髒下去了。

3.先更新數據庫,成功後,讓緩存失效。

併發問題:比如,一個是讀操作,但是沒有命中緩存,然後就到數據庫中取數據,此時來了一個寫操作,寫完數據庫後,讓緩存失效,然後,之前的那個讀操作再把老的數據放進去,所以,會造成髒數據。
不過,實際上出現的概率可能非常低,因爲這個條件需要發生在讀緩存時緩存失效,而且併發着有一個寫操作。而實際上數據庫的寫操作會比讀操作慢得多,而且還要鎖表,而讀操作必需在寫操作前進入數據庫操作,而又要晚於寫操作更新緩存,所有的這些條件都具備的概率基本並不大。
可以通過2PC或是Paxos協議保證一致性解決

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章