(聲明:本文部分內容摘自網絡優秀文章,版權歸原作者所有)
一、緩存方式
1 進程內緩存
直接在jvm虛擬機中緩存(堆、直接內存、磁盤3級緩存),速度快,效率高;但是緩存共享麻煩。
2 獨立緩存服務
redis、memcache均為緩存服務。
3 分布式緩存
指在每個程序中都有一個相同的緩存,同步更新有延時的。如ehcache集群,緩存共享復雜,維護不方便,所以不常用。
二、緩存的訪問模式
使用緩存時有幾種常見的訪問模式:
1 預留緩存(Cache-Aside)
應用程序 在訪問數據庫之前必須要先訪問緩存,如果緩存中包含了該數據則直接返回,不用再經過數據庫,否則應用程序必須要從先數據庫中取回數據,存儲在緩存中并且將數據返回,當有數據要寫入的時候,緩存內容必須要和數據庫內容保持一致。
這種方式是將數據庫與緩存通過客戶端應用程序主動管理來進行同步,這不是很好的使用方式。
2 Read-Through模式
相比上面的由客戶端應用程序來管理數據庫和緩存同步的方式,這種模式緩存會配有一個緩存中間件,該中間件來負責數據庫數據和緩存之間的同步問題。當我們應用要獲取緩存數據時,這個緩存中間件要確認緩存中是否有該數據,如果沒有,從數據庫加載,然后放入緩存,下次以后再訪問就可以直接從緩存中獲得。(緩存和數據庫 通常是一對一的關系,中間件需要維護這種業務關系)
3 Write-Through模式
這種模式就是緩存能夠感知數據的變化。也就是說在更新數據庫的同時也要更新緩存,該模式其實也是弱一致性,當數據庫更新數據失敗的時候,緩存不能繼續更新數據,要保持數據庫和緩存的最終一致性。
4 Write-behind模式
該模式是以緩存為優先,將緩存更新的數據存放隊列中,然后數據庫定時批量從隊列中取出數據更新數據庫。(Cassandra是否是該中模式,待確認)
三、緩存問題及解決方案
1 緩存穿透
1.1、 場景
應用程序 通常先檢查緩存中是否存在,如果存在直接返回緩存內容,如果不存在就直接查詢數據庫,然后再緩存查詢結果返回。這個時候如果查詢的某一個數據在緩存中一直不存在(1 數據庫中也不存在,如利用不存在的key頻繁攻擊;2 未加載至緩存或者緩存失效),就會造成每一次請求都查詢DB,這樣緩存就失去了意義,在流量大時,可能DB就掛掉了。
1.2、解決方案
可以在封裝的緩存SET和GET部分增加個步驟,如果查詢一個KEY不存在,就以這個KEY為前綴設定一個標識KEY;以后再查詢該KEY的時候,先查詢標識KEY,如果標識KEY存在,就返回一個協定好的非FALSH或者NULL值,然后應用程序 做相應的處理,這樣緩存層就不會被穿透。當然這個KEY的失效時間不能太長。
2 緩存并發
2.1、 場景
有時候如果網站并發訪問高,一個緩存如果失效,可能出現多個進程同時查詢DB,同時設置緩存的情況,如果并發確實很大,這也可能造成DB壓力過大,還有緩存頻繁更新的問題。
2.2、 解決方案
對緩存查詢加鎖,如果KEY不存在,就加鎖(如果是分布式,需要加分布式鎖),然后查DB入緩存,然后解鎖;其他進程如果發現有鎖就等待,然后等解鎖后返回數據或者進入DB查詢。
3 緩存失效
3.1、 場景
引起這個問題的主要原因還是高并發的時候,平時我們設定一個緩存的過期時間時,可能有一些會設置5分鐘啊,10分鐘這些;并發很高時可能會出在某一個時間同時生成了很多的緩存,并且過期時間都一樣,這個時候就可能引發一當過期時間到后,這些緩存同時失效,請求全部轉發到DB,DB可能會壓力過重。
3.2、 解決方案
將緩存失效時間分散開,比如我們可以在原有的失效時間基礎上增加一個隨機值,比如1-5分鐘隨機,這樣每一個緩存的過期時間的重復率就會降低,就很難引發集體失效的事件。
四、二級緩存
1 使用二級緩存好處
1.1 進程內緩存,無需IO和序列化,速度更快。適合小數量的熱點數據。
1.2 減少緩存數據的網絡傳輸開銷。
1.3 當集中式緩存出現故障的時候;Ehcache等本地緩存依然能夠支撐應用程序正常使用,增加了程序的健壯性。
1.4 使用二級緩存策略可以在一定程度上阻止緩存穿透問題。
2 使用二級緩存,注意點
2.1 堆內緩存,涉及GC。只能緩存少量熱點數據。
2.2 分布式環境中,各個應用中本地緩存的同步問題。
3 二級緩存方案
3.1 結構圖如下:

1、 redis做共享緩存,ehcache做本地緩存。
2、 本地緩存的 熱點數據key 集合,定期放入 redis 共享緩存中。
3、 程序重啟時,從共享緩存中獲取數據本地緩存。
3.2 實現集群環境中的本地緩存變更同步
(一)MQ廣播模式,實現分布式系統中的本地緩存同步
需消息持久化,以保證緩存程序宕機后,能接收到消息。
(二)基于zookeeper的watcher機制,
1)建立持久化節點/localCacheChange,本地緩存程序watch /localCacheChange節點的數據變更。
2)當緩存發生變化時,設置/localCacheChange節點數據為變更數據。
3)本地緩存程序,watch到/localCacheChange節點數據變更,將本地緩存更新。
(問題:以上方案,如果緩存程序宕機,無法watch到節點數據的變化。)