關注公衆號 架構師之路很久,沈劍分享的很多幹貨,收益良多。結合工作需要整理一下緩存應用中需要注意的問題。
緩存無處不在,跨進程的緩存:瀏覽器本地緩存–>cdn–>webServer(Nginx)–>appServer(tomcat,jetty,WAS)–>緩存服務器–>DB。
進程內緩存,一級緩存,二級緩存。
緩存分類
本地緩存
- 特點
優點:store in jvm;本地訪問,快速訪問,省去網絡開銷;各節點獨立存儲,適合小量數據,只讀數據。
缺點:存儲量有限,一份數據複製多份,每個jvm一份,浪費內存,多節點間數據更新同步複雜,影響服務無狀態化,開發複雜度提高。 - 常見實現方式
HashMap
ConcurrentHashMap,
Ehcache- 使用場景
少量高頻只讀數據 - 緩存更新方式
應用啓動時,初始化緩存,緩存預熱。
讀緩存,過期則 單線程從DB獲取數據,存入本地緩存。
源數據更新,直接刪除緩存。 緩存只提供讀服務,不做緩存的更新修改。
多節點部署時,源數據更新,發消息到MQ,各個服務節點消費消息,刪除本地緩存。
- 使用場景
緩存服務-分佈式緩存服務
- Redis
- MemCache
- Apache Ignite
緩存常見誤用
現象: 緩存當數據管道使用,多個應用進程讀寫同一個緩存服務
- 使用不當原因
- 應用間耦合嚴重,緩存偏向於數據存儲層,存儲直接暴漏給外部系統,架構分層上不合理。
- 服務器間Rpc通信,暴漏接口,針對接口建立協議,而不是針對實現。
- 數據權限歸屬不清
- 系統讀寫速度不同,互相影響,資源使用存在爭搶問題。 - 解決方案
系統間通過服務接口約定交互協議。通過MQ解耦,緩存只是接口實現層面的事情。
現象:緩存雪崩
- 原因:緩存服務整體掛掉,導致流量壓垮源數據庫,導致源數據庫啓動不了。
- 解決辦法:
容量規劃、緩存水平切分,緩存局部掛掉,不影響整體。
緩存失效策略
限流方案
現象:調用方緩存數據
- 原因:開發人員原因
- 解決辦法:
服務方緩存,作爲服務的實現方式,對調用方隱藏。服務調用方只通過接口訪問數據,客戶端與緩存解耦合。
現象:緩存穿透
-
原因:訪問大量不存在的數據,導致回源流量過大,給DB帶來很大壓力。
-
解決辦法 :
緩存空對象。如果緩存未命中,而數據庫中也沒有這個對象,則可以緩存一個空對象到緩存。如果使用Redis,這種key需設置一個較短的時間,以防內存浪費。
緩存預測,預測key是否存在,如果緩存的量不大可以使用hash來判斷,如果量大可以使用布隆過濾器來做判斷. -
緩存更新策略
單線程獨立寫數據到緩存服務
緩存、源系統讀寫一致性問題
- WriteThrough
- ReadThrough
- 什麼是“Cache Aside Pattern”?
- 讀過程
先讀cache,再讀db
如果,cache hit,則直接返回數據
如果,cache miss,則訪問db,並將數據set回緩存 - 寫過程
第一步要操作數據庫,第二步操作緩存
緩存,採用delete淘汰,而不是set更新
寫過程–** 爭議的地方**
考慮DB,緩存服務都是集羣的情況:
1、先寫DB,後同步緩存
正常情況:
db主–>db從同步完成–>緩存讀新值,緩存存放最新的值。
異常情況:
db主寫–>緩存服務讀db從(舊值)–>db從同步新值。緩存存放舊值直到過期。
解決辦法:
業務需求不要求實時一致,可忽略不處理。
技術上解決寫時的不一致: - 第一種辦法:
1、寫數據時記錄key值到分佈式cache.預估數據同步時間段,設置key緩存有效期和同步時間一致。
2、讀數據時,先從分佈式cache查詢key是否存在,若存在,則數據正在同步中,讀節點爲髒數據,從主節點讀取,不存在,數據沒有修改,從節點讀取。
當然這種方案,依賴cache的高可用,只是縮小數據不一致的時間段,同時, - 另一種辦法
1、先寫DB,db節點數據同步的同時,異步發消息到MQ
2、緩存服務消費MQ內容,淘汰數據,從主DB同步最新數據。
此種辦法,依賴MQ的高可用,消息有序。
總之,不管那種方案,基礎服務,消息中間件的穩定性、可用性都是必須要有保障的,越基礎的越重要。寫服務數據主從同步期間,會出現短暫不一致,業務上是否可接受緩存數據的暫時不準確是首先要考慮的。