Java互聯網架構-高併發緩存架構你那些年遇到的踩坑故事

概述html

今天講的這個話題,我相信是衆多工程師和團隊的痛。從我剛開始工做,那時候構建本地緩存,到後續memcache, Redis的出現,到如今各類分佈式集羣的緩存,例如redis Cluster等產品的出現,緩存愈來愈發達和複雜了,緩存對咱們的系統也愈加重要,如今很難相信一個後端服務裏沒有緩存的存在。在這篇文章裏,我會和你們分享一下過去踩到的緩存踩坑故事,而後試圖給出一些解決方案,你們能夠一塊兒討論,最終拿出更好的方法。因爲篇幅有限,因此這裏的緩存討論,只侷限於後端服務的緩存,而且不涉及具體的框架。前端

從兩個角度看ajax

1、客戶端:redis

html緩存:數據庫

<meta http-equiv="Cache-Control"content="no-cache"/> 手機頁面一般在第一次加載後會進行緩存,而後每次刷新會使用緩存而不是去從新向服務器發送請求。 若是不但願使用緩存能夠設置no-cache。後端

<meta http-equiv="pragma" content="no-cache"><meta http-equiv="cache-control" content="no-cache"><meta http-equiv="expires" content="0">

ajax緩存:瀏覽器

cache:緩存

要求爲Boolean類型的參數,默認爲true(當dataType爲script時,默認爲false),設置爲false將不會從瀏覽器緩存中加載請求信息服務器

2、服務端架構

服務端確定是有緩存的,涉及CDN,redis。

案例1,緩存和DB的同步更新不在同一個事務裏而且沒有重試補償機制

爲了減小系統間的依賴,不一樣系統的數據更新每每不放在同一個事務裏,採用MQ來進行通訊。你們能夠看下圖,後臺系統CRM更新產品數據到DB,Product系統收到異步消息通知後,更新最新數據到緩存。這是一個最多見的緩存應用場景,我相信不少團隊都是這樣用的。在這個Case裏容易出現的問題在於,若是批處理任務收到消息後服務crash掉了,緩存沒有正常更新,就出現了與DB的數據不一樣步,前端系統一直不能讀到最新數據,致使業務異常。

Java互聯網架構-高併發緩存架構你那些年遇到的踩坑故事

解決方案:1. 失敗消息必定要創建必定時間間隔的重試機制

2. 系統要有緩存更新的報警機制,方便更新失敗或者重試超時後,能夠人工介入進行補償。

案例2, 同一數據被1個以上的服務執行寫操做,其中一個服務的緩存數據沒有版本控制

這也是兩個不一樣服務更新數據過程當中很常見的狀況,見下圖,CRM系統更新了某個用戶的Profile, 保存更新數據庫後,經過MQ通知用戶系統更新緩存,因爲是異步更新延遲,在緩存更新前,用戶系統收到前端的指令,讀取了當前緩存裏的用戶數據,作了修改,並更新到DB中。出現的結果就是數據庫裏的CRM的更新被錯誤覆蓋。

Java互聯網架構-高併發緩存架構你那些年遇到的踩坑故事

解決方案:緩存裏的數據有一個標誌位能夠做爲更新數據庫數據的依據(Update_time or Version), 若是緩存裏數據時間與數據庫時間不能匹配,意味着另一個服務更新了該數據,那麼就先從DB裏讀取最新數據版本,而後在新版本上提交數據。

案例3, 併發查詢緩存中同一數據,若是緩存沒命中,致使DB瞬時被打爆

作促銷活動的時候,存在大量用戶的併發訪問某一個特定商品,該商品數據緩存失效,或者作了數據更改,可是對應緩存尚未更新,那麼全部這些訪問將同時直接被做用到DB上。

Java互聯網架構-高併發緩存架構你那些年遇到的踩坑故事

解決方案:作一個計數器或者鎖(沒有特別複雜邏輯的話,能夠直接用HashMap),若是發現某個KEY緩存沒有命中,那麼在計數器+1, 而後訪問數據庫,拿到結果更新緩存,清理掉計數器中的key。 在這個過程當中,若是有第二個線程或者更多的線程須要訪問這個KEY時,發現計數器的值>1 或者被加鎖, 那麼wait, 直到計數器清理掉,固然,這個技術器閾值是能夠在配置文件裏配置的,不必定是1。

案例4, 緩存沒有設置默認值,被攻擊,緩存一直保持在被「穿透」狀態

這個狀況,和案例3比較相似,都是緩存沒法命中,但不同的地方在於,數據的KEY值是沒法控制的,因此無法簡單的用計數器和鎖來處理, 比方,被人爲攻擊,製造的大量的無效userID訪問。

解決方案:全部沒有在緩存的KEY,所有分配一個默認VALUE 「UNKOWN-KEY」 ,具體是什麼狀況下,將默認值分配給沒有命中的KEY, 這個能夠根據本身的業務系統來定,比方說,能夠根據特定的IP段,或者沒有命中的總次數等,而後咱們就能夠決定是否繼續訪問DB仍是直接返回默認值給前端,拒絕本次數據訪問。這種作法的核心在於,每次數據訪問,都會有緩存結果返回,根據系統的狀況來決定是否要進一步訪問DB。

總結,今天列舉的這幾個案例,概括起來,能夠總結爲如下幾點:

1. 保證緩存同步

2. 減小緩存併發

3. 杜絕緩存穿透

緩存與背後的DB是相互依存的關係,緩存系統的設計原則,就是將訪問的異常處理或者壓力盡量的前置處理掉,將DB還原成它最初原本的存儲功能。

轉自:

  頭條號:   Java小馬哥

相關文章
相關標籤/搜索