緩存在高並發場景下的常見問題

緩存一致性問題

當數據時效性要求很高時,需要保證緩存中的數據與數據庫中的保持一致,而且需要保證緩存節點和副本中的數據也保持一致,不能出現差異現象。這就比較依賴緩存的過期和更新策略。一般會在數據發生更改的時,主動更新緩存中的數據或者移除對應的緩存。

緩存併發問題

緩存併發問題通常發生在高併發的場景下, 當一個緩存key過期時,有大量的請求在獲取該緩存key, 多個請求同時發現緩存過期, 因此多個請求會同時訪問數據庫來查詢最新數據, 並且回寫緩存, 這樣會造成應用和數據庫的負載增加, 性能降低, 由於併發較高, 甚至可能導致數據庫被壓死.

那麼如何解決這個問題呢?

  1. 本地鎖. 與分佈式鎖類似,我們通過本地鎖的方式來限制只有一個線程去數據庫中查詢數據,而其他線程只需等待,等前面的線程查詢到數據後再訪問緩存。但是,這種方法只能限制一個服務節點只有一個線程去數據庫中查詢,如果一個服務有多個節點,則還會有多個數據庫查詢操作,也就是說在節點數量較多的情況下並沒有完全解決緩存併發的問題
  2. 分佈式鎖. 使用分佈式鎖,保證對於每個key同時只有一個線程去查詢後端服務,其他線程沒有獲得分佈式鎖的權限,因此只需要等待即可。這種方式將高併發的壓力轉移到了分佈式鎖,因此對分佈式鎖的考驗很大
  3. 軟過期. 軟過期指對緩存中的數據設置失效時間,就是不使用緩存服務提供的過期時間,而是業務層在數據中存儲過期時間信息,由業務程序判斷是否過期並更新,在發現了數據即將過期時,將緩存的時效延長,程序可以派遣一個線程去數據庫中獲取最新的數據,其他線程這時看到延長了的過期時間,就會繼續使用舊數據,等派遣的線程獲取最新數據後再更新緩存。也可以通過異步更新服務來更新設置軟過期的緩存,這樣應用層就不用關心緩存併發的問題了。

緩存穿透問題

在高併發場景下,如果某一個key被高併發訪問,沒有被命中,出於對容錯性考慮,會嘗試去從後端數據庫中獲取,從而導致了大量請求達到數據庫,而當該key對應的數據本身就是空的情況下,這就導致數據庫中併發的去執行了很多不必要的查詢操作,從而導致巨大沖擊和壓力。

緩存穿透通常是由惡意攻擊或者無意造成的

緩存穿透的解決辦法:

1.緩存空對象

對查詢結果為空的對象也進行緩存,如果是集合,可以緩存一個空的集合(非null),如果是緩存單個對象,可以通過字段標識來區分。這樣避免請求穿透到後端數據庫。同時,也需要保證緩存數據的時效性。

2.單獨過濾處理

我們通常將空值緩存起來,再次接收到同樣的查詢請求時,若命中緩存並且值為空,就會直接返回,不會透傳到數據庫,避免緩存穿透。當然,有時惡意襲擊者可以猜到我們使用了這種方案,每次都會使用不同的參數來查詢,這就需要我們對輸入的參數進行過濾,例如,如果我們使用ID進行查詢,則可以對ID的格式進行分析,如果不符合產生ID的規則,就直接拒絕,或者在ID上放入時間信息,根據時間信息判斷ID是否合法,或者是否是我們曾經生成的ID,這樣可以攔截一定的無效請求。

緩存雪崩問題

緩存雪崩指緩存服務器重啟或者大量緩存集中在某一個時間段內失效,給後端數據庫造成瞬時的負載升高的壓力,甚至壓垮數據庫的情況。

緩存雪崩是由緩存同時失效造成的

通常的解決辦法是對不同的數據使用不同的失效時間,甚至對相同的數據、不同的請求使用不同的失效時間. 比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個緩存的過期時間的重複率就會降低,就很難引發集體失效的事件。

緩存擊穿問題

對於一些設置了過期時間的key,如果這些key可能會在某些時間點被超高併發地訪問,是一種非常“熱點”的數據。這個時候,需要考慮一個問題:緩存被“擊穿”的問題,這個和緩存雪崩的區別在於這裡針對某一key緩存,前者則是很多key。

這個問題的解決辦法,網上的一些我也沒看太懂,實在不好意思了


分享到:


相關文章: