使用快取的正確姿勢

2022-02-13 13:14:51 字數 2782 閱讀 1531

快取是現在系統中必不可少的模組,並且已經成為了高併發高效能架構的乙個關鍵元件。這篇部落格我們來分析一下使用快取的正確姿勢。

一般來說,快取有以下三種模式:

通俗一點來講就是,同時更新快取和資料庫(cache aside 更新模式);先更新快取,快取負責同步更新資料庫(read/write through 更新模式);先更新快取,快取定時非同步更新資料庫(write behind caching 更新模式)。這三種模式各有優劣,可以根據業務場景選擇使用。

這是最常用的快取模式了,具體的流程是:

注意我們上面所提到的,快取更新時先更新資料庫,然後在讓快取失效。那麼為什麼不是直接更新快取呢?這裡有一些快取更新的坑,我們需要避免入坑。

避坑指南一

先更新資料庫,再更新快取。這種做法最大的問題就是兩個併發的寫操作導致髒資料。如下圖(以redis和mysql為例),兩個併發更新操作,資料庫先更新的反而後更新快取,資料庫後更新的反而先更新快取。這樣就會造成資料庫和快取中的資料不一致,應用程式中讀取的都是髒資料。

避坑指南二

先刪除快取,再更新資料庫。這個邏輯是錯誤的,因為兩個併發的讀和寫操作導致髒資料。如下圖(以redis和mysql為例)。假設更新操作先刪除了快取,此時正好有乙個併發的讀操作,沒有命中快取後從資料庫中取出老資料並且更新回快取,這個時候更新操作也完成了資料庫更新。此時,資料庫和快取中的資料不一致,應用程式中讀取的都是原來的資料(髒資料)。

避坑指南三

先更新資料庫,再刪除快取。這種做法其實不能算是坑,在實際的系統中也推薦使用這種方式。但是這種方式理論上還是可能存在問題。如下圖(以redis和mysql為例),查詢操作沒有命中快取,然後查詢出資料庫的老資料。此時有乙個併發的更新操作,更新操作在讀操作之後更新了資料庫中的資料並且刪除了快取中的資料。然而讀操作將從資料庫中讀取出的老資料更新回了快取。這樣就會造成資料庫和快取中的資料不一致,應用程式中讀取的都是原來的資料(髒資料)。

但是,仔細想一想,這種併發的概率極低。因為這個條件需要發生在讀快取時快取失效,而且有乙個併發的寫操作。實際上資料庫的寫操作會比讀操作慢得多,而且還要加鎖,而讀操作必需在寫操作前進入資料庫操作,又要晚於寫操作更新快取,所有這些條件都具備的概率並不大。但是為了避免這種極端情況造成髒資料所產生的影響,我們還是要為快取設定過期時間。

在上面的 cache aside 更新模式中,應用**需要維護兩個資料儲存,乙個是快取(cache),乙個是資料庫(repository)。而在read/write through 更新模式中,應用程式只需要維護快取,資料庫的維護工作由快取**了。

read through 模式就是在查詢操作中更新快取,也就是說,當快取失效的時候,cache aside 模式是由呼叫方負責把資料加載入快取,而 read through 則用快取服務自己來載入。

write through 模式和 read through 相仿,不過是在更新資料時發生。當有資料更新的時候,如果沒有命中快取,直接更新資料庫,然後返回。如果命中了快取,則更新快取,然後由快取自己更新資料庫(這是乙個同步操作)。

write behind caching 更新模式就是在更新資料的時候,只更新快取,不更新資料庫,而我們的快取會非同步地批量更新資料庫。這個設計的好處就是直接操作記憶體速度快。因為非同步,write behind caching 更新模式還可以合併對同乙個資料的多次操作到資料庫,所以效能的提高是相當可觀的。

但其帶來的問題是,資料不是強一致性的,而且可能會丟失。另外,write behind caching 更新模式實現邏輯比較複雜,因為它需要確認有哪些資料是被更新了的,哪些資料需要刷到持久層上。只有在快取需要失效的時候,才會把它真正持久起來。

三種快取模式的優缺點:

cache aside 更新模式實現起來比較簡單,但是需要維護兩個資料儲存,乙個是快取(cache),乙個是資料庫(repository)。

read/write through 更新模式只需要維護乙個資料儲存(快取),但是實現起來要複雜一些。

write behind caching 更新模式和read/write through 更新模式類似,區別是write behind caching 更新模式的資料持久化操作是非同步的,但是read/write through 更新模式的資料持久化操作是同步的。優點是直接操作記憶體速度快,多次操作可以合併持久化到資料庫。缺點是資料可能會丟失,例如系統斷電等。

快取是通過犧牲強一致性來提高效能的。所以使用快取提公升效能,就是會有資料更新的延遲。這需要我們在設計時結合業務仔細思考是否適合用快取。然後快取一定要設定過期時間,這個時間太短太長都不好,太短的話請求可能會比較多的落到資料庫上,這也意味著失去了快取的優勢。太長的話快取中的髒資料會使系統長時間處於乙個延遲的狀態,而且系統中長時間沒有人訪問的資料一直存在記憶體中不過期,浪費記憶體。

參考:左耳朵耗子《效能設計篇之"快取"》

shape使用正確姿勢

android中常常使用shape來定義控制項的一些顯示屬性,今天看了一些shape的使用,對shape有了大體的了解,稍作總結 先看下面的 solid 實心,就是填充的意思 android color指定填充的顏色 gradient 漸變 android startcolor和android en...

正確使用非同步函式的姿勢

原文 jakearchibald.com 2017 await 翻譯 瘋狂的技術宅 在編寫非同步函式時,await return與return await之間存在差異,選擇正確的處理方式非常重要。讓我們先從這個非同步函式開始 async function waitandmaybereject 複製 ...

正確使用執行緒池的姿勢

提示手動建立執行緒池,效果會更好哦。executorservice fixedthreadpool executors.newfixedthreadpool 10 inspection info 執行緒池不允許使用executors去建立,而是通過threadpoolexecutor的方式,這樣的處...