Java高級編程——緩存三大矛盾問題總結

緩存三大矛盾問題總結

1、緩存實時性和一致性問題:當有了寫入後咋辦?

雖然使用了緩存,大家心裡都有一個預期,就是實時性和一致性得不到完全的保證,畢竟數據保存了多份,數據庫一份,緩存中一份,當數據庫中因寫入而產生了新的數據,往往緩存是不會和數據庫操作放在一個事務裡面的,如何將新的數據更新到緩存裡面,什麼時候更新到緩存裡面,不同的策略不一樣。

從用戶體驗角度,當然是越實時越好,用戶體驗越流暢,完全從這個角度出發,就應該有了寫入,馬上廢棄緩存,觸發一次數據庫的讀取,從而更新緩存。但是這和第三個問題,高併發就矛盾了,如果所有的都實時從數據庫裡面讀取,高併發場景下,數據庫往往受不了。

2、緩存的穿透問題:當沒有讀到咋辦?

為什麼會出現緩存讀取不到的情況呢?

第一:可能讀取的是冷數據,原來從來沒有訪問過,所以需要到數據庫裡面查詢一下,然後放入緩存,再返回給客戶。

第二:可能數據因為有了寫入,被實時的從緩存中刪除了,就如第一個問題中描述的那樣,為了保證實時性,當數據庫中的數據更新了之後,馬上刪除緩存中的數據,導致這個時候的讀取讀不到,需要到數據庫裡面查詢後,放入緩存,再返回給客戶。

第三:可能是緩存實效了,每個緩存數據都會有實效時間,過了一段時間沒有被訪問,就會失效,這個時候數據就訪問不到了,需要訪問數據庫後,再放入緩存。

第四:數據被換出,由於緩存內存是有限的,當使用快滿了的時候,就會使用類似LRU策略,將不經常使用的數據換出,所以也要訪問數據庫。

第五:後端確實也沒有,應用訪問緩存沒有,於是查詢數據庫,結果數據庫裡面也沒有,只好返回客戶為空,但是尷尬的是,每次出現這種情況的時候,都會面臨著一次數據庫的訪問,純屬浪費資源,常用的方法是,講這個key對應的結果為空的事實也進行緩存,這樣緩存可以命中,但是命中後告訴客戶端沒有,減少了數據庫的壓力。

無論哪種原因導致的讀取緩存讀不到的情況,該怎麼辦?是個策略問題。

一種是同步訪問數據庫後,放入緩存,再返回給客戶,這樣實時性最好,但是給數據庫的壓力也最大。

另一種方式就是異步的訪問數據庫,暫且返回客戶一個fallback值,然後同時觸發一個異步更新,這樣下次就有了,這樣數據庫壓力小很多,但是用戶就訪問不到實時的數據了。

3、緩存對數據庫高併發訪問:都來訪問數據庫咋辦?

我們本來使用緩存,是來攔截直接訪問數據庫請求的,從而保證數據庫大本營永遠處於健康的狀態。但是如果一遇到不命中,就訪問數據庫的話,平時沒有什麼問題,但是大促情況下,數據庫是受不了的。

一種情況是多個客戶端,併發狀態下,都不命中了,於是併發的都來訪問數據庫,其實只需要訪問一次就好,這種情況可以通過加鎖,只有一個到後端來實現。

另外就是即便採取了上述的策略,依然併發量非常大,後端的數據庫依然受不了,則需要通過降低實時性,將緩存攔在數據庫前面,暫且撐住,來解決。

Java高級編程——緩存三大矛盾問題總結

二、解決緩存三大矛盾的刷新策略

1、實時策略

所謂的實時策略,是平時緩存使用的最常用的策略,也是保持實時性最好的策略。

讀取的過程,應用程序先從cache取數據,沒有得到,則從數據庫中取數據,成功後,放到緩存中。如果命中,應用程序從cache中取數據,取到後返回。

寫入的過程,把數據存到數據庫中,成功後,再讓緩存失效,失效後下次讀取的時候,會被寫入緩存。那為什麼不直接寫緩存呢?因為如果兩個線程同時更新數據庫,一個將數據庫改為10,一個將數據庫改為20,數據庫有自己的事務機制,可以保證如果20是後提交的,數據庫裡面改為20,但是回過頭來寫入緩存的時候就沒有事務了,如果改為20的線程先更新緩存,改為10的線程後更新緩存,於是就會長時間出現緩存中是10,但是數據庫中是20的現象。

這種方式實時性好,用戶體驗好,是默認應該使用的策略。

2、異步策略

所謂異步策略,就是當讀取的時候讀不到的時候,不直接訪問數據庫,而是返回一個fallback數據,然後往消息隊列裡面放入一個數據加載的事件,在背後有一個任務,收到事件後,會異步的讀取數據庫,由於有隊列的作用,可以實現消峰,緩衝對數據庫的訪問,甚至可以將多個隊列中的任務合併請求,合併更新緩存,提高了效率。

當更新的時候,異步策略總是先更新數據庫和緩存中的一個,然後異步的更新另一個。

一是先更新數據庫,然後異步更新緩存。當數據庫更新後,同樣生成一個異步消息,放入消息隊列中,等待背後的任務通過消息進行緩存更新,同樣可以實現消峰和任務合併。缺點就是實時性比較差,估計要過一段時間才能看到更新,好處是數據持久性可以得到保證。

一是先更新緩存,然後異步更新數據庫。這種方式讀取和寫入都用緩存,將緩存完全擋在了數據庫的前面,把緩存當成了數據庫在用。所以一般會使用有持久化機制和主備的redis,但是仍然不能保證緩存不丟數據,所以這種情況適用於併發量大,但是數據沒有那麼關鍵的情況,好處是實時性好。

在實時策略扛不住大促的時候,可以根據場景,切換到上面的兩種模式的一個,算是降級策略。

3、定時策略

如果併發量實在太大,數據量也大的情況,異步都難以滿足,可以降級為定時刷新的策略,這種情況下,應用只訪問緩存,不訪問數據庫,更新頻率也不高,而且用戶要求也不高,例如詳情,評論等。

這種情況下,由於數據量比較大,建議將一整塊數據拆分成幾部分進行緩存,而且區分更新頻繁的和不頻繁的,這樣不用每次更新的時候,所有的都更新,只更新一部分。並且緩存的時候,可以進行數據的預整合,因為實時性不高,讀取預整合的數據更快。

Java高級編程——緩存三大矛盾問題總結


分享到:


相關文章: