您好?我是Jayon。
今天要來跟大家說明設定快取的依據。這篇文章是我在實務工作中撰寫的,所以僅供參考就好哈哈。
什麼是快取?
快取是指事先儲存稍後可能會被請求的結果,以便快速提供服務。也就是說,先將結果儲存起來,當稍後有請求時,直接從快取中取得結果,而不用再參考資料庫或 API。快取的出現,其背景是建立在帕雷托法則(Pareto principle)上。
帕雷托法則是指 80% 的結果是由 20% 的原因造成的,建議參考下圖!
也就是說,快取不需要儲存所有的結果,只要儲存 20% 使用頻率最高的結果,就能提升整體效率。
要快取哪些資料?
根據帕雷托法則,我們不能隨便快取任何資料,而必須要快取必要的資料。那麼,要快取哪些資料呢?
讀取頻繁但寫入很少的資料
理論上,大家經常會說「要快取讀取頻繁但寫入很少的資料」,但「讀取頻繁」和「寫入很少」的標準其實非常模糊。
因此,我通常會按照以下步驟調查要快取的資料。
- 透過 APM 工具(例如 DataDog)查看 RDB 查詢呼叫記錄的前 5 名。
- 從中找出查詢查詢,並確認它從哪個表格中取得資料。
- 確認該表格的更新查詢被呼叫了多少次。
透過這個流程,我們可以觀察哪些資料的查詢次數很多,但更新查詢次數很少。在我實務工作中觀察到的表格,查詢查詢每天發生 174 萬次,但更新查詢最多只有 500 次左右。這樣的情況,我想任何人都可以判斷它很適合快取吧哈哈。
對更新敏感的資料
對更新敏感的資料表示 RDB 和快取之間的差異必須很短。例如,與付款相關的資訊對更新非常敏感,因此即使符合上述快取條件,也需要考慮是否要套用。
我必須要針對符合這兩種特性的付款相關表格進行快取。因此,並非所有使用付款相關表格的邏輯都套用快取,而是決定僅在實際上不會發生付款的相對安全的邏輯中部分套用快取。
本地快取與全域快取
現在,我們已經大致確定要快取哪些資料以及快取的範圍。那麼,接下來就要考慮「在哪裡」儲存快取的資料。通常可以儲存在本地記憶體中,或者使用 Redis 等獨立的伺服器。
本地快取
本地快取是指將快取的資料儲存在應用程式伺服器的記憶體中,通常會使用 Guava 快取或 Caffeine 快取。
**優點**
- 在執行應用程式邏輯時,可以直接從同一台伺服器的記憶體中查詢快取,因此速度很快。
- 容易實作。
**缺點**
- 如果有多個執行個體,就會出現一些問題。
全域快取
全域快取是指另外設置一個伺服器來儲存快取資料,例如 Redis。
**優點**
- 執行個體之間可以共享快取,因此即使一個執行個體修改了快取,所有執行個體都可以取得相同的快取值。
- 即使出現新的執行個體,也可以直接參考現有的快取儲存庫,因此不需要重新放入快取。
**缺點**
- 需要透過網路傳輸,因此速度比本地快取慢。
- 需要另外設置快取伺服器,因此會產生基礎設施管理成本。
我選擇了哪一種?
目前公司應用程式伺服器採用多個執行個體的架構,但我選擇了本地快取。
主要有三個原因。
- 要快取的資料儲存在 RDB 中,大約有 4 萬筆,全部載入記憶體也只需要 4MB 以下。
- 需要提升付款相關資料的查詢效能。
- 雖然原本就有 Redis,但將新的快取儲存在 Redis 中會產生基礎設施成本。
如何更新快取?
如果應用程式伺服器有多個,而且套用了本地快取,那麼每個應用程式伺服器儲存的快取值可能會不同。例如,A 伺服器儲存的快取資料是「1」,但 B 伺服器儲存的快取資料由於 B 伺服器修改過,所以變成「2」。如果使用者向負載平衡器發送請求,就會從 A 伺服器和 B 伺服器取得不同的值。
因此,需要將每個執行個體的快取自動移除,並從 RDB 中查詢資料。這時,通常會使用 TTL。
TTL 要設定多久?
TTL 是 Time To Live 的縮寫,是指設定一段時間後自動刪除快取。例如,如果將 TTL 設定為 5 秒,那麼快取資料 5 秒後就會自動刪除。之後,如果發生快取遺漏,就會從 RDB 中查詢資料並儲存。
那麼,TTL 要設定多久呢?
**讀取/寫入都在同一個快取伺服器上發生**
如果讀取/寫入都在 Redis 等全域快取伺服器上發生,或者發生在套用本地快取的單一應用程式伺服器上,那麼 TTL 的值可以設定為小時或更長的時間。因為寫入時會修改舊的快取,而且從該快取取得資料的伺服器始終都會取得最新的資料。
這種情況下,也可以不設定 TTL,而是讓快取伺服器滿了之後,透過 LRU 演算法自動逐漸清空快取。
**讀取/寫入在多個快取伺服器上發生**
如果讀取/寫入在多個全域快取伺服器上發生,或者發生在套用本地快取的多個應用程式伺服器上,那麼最好將 TTL 設定為秒到分鐘的範圍。因為可能會讀取到尚未反映修改的快取伺服器上的舊資料。
此時,TTL 會根據各種情境決定,更新越重要,值變更的機率越高,TTL 就應該設定得越短;更新越不重要,值變更的機率越低,TTL 就可以設定得稍微長一點。
我如何設定 TTL?
我需要快取的資料是與付款相關的資料,即使在實際上不會發生付款的嚴格邏輯中不套用快取,但由於付款的特性,更新仍然很重要。不過,更新的可能性很低,因此我將 TTL 安全地設定為 5 秒。
結論
總結來說,我選擇的快取方式如下。
- 付款相關資料
- 查詢非常頻繁,但修改很少。
- 僅在實際上不會發生付款,但會發生查詢的邏輯中套用快取。
- 套用本地快取,並將 TTL 設定為 5 秒。
接下來要做的,就是針對實際套用的快取方式進行效能測試。目前我還在思考如何具體進行效能測試,之後會在後續的文章中撰寫!
评论0