前言(preface):
本篇是記錄學習cache快取策略的筆記,以及一些實作經驗。
基本快取概念(Concepts):
為了減輕Database的壓力以及提升系統效能,原理就是將可重複使用的資料存在記憶體中,這樣就能免去每次都需要跑去資料庫拿取又或是重新經過複雜計算的動作,類似殺雞焉用牛刀的概念。
快取的策略、模式
Cache Aside
最簡單又常見的一個,讀策略就是cache有就拿沒有就去資料庫拿拿完再放入cache,寫策略則是先更新完資料庫後刪除於快取的舊資料
可以注意的點是,此策略下是由應用程式(Application Layer)負責與快取、資料庫做互動,快取系統只被作為一個暫存資料的容器。
應用情境:
不適合寫多的情形,假設a發出讀取請求且未命中緩存的當下近乎同時b發出寫入請求,儘管資料庫是最新的資料但a有極大的機率拿到髒資料(也因此導致快取與DB資料不一致),原因是因為寫策略並沒有更新到緩存,所以通常遇到寫多的情形時會選擇其他策略又或是將緩存過期時間設置極短
優點:
- 易實現、不容易產生快取併發問題
- 讀多的情況下有極高的效能(廢話XD)資料庫負擔小
- 快取server負擔小(因為寫策略不用管那麼多,一有更新直接清掉快取就行)
缺點:
資料寫入操作變多時會漸漸失去使用快取的意義,因為一直刪快取不就等於大部分時間都要進DB重新拿資料嗎XD
我覺得滿有意義的思考點:
- 為什麼寫策略要先更新資料庫再刪除快取而不是先刪除快取再更新資料庫?
原因:讀寫併發時,會因為快取與資料庫操作之間耗時的差異導致數據不一致的問題
–>快取的寫入遠快於資料庫的寫入,所以規定好寫入規則為先更新資料庫再刪除快取就能解決快取、資料庫數據不一致的問題
鮮明的例子:
當A發出讀取某資料請求且快取未命中後,B發出對那筆資料寫入的請求,a從資料庫讀取到資料後的下一步便是將資料寫回快取(只是這個資料將會是髒資料),而對資料庫操作必是>>對快取的操作耗時,所以B更新資料庫這個操作絕對會比a將髒資料寫進緩存來得慢許多,也就是如果以先更新資料庫在刪除快取的規則順序下,讀寫併發也不太可能造成資料庫與快取不一致的問題(因為更新後下一步便是清除快取(髒資料掰掰)),反之,如果是先刪除在更新的順序下,可以發現資料庫更新會是最後完成的動作也就是說快取存到的是髒資料。
- 下面兩張附圖可以更清晰地看出這兩種規則順序造成的差異:
為什麼寫策略是刪除快取不是更新快取?
更新快取 這個行為相較刪除快取來的更耗效能(此處耗的是快取server),簡單舉例:一篇包含標題、內文、點讚數、留言數等等的文章快取,當多一個用戶留言或按讚時,實際上改變的資料只是一小部分,但為了做到更新快取那就需要將整筆資料query出來也就是額外的I/O消耗,反之刪除快取就不會有這額外的負擔。
這就是Cache Aside的特色,不用管那麼多也因此減輕了cache的壓力,不需每次資料一變動就要跑進資料庫拿又存進快取這樣的動作,但當然Trade off就是前面所提到的缺點。
Read/Write Through
概念上最主要的差異在於Write Through,此策略需要開發人員在快取層自行設計一套事務機制來確保快取和DB之間的數據一致性,因為應用程式(Application layer)不再會直接與資料庫溝通,轉而透過快取層做代理(可以看以下附圖更好理解)。在實作難度上也是差異頗大。
應用情境:
- 也不適合在寫多讀少,但相對cache aside來得更適合些@@
- 適合於金融相關的應用程式中使用(高資料一致性)
優點:
在提供資料一致性方面比Cache aside表現得更好
缺點:
- 不易實現
- 寫入性能相對較低(寫入延遲)
- 快取伺服器負載大(每一個寫入都要排隊—>同步)
對cache aside/Read/Write Through的小整理:
遇到寫入併發時,cache aside是依賴資料庫本身的事務管理機制去解決 而Read/Write Through策略下則是要開發人員在快取層中自行設計一套事務管理邏輯來處理
Read/Write Through相較於cache aside最大的優點就是讓資料庫與快取資料差異減少,不會像cache aside雖然資料庫都是最新的資料但快取必須要等待下一次read才會更新至最新
Read/Write Through 中cache作為應用程式與資料庫之間的管理者(自動化)而非像cache aside中cache只是單純作為一個存快取資料的容器
那到底誰適合寫多讀少的情境呢?
Ans:Write behind Cache
Write behind Cache最大的差別就在於將快取寫入資料庫的時機點不再像Write Through是同步的機制而是以非同步的方式將快取更新至資料庫,簡單來說就是,隔了一些時間再批量將緩存的資料存入資料庫(更白話就是改天在一起進資料庫啦),且是異步執行所以不會阻塞程式運作
應用情境:
- 寫多讀少
- 電商的購物車
- OS (Redis似乎並沒有提供此策略實作方法似乎都是被運用於OS設計中)
優點:
極大減輕資料庫壓力,尤其在高併發的情况下(因為數據庫更新是異步而非像Write-Tgrough是同步)
缺點:
因為在存入資料庫前的時間都是存在Cache,所以如果發生意外你的PC裂開了,你也裂開了(資料丟失風險)
Other issue
Cache avalanche
快取過期時間設置一致,導致時間一到同時全部失效,全都要進DB重拿,資料庫壓力突然喜馬拉雅山大
以隨機值當來當作 Expire time
Cache breakdown
某熱門快取非常巧合的過期了,在還未更新前一坨請求進來導致沒必要的瘋狂更新、查詢
保證只有一個線程可以去更新緩存,其他線程則等待
刻意設置為永久,cache滿了再透過比如message queue的方式去觸發清除的行為
Hotspot cache
熱點的數據導致cache撐不住(熱門到一台server不夠撐)
運用load balance的概念去分散壓力
THANKS WATCHING