缓存级别与缓存更新问题
緩存失效問題被認為是計算機科學中最難的兩件事之一,這篇文章來自翻譯,內容主要包括緩存級別與緩存更新常見的幾種模式。
緩存應用模式
常見緩存應用模式
緩存常用來加快頁面的加載速度,減少服務器或數據庫服務的負載。緩存應用的常見模式如上圖所示:
數據庫常常得益于對均勻分布的數據的讀寫,但是熱點數據使得這種均勻被打破,從而出現了系統瓶頸。通過數據庫服務前置緩存服務,可以有效吸收不均勻的負載和抵擋流量高峰。
緩存級別
緩存級別關注的問題是在什么時候做緩存(When)以及在什么地方做緩存(Where),下面介紹幾種常見的緩存級別。
客戶端緩存
緩存可以存儲在客戶端(操作系統或瀏覽器、服務端、或者是獨立的緩存系統中。
CDN緩存
CDN也可以被認為是一種緩存。
Web服務器緩存
反向代理或者像Varnish這樣的緩存服務可以直接保存靜態的或動態的緩存內容。Web服務器也可以緩存請求直接響應客戶端從而避免請求再次觸達應用。
數據庫緩存
我們的數據庫服務在默認的配置或者稍微針對通用場景進行優化的情況下通常包含不同級別的緩存,針對特定的使用場景進行適當的調整可以進一步提高性能。
應用緩存
像Memcached和Redis這種內存key-value緩存服務,通常是置于應用和數據庫服務之間,因為數據存儲在內存中,因此這要比將數據存儲在磁盤的數據庫要快的多。但是內存與磁盤相比往往受限于空間,因此類似LRU(Least Recently Used)這種緩存淘汰算法應運而生,他們將相對較少訪問的”冷”數據從內存置換出來將訪問頻率較高的“熱”數據放入內存(將內存的使用價值最大化,譯者注)。
Redis還有很多其他的功能,包括:
- 持久化選項;
- 內建數據結構(如sets、lists);
下面是針對數據庫查詢級別和對象級別的一般緩存:
- 行級緩存;
- 查詢級別緩存;
- 完全序列化的緩存;
- 渲染后的HTML緩存;
值得一提的是,我們通常要避免文件級別的緩存,因為基于文件的緩存常常難于擴展和維護。
查詢級別緩存:
每當我們查詢數據庫的時候,將查詢(比如SQL)進行hash并作為key和查詢結果關聯存儲,這種方法會遇到緩存過期的問題:
- 對于復雜的查詢很難刪除緩存的結果;
- 緩存粒度較大,如果查詢結果中只有丁點數據被更新,則整個查詢都要過期;
對象級別緩存:
對象級別緩存是將數據看做對象:
- 如果數據被修改則將數據從緩存中移除;
- 使用異步的任務來更新緩存;
對象級別的緩存建議的使用場景:
- 用戶會話;
- 渲染后的頁面;
- 活動流;
- 用戶圖形數據;
緩存更新問題
因為內存受限于空間緩存只能存儲有限的數據,因此我們需要決定在我們的應用場景中,使用何種緩存更新策略,下面介紹幾種常見的模式。
Cache-Aside
Cache-Aside模式
應用負責基于存儲讀寫數據,緩存不直接和存儲打交道,應用的行為如下:
代碼示例如下:
| 1 2 3 4 5 6 7 | def get_user(self, user_id): ????user = cache.get("user.{0}", user_id) ????if user is None: ????????user = db.query("SELECT * FROM users WHERE user_id = {0}", user_id) ????????if user is not None: ????????????cache.set(key, json.dumps(user)) ????return user |
這種模式的缺點如下:Memcached通常被應用于這種方式,這種模式對于接下來的數據讀取將非???#xff0c;Cache-Aside也叫做延遲加載,只有需要的數據被緩存,避免不需要的數據占用緩存空間。
- 每次緩存沒命中都增加系統之間的交互,這將會增加響應延遲;
- 當對應數據庫中的數據被更新之后將出現臟數據問題,這個問題可以通過設置過期時間(TTL)來緩解,當時間過期將發生強制更新緩存;
- 當一個節點壞了之后,新的節點代替舊的節點,這個時候將出現大量的緩存穿透問題;
Write-Though
Write-Though模式
應用將緩存作為主要存儲,讀寫都直接和緩存打交道,緩存負責基于存儲進行讀寫:
應用代碼示例:
| 1 | set_user(12345, {"foo":"bar"}) |
緩存代碼如下:
| 1 2 3 | def set_user(user_id, values): ????user = db.query("UPDATE Users WHERE id = {0}", user_id, values) ????cache.set(user_id, user) |
Write-Though對于所有的寫操作都是比較慢的,但是對于讀來說很快,用戶通常需要容忍寫延遲,但是不會出現臟數據。
這種模式的缺點如下:
- 由于failure或者scaling帶來的新增節點的時候,新增節點在下次更新數據之前將沒有數據,這個問題可以結合Cache-Aside模式來緩解;
- 對于很多寫入的數據將永遠不會讀取到,這個問題可以通過設置過期時間解決;
Write-Behind(Write-Back)
Write-Behind模式
在這種模式下,應用的行為如:
這種模式的缺點如下:
- 如果在數據被更新到存儲之前緩存掛了,則數據將會丟失;
- 實現起來比Write-Though和Cache-Aside模式更為復雜;
Refresh-Ahead
Refresh-Ahead模式
我們可以配置緩存自動在最近訪問的數據過期之前更新它們,如果可以準確預測將要訪問的數據,Refresh-Ahead模式可以有效地減少讀寫的延遲。
這種模式的缺點如下:
- 如果預測數據不準確,則比不做什么更有損性能;
緩存的缺點
一種解決方案通常會帶來一些問題,我們來看看引入緩存帶來的問題:
- 緩存的引入帶來了一致性問題,我們需要處理緩存中的數據與原數據不一致的問題;
- 緩存的引入增加了軟件架構的復雜性;;
- 緩存過期是個難題,這個問題主要體現在何時更新緩存上;
擴展閱讀
- From cache to in-memory data grid
- Scalable system design patterns
- Introduction to architecting systems for scale
- Scalability, availability, stability, patterns
- Scalability
- AWS ElastiCache strategies
- Wikipedia
from:?http://www.importnew.com/23967.html
總結
以上是生活随笔為你收集整理的缓存级别与缓存更新问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTTPS协议原理分析
- 下一篇: 玩转Eclipse — 自动生成sett