緩存基本原理

  • 緩存的作用不僅在於更快地獲取到數據,還可以避免源站被大量請求擊穿(例如數據庫)
  • 從緩存視角看URL到IP的映射過程
    • 先查詢瀏覽器內部的“域名-IP”緩存,如果曾經使用該瀏覽器訪問過這個域名,那麼很有可能命中緩存
    • 接着會查詢操作系統是否存在緩存,例如/etc/hosts文件可以自定義域名到IP的映射緩存
    • 最後會遞歸查詢DNS服務器,每一次都可能命中緩存

緩存應用模式

cache-aside

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-AOW9OOcm-1587457566676)(C:\Users\35135\Desktop\markdown\1587347216610.png)]

  • 數據更新時必須先更新數據庫,而不是先令緩存失效,這個順序不能倒過來
    • 如果先令緩存失效,那麼在數據庫更新成功前,如果有另外一個請求訪問了緩存,發現緩存數據庫已經失效,於是就會按照數據獲取策略,從數據庫中使用這個已經陳舊的數值去更新緩存中的數據
    • 這就導致這個過期的數據會長期存在於緩存中,最終導致數據不一致的嚴重問題。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-BbSXJAju-1587457566687)(C:\Users\35135\Desktop\markdown\1587347390248.png)]

  • 數據庫更新後,需要令緩存失效,而不是更新緩存
    • 如果兩個幾乎同時發出的請求分別要更新數據庫中的值爲 A 和 B,如果結果是 B 的更新晚於 A,那麼數據庫中的最終值是 B。但是,如果在數據庫更新後去更新緩存,而不是令緩存失效,那麼緩存中的數據就有可能是 A,而不是 B
    • 因爲數據庫雖然是“更新爲 A”在“更新爲 B”之前發生,但如果不做特殊的跨存儲系統的事務控制,緩存的更新順序就未必會遵從“A 先於 B”這個規則,這就會導致這個緩存中的數據會是一個長期錯誤的值 A
    • 如果是令緩存失效,這個問題就消失了。因爲 B 是後寫入數據庫的,那麼在 B 寫入數據庫以後,無論是寫入 B 的請求讓緩存失效,還是併發的競爭情形下寫入 A 的請求讓緩存失效,緩存反正都是失效了。那麼下一次的訪問就會從數據庫中取得最新的值,並寫入緩存,這個值就一定是 B

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-Sq6spasf-1587457566691)(C:\Users\35135\Desktop\markdown\1587347543126.png)]

read/write-through

  • 好處在於對(ORM)框架的使用者來說,並不需要關心緩存和數據庫之間的一致性是怎麼維護的

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gUaMsJB3-1587457566697)(C:\Users\35135\Desktop\markdown\1587354615077.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KC1wdieF-1587457566705)(C:\Users\35135\Desktop\markdown\1587348073586.png)]

緩存使用的問題

緩存穿透

  • 緩存穿透是在大量對於同一個數據的訪問,經過了緩存屏障,但是緩存卻未能起到應有的保護作用
  • 例如對某一個 key 的查詢,如果數據庫裏沒有這個數據,那麼緩存中也沒有數據的存放,每次請求到來都會去查詢數據庫,緩存根本起不到應有的作用
    • 可以在緩存中存放key不存在這個結果
    • 也可以使用布隆過濾器,在數據庫查詢前過濾不存在的結果
  • 如果緩存失效,而數據庫查詢過程比較慢,大量同一數據的請求幾乎同時到來,就會全部穿透緩存,一併落到了數據庫上,而最早的那個請求引發的緩存回填還沒有發生,在這種情況下數據庫直接就掛掉了,雖然緩存的機制本身看起來並沒有任何問題
    • 可以採用流量控制的方式,限制對於同一數據的訪問,必須等到前一個完成以後,下一個才能進行,即如果緩存失效而引發的數據庫查詢正在進行,其它請求就得等着
      • 等待機制可能較爲複雜,且有可能影響用戶體驗
    • 另一種方法是緩存預熱,在大批量請求到來以前,先主動將該緩存填充好
      • 侷限性是需要提前知道哪些數據可能引發緩存穿透的問題

緩存雪崩

  • 原本起屏障作用的緩存,如果在一定的時間段內,對於大量的請求訪問失效,即失去了屏障作用,造成它後方的系統壓力過大,引起系統過載、宕機等問題,就叫做緩存雪崩
    • 例如機房突然斷電,在恢復的時候把網頁服務器都通上了電,這時候緩存服務幾乎還沒有緩存數據,緩存命中率幾乎爲零,於是大量的請求衝向數據庫,直接把數據庫沖垮了。外在的表現就是,斷電導致網站無法提供服務,短期內訪問恢復,隨後又喪失服務能力
  • 對於上述類型雪崩,最常見的解決方案就是限流和預熱
    • 前者保證了請求大量落到數據庫的時候,系統只接納能夠承載的數量;而後者則在請求訪問前,先主動地往內存中加載一定的熱點數據,這樣請求到來的時候,緩存不是空的,已經具有一定的保護能力了
  • 另外一個常見的緩存雪崩場景是:緩存數據通常都有過期時間的,如果緩存加載的時間比較集中,那麼很可能到了某一時間點,大量的緩存就會同時過期,於是對應這些數據的請求全部落到了後面的數據庫上,從而造成系統崩潰
    • 這個問題解決起來也不難,那就是避免緩存集中寫入的時間,如果無法避免,就使用一個範圍隨機數來均勻地分散過期時間,從而打散緩存過期對系統造成的壓力

LRU緩存算法的缺陷

  • 如果用戶有意無意地訪問一些錯誤信息,就會破壞掉這個 LRU 隊列中最近訪問數據的真實性
    • 例如由於搜索引擎的多個並行爬蟲在短時間內訪問網站並抓取一些冷門頁面,這時候這個 LRU 隊列中就存儲了相關的冷門數據信息。接着網站活動開啓的時間到了,用戶量很快就上來了,這時候大量的數據訪問全部穿透緩存,導致數據庫壓力劇增,網站響應時間一下就飆升到了告警線之上
  • 解決方法
    • 主緩存隊列排的是“第 K 次訪問的元素”,也就是說,如果訪問次數小於 K,則在另外的一個“低級”隊列中維護,這樣就保證了只有到達一定的訪問下限纔會被送到主 LRU 隊列中
    • 這種方法保證了偶然的頁面訪問不會影響網站在 LRU 隊列中應有的數據分佈
    • 再進一步優化,可以將兩級隊列變成更多級,或者是將低級隊列的策略變成 FIFO(2Q 算法)等等

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章