使用緩存的正確姿式

緩存是如今系統中必不可少的模塊,而且已經成爲了高併發高性能架構的一個關鍵組件。這篇博客咱們來分析一下使用緩存的正確姿式。sql

緩存能解決的問題

  • 提高性能數據庫

    絕大多數狀況下,select 是出現性能問題最大的地方。一方面,select 會有不少像 join、group、order、like 等這樣豐富的語義,而這些語義是很是耗性能的;另外一方面,大多    數應用都是讀多寫少,因此加重了慢查詢的問題。
    
    分佈式系統中遠程調用也會耗不少性能,由於有網絡開銷,會致使總體的響應時間降低。爲了挽救這樣的性能開銷,在業務容許的狀況(不須要太實時的數據)下,使用緩存是很是必要的事情。
  • 緩解數據庫壓力緩存

    當用戶請求增多時,數據庫的壓力將大大增長,經過緩存可以大大下降數據庫的壓力。

緩存的適用場景

  • 對於數據實時性要求不高微信

    對於一些常常訪問可是不多改變的數據,讀明顯多於寫,適用緩存就頗有必要。好比一些網站配置項。
  • 對於性能要求高網絡

    好比一些秒殺活動場景。

緩存三種模式

通常來講,緩存有如下三種模式:架構

  • Cache Aside 更新模式併發

  • Read/Write Through 更新模式異步

  • Write Behind Caching 更新模式分佈式

通俗一點來說就是,同時更新緩存和數據庫(Cache Aside 更新模式);先更新緩存,緩存負責同步更新數據庫(Read/Write Through 更新模式);先更新緩存,緩存定時異步更新數據庫(Write Behind Caching 更新模式)。這三種模式各有優劣,能夠根據業務場景選擇使用。ide

Cache Aside 更新模式

這是最經常使用的緩存模式了,具體的流程是:

  • 失效:應用程序先從 cache 取數據,沒有獲得,則從數據庫中取數據,成功後,放到緩存中。
  • 命中:應用程序從 cache 中取數據,取到後返回。
  • 更新:先把數據存到數據庫中,成功後,再讓緩存失效

mark

注意咱們上面所提到的,緩存更新時先更新數據庫,而後在讓緩存失效。那麼爲何不是直接更新緩存呢?這裏有一些緩存更新的坑,咱們須要避免入坑。

避坑指南一

先更新數據庫,再更新緩存。這種作法最大的問題就是兩個併發的寫操做致使髒數據。以下圖(以Redis和Mysql爲例),兩個併發更新操做,數據庫先更新的反然後更新緩存,數據庫後更新的反而先更新緩存。這樣就會形成數據庫和緩存中的數據不一致,應用程序中讀取的都是髒數據。

mark

避坑指南二

先刪除緩存,再更新數據庫。這個邏輯是錯誤的,由於兩個併發的讀和寫操做致使髒數據。以下圖(以Redis和Mysql爲例)。假設更新操做先刪除了緩存,此時正好有一個併發的讀操做,沒有命中緩存後從數據庫中取出老數據而且更新回緩存,這個時候更新操做也完成了數據庫更新。此時,數據庫和緩存中的數據不一致,應用程序中讀取的都是原來的數據(髒數據)。

mark

避坑指南三

先更新數據庫,再刪除緩存。這種作法其實不能算是坑,在實際的系統中也推薦使用這種方式。可是這種方式理論上仍是可能存在問題。以下圖(以Redis和Mysql爲例),查詢操做沒有命中緩存,而後查詢出數據庫的老數據。此時有一個併發的更新操做,更新操做在讀操做以後更新了數據庫中的數據而且刪除了緩存中的數據。然而讀操做將從數據庫中讀取出的老數據更新回了緩存。這樣就會形成數據庫和緩存中的數據不一致,應用程序中讀取的都是原來的數據(髒數據)。

mark

可是,仔細想想,這種併發的機率極低。由於這個條件須要發生在讀緩存時緩存失效,並且有一個併發的寫操做。實際上數據庫的寫操做會比讀操做慢得多,並且還要加鎖,而讀操做必需在寫操做前進入數據庫操做,又要晚於寫操做更新緩存,全部這些條件都具有的機率並不大。可是爲了不這種極端狀況形成髒數據所產生的影響,咱們仍是要爲緩存設置過時時間。

Read/Write Through 更新模式

在上面的 Cache Aside 更新模式中,應用代碼須要維護兩個數據存儲,一個是緩存(Cache),一個是數據庫(Repository)。而在Read/Write Through 更新模式中,應用程序只須要維護緩存,數據庫的維護工做由緩存代理了。

mark

Read Through

Read Through 模式就是在查詢操做中更新緩存,也就是說,當緩存失效的時候,Cache Aside 模式是由調用方負責把數據加載入緩存,而 Read Through 則用緩存服務本身來加載。

Write Through

Write Through 模式和 Read Through 相仿,不過是在更新數據時發生。當有數據更新的時候,若是沒有命中緩存,直接更新數據庫,而後返回。若是命中了緩存,則更新緩存,而後由緩存本身更新數據庫(這是一個同步操做)。

Write Behind Caching 更新模式

Write Behind Caching 更新模式就是在更新數據的時候,只更新緩存,不更新數據庫,而咱們的緩存會異步地批量更新數據庫。這個設計的好處就是直接操做內存速度快。由於異步,Write Behind Caching 更新模式還能夠合併對同一個數據的屢次操做到數據庫,因此性能的提升是至關可觀的。

但其帶來的問題是,數據不是強一致性的,並且可能會丟失。另外,Write Behind Caching 更新模式實現邏輯比較複雜,由於它須要確認有哪些數據是被更新了的,哪些數據須要刷到持久層上。只有在緩存須要失效的時候,纔會把它真正持久起來。

mark

總結

三種緩存模式的優缺點:

Cache Aside 更新模式實現起來比較簡單,可是須要維護兩個數據存儲,一個是緩存(Cache),一個是數據庫(Repository)。

Read/Write Through 更新模式只須要維護一個數據存儲(緩存),可是實現起來要複雜一些。

Write Behind Caching 更新模式和Read/Write Through 更新模式相似,區別是Write Behind Caching 更新模式的數據持久化操做是異步的,可是Read/Write Through 更新模式的數據持久化操做是同步的。優勢是直接操做內存速度快,屢次操做能夠合併持久化到數據庫。缺點是數據可能會丟失,例如系統斷電等。

緩存是經過犧牲強一致性來提升性能的。因此使用緩存提高性能,就是會有數據更新的延遲。這須要咱們在設計時結合業務仔細思考是否適合用緩存。而後緩存必定要設置過時時間,這個時間過短太長都很差,過短的話請求可能會比較多的落到數據庫上,這也意味着失去了緩存的優點。太長的話緩存中的髒數據會使系統長時間處於一個延遲的狀態,並且系統中長時間沒有人訪問的數據一直存在內存中不過時,浪費內存。

參考:左耳朵耗子《性能設計篇之"緩存"》



做者: 擼碼那些事

來源:http://songwenjie.cnblogs.com/
聲明:本文爲博主學習感悟總結,水平有限,若是不當,歡迎指正。若是您認爲還不錯,不妨點擊一下下方的 推薦按鈕,謝謝支持。轉載與引用請註明出處。

微信公衆號:
相關文章
相關標籤/搜索