系統架構設計:進程緩存和緩存服務,如何抉擇?

概述

我們所說的緩存分爲進程內部緩存(系統內部緩存)和 緩存服務(如 redis/memcache)。計算機服務從原來的單體結構,到多實例,到現在流行的微服務,緩存服務變得原來越流行了。  

進程緩存

先說說進程緩存,它將數據存儲在站點、服務的進程內。在 Web 的發展歷史上,這樣的方式備受歡迎。比如早期常用的. Net 的  System.Web.Caching.

這種實現載體很簡單,比如一個帶鎖的 HasTable,或者一個 List 對象。使用簡單便捷,能存儲數據、html 頁面片段、文件,甚至任何對象。

在單體結構的 Web 模式下,進程內緩存被開發到極致,大概流程如下圖:

 

與原先沒有緩存相比,進程內緩存的好處是,數據讀取不再直接訪問數據庫,先判斷緩存中是否存在,如果存在,則直接讀取,不存在則再去數據庫中取,同時寫入緩存。

這樣避免了每次的請求都走數據庫,減少網絡開銷和數據請求次數,提高了數據獲取效率,基本等同在內存中執行。

緩存的目的是爲了冷熱數據的隔離,對於頻繁被修改的數據,緩存的意義不是很大,比如微信用戶的實時步數。比較有價值的是那些不被頻繁修改且數據量較大的內容,比如系統字典、配置數據。

判斷是否需要創建緩存需要一定的依據,以下是我的團隊的策略,不一定適用,可以參考:

緩存的必要性:數據的變更是否過於頻繁,過於頻繁則可能導致緩存不斷重建,反而降低效率。評估方式:緩存的過期時間內沒被主動更新的量值應該超過 60%。

假設緩存時間:3600s

假設同一種類型緩存數據基數:6000 個

 6000 * 60% = 3600 的數據在一個小時內事務未更新,這樣的緩存價值更大。 

進程緩存的問題

在互聯網大潮下,隨着用戶量的激增,原來單體結構逐漸的向 Web 服務集羣發展,在多實例目標下,進程緩存的弊端越來越明顯。

比如緩存無法統一的問題。

如果站點和服務中的多個節點訪問統一的緩存服務(比如 redis 或者 memerche),數據統一存儲,數據的一致性就比較容易保障。

但如果是進程緩存,數據存儲在站點和服務的多個節點內,每個節點一個緩存,存儲多份,一致性就比較難保障。 

 如上圖,但是有個問題,Cache1、Cache1、Cache3 一致性難以保障,如果想保持緩存的一致性時,該怎麼辦呢?

一般有以下幾種方法:

1、單一服務節點通知其他服務節點,如果我們只是 Web Service1 在執行業務操作的時候修改數據庫,更新緩存,同時通知其他 Web Service

服務,其他 Web Service 接收到信息的時候,進行緩存更新。 

 2、 啓動 MQ 通知其他節點:如下圖,可以通過 MQ 通知其他節點。寫請求發生在 server1,在修改完自己緩存數據與數據庫中的數據之後,給 MQ 生產數據變化通知,

server2 和 server1 訂閱 MQ 消息,當消費到 MQ 信息的時候,也修改緩存數據。

 3、有一種簡單的方式,也可以解耦與 Web Server 的關係,就是直接放棄了 “實時一致性”,啓動一個獨立的進程服務,定時從後端拉取最新的數據,更新內存緩存。

 上述的幾種方法爲了保持數據的一致性,增加了一定的開銷,一方面緩存數據同步過程中會有出錯的風險;

另一方面實際上違背了緩存的原則:冷熱數據隔絕,有效的利用冷數據,減輕數據庫壓力,提升效率。如果緩存被頻繁修改或者同步,那緩存的價值就不大了。 

補充:1、2 兩種方式,實例越多,緩存冗餘越多,各緩存節點數據同步的原子性越難保證,一致性也就越難保證。

第 3 種方式:採用定時拉取本身已經放棄了數據的實時一致性。

**所以我們在以下這幾種情況下拋棄進程緩存,選用緩存服務:  **

1、Web 集羣下,包含多個實例,並且不允許業務數據的不一致性(我相信大部分業務不允許)

2、進程內緩存數據量較大,緩存內存空間不足,影響 Web 性能,可以考慮走緩存服務(緩存服務如 redis,一般獨立服務甚至集羣配置,支持超大量級)。

3、評估 value 大小、緩存內存空間、峯值 QPS、過期時間、緩存命中率、讀寫更新策略、key 值分佈路由策略、過期策略以及數據一致性方案,根據實際需要判斷是否走緩存服務。 

緩存服務

在互聯網分層架構中,最常用的 kv 結構的緩存是 redis。他有如下特點:

 1、它支持複雜數據結構

value 是字符串、哈希,列表,集合,有序集合這類複雜的數據結構。支持各種場景,如客戶訂單信息列表,用戶消息,帖子評論等。 

2、支持持久化

首先,redis 的所有數據都是保存在內存中,然後不定期的通過異步方式保存到磁盤上 (這稱爲 “半持久化模式”);

也可以把每一次數據變化都寫入到一個 append only file(aof) 裏面 (這稱爲 “全持久化模式”,效率會低一點)。 

但是我們儘量不要把 redis 當作數據庫用,如果真的需要持久化數據,建議可以走 MySQL:

2.1、redis 的定期快照不能保證數據不丟失

2.2、redis 的 AOF 會降低效率,並且不能支持太大的數據量  

3、具備高可用特性

redis 天然支持集羣功能,可以實現主動複製,讀寫分離。 官方也提供了 sentinel 集羣管理工具,能夠實現主從服務監控,故障自動轉移。 

4、存儲的內容比較大

String 類型:一個 String 類型的 value 最大可以存儲 512M,List、Set、Hash 類型:list 的元素個數最多爲 2^32-1 個,也就是 4294967295 個。 

5、 支持事務

操作都是原子性,對數據的更改要麼全部執行,要麼全部不執行。避免業務數據的不一致性。

緩存使用注意

1、Web 服務 單體模式轉爲多實例之後,我們將進程緩存升級爲緩存服務(redis),清清理了所有的緩存使用,都改成了對接 redis。但是有一些地方漏掉,因爲我們有 3 個實例,所以漏掉的那幾個地方,一旦修改某個數據之後,一會兒是新值,一會兒舊值,很神奇。

2、謹防緩存擊穿、雪崩的產生,這個我們有慘痛的教訓,後續來一篇專門分析下。


作者:翁智華

來源:https://www.cnblogs.com/wzh2010/p/13874206.html

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