系統設計 - 應用系統緩存

在應用系統中,使用緩存不算非常難的事,但是設計好一套緩存策略比較麻煩,這樣既能起到好的緩存效果也能在合適的時候更新緩存。

今天我們聊聊緩存。

不過,在計算機科學中,有很多種緩存。我們先聊聊緩存的類型,再談應用系統中的緩存。

我們會涉及哪些緩存知識?

這裏我把緩存分爲兩類:

非應用緩存有:

非應用緩存通常不會侵入業務,比較透明,我們需要知道它們存在必要時做出配置。另外,對程序員來說更多的關心應用緩存,這些緩存往往和業務相關。

應用緩存有:

通常來說,分佈式緩存需要一套有效的緩存策略。回答:緩存哪種類型的對象?緩存的顆粒度如何?什麼時候去更新?

緩存對象顆粒度

對於後端服務來說,根據分層會有不同的 POJO(API 返回對象、領域對象、數據庫 PO),我們緩存什麼呢?實際項目中這幾種情況都會有。

爲了取得最好的緩存效果(命中率高,手動失效少),需要權衡被緩存對象的顆粒度。

緩存 API 返回對象(Response)

如果以 Response 爲粒度,其實是以用例爲視角。比如訂單詳情,需要組裝非常多的數據,且變化不劇烈。

特點是:

緩存領域對象聚合

如果使用 DDD 分層,有聚合概念,可以以聚合粒度緩存。

其特點是:

如果使用 Mybatis、Mybatis Plus 一般會定義自己的 PO 對象,所以可以單獨處理緩存策略。

它的特點是:

一般來說,緩存顆粒度越小,失效策略越好處理,但是緩存住的數據和邏輯就越少,在有些場景下不能滿足我們的期望。

另外,還有一些特殊場景的緩存。

一般來說:推薦使用聚合緩存;列表不緩存,使用讀寫分離從庫查詢;更新均不做緩存,只對單個查緩存。

緩存設計注意事項

緩存雪崩、緩存擊穿、緩存穿透

緩存雪崩是指在某一個時刻突然緩存都失效了。原因有兩種,一種是緩存服務器宕機了,流量全部進入數據庫;另外一種情況是在同一時刻失效了。

對於前者可以通過熔斷、高可用等設計,而後者需要對緩存過期時間加一個隨機偏移值,避免同時失效。

緩存擊穿和雪崩有點類似,業界往往說的是系統健康運行依賴某些熱點 key 的緩存,當這些熱點 key 失效後流量全部打到數據庫上。

爲了保證熱點 key 安全,在一些關鍵系統甚至會用多套 Redis 分級處理,或者將其設置爲永不過期,通過程序觸發更新的方式保證服務可用性。這種思想有點以前 CMS 站點的靜態化,將動態頁面輸出爲 HTML 頁面靜態化。

緩存穿透常常說的是,明明有 Redis 緩存但是偏偏大部分都不能命中進入到數據庫。有時候是因爲 key 設計不合理,導致命中率很低,其它情況有可能是遇到爬蟲或者攻擊,製造了大量的無效參數,這些參數不會命中緩存直接進入了數據庫查詢階段。

如果頻繁發生緩存穿透,剛好條件又合適可以採用返回空對象,避免回源到數據庫。

緩存更新策略

我們一般不會主動更新緩存,而是讓其失效,在下一次取數時如果沒有緩存則更新緩存。

緩存更新在不同場景下有幾種策略:

需要緩存的常見場景

序列化和反序列化坑

如何寫出方便緩存的代碼?

緩存友好的代碼,其本質是容易找到一個標識標記這組數據,這也是爲什麼列表頁不適合緩存的原因。

參考資料

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/3XfEQpBRaAWyV5rsZhpjkw