本文出自:https://thinkinjava.cn做者:莫那 魯道java
一般,咱們會使用緩存用於緩衝對 DB 的衝擊,若是緩存宕機,全部請求將直接打在 DB,形成 DB 宕機——從而致使整個系統宕機。redis
2 種策略(同時使用):數據庫
解釋 1:緩存查詢一個沒有的 key,同時數據庫也沒有,若是黑客大量的使用這種方式,那麼就會致使 DB 宕機。緩存
解決方案:咱們可使用一個默認值來防止,例如,當訪問一個不存在的 key,而後再去訪問數據庫,仍是沒有,那麼就在緩存裏放一個佔位符,下次來的時候,檢查這個佔位符,若是發生時佔位符,就不去數據庫查詢了,防止 DB 宕機。併發
解釋 2:大量請求查詢一個剛剛失效的 key,致使 DB 壓力倍增,可能致使宕機,但實際上,查詢的都是相同的數據。異步
解決方案:能夠在這些請求代碼加上雙重檢查鎖。可是那個階段的請求會變慢。不過總比 DB 宕機好。分佈式
解釋:多個客戶端寫一個 key,若是順序錯了,數據就不對了。可是順序咱們沒法控制。ide
解決方案:使用分佈式鎖,例如 zk,同時加入數據的時間戳。同一時刻,只有搶到鎖的客戶端才能寫入,同時,寫入時,比較當前數據的時間戳和緩存中數據的時間戳。spa
解釋:連續寫數據庫和緩存,可是操做期間,出現併發了,數據不一致了。blog
一般,更新緩存和數據庫有如下幾種順序:
三種方式的優劣來看一下:
先更新數據庫,再更新緩存。
這麼作的問題是:當有 2 個請求同時更新數據,那麼若是不使用分佈式鎖,將沒法控制最後緩存的值究竟是多少。也就是併發寫的時候有問題。
先刪緩存,再更新數據庫。
這麼作的問題:若是在刪除緩存後,有客戶端讀數據,將可能讀到舊數據,並有可能設置到緩存中,致使緩存中的數據一直是老數據。
有 2 種解決方案:
總的來說,比較麻煩。
先更新數據庫,再刪除緩存
這個實際是經常使用的方案,可是有不少人不知道,這裏介紹一下,這個叫 Cache Aside Pattern,老外發明的。若是先更新數據庫,再刪除緩存,那麼就會出現更新數據庫以前有瞬間數據不是很及時。
同時,若是在更新以前,緩存恰好失效了,讀客戶端有可能讀到舊值,而後在寫客戶端刪除結束後再次設置了舊值,很是巧合的狀況。
有 2 個前提條件:緩存在寫以前的時候失效,同時,在寫客戶度刪除操做結束後,放置舊數據 —— 也就是讀比寫慢。設置有的寫操做還會鎖表。
因此,這個很難出現,可是若是出現了怎麼辦?使用雙刪!!!記錄更新期間有沒有客戶端讀數據庫,若是有,在更新完數據庫以後,執行延遲刪除。
還有一種可能,若是執行更新數據庫,準備執行刪除緩存時,服務掛了,執行刪除失敗怎麼辦???
這就坑了!!!不過能夠經過訂閱數據庫的 binlog 來刪除。