Redis 將所有資料都儲存在記憶體中,這引發了一個明顯的問題:什麼機制能防止它填滿記憶體而導致當機?答案是兩種機制共同運作。過期機制讓金鑰在設定時間後自動刪除,這使得 Redis 作為快取時更安全。淘汰機制則決定當 Redis 達到記憶體限制時,要丟棄哪些資料。了解這兩種機制,才能讓快取靜默管理記憶體,而不會在最糟糕的時刻因記憶體不足錯誤而當機。
這是 Redis 大師班基礎模組的第 8 篇也是最後一篇,接續 sorted sets。
設定過期時間
您可以為金鑰附加存活時間(time-to-live),當時間到達時 Redis 會自動刪除它:
SET session:abc "..." EX 3600 # expires in 3600 seconds
EXPIRE user:1:cache 300 # set a 5-minute TTL on an existing key
TTL user:1:cache # seconds remaining, -1 if none, -2 if gone
PERSIST user:1:cache # remove the TTL, make it permanent
Enter fullscreen mode Exit fullscreen mode
過期機制是 Redis 成為安全快取的關鍵。將資料庫查詢結果快取並設定 5 分鐘 TTL,它會自動刷新:5 分鐘後金鑰消失,下一個請求會未命中快取並重新填入。您無需手動清理快取資料,也不會讓過時的項目永久累積。幾乎每個快取金鑰都應該有 TTL,因為沒有過期時間的快取只是最終會用光的記憶體。
過期機制實際如何運作
一個實用的細節:Redis 不會持續掃描過期的金鑰。它使用兩種機制。惰性過期(Lazy expiration)在有人嘗試存取金鑰時刪除過期金鑰(存取時會檢查 TTL,若已過期則移除)。主動過期(Active expiration)則執行背景工作,定期取樣金鑰並移除發現的過期金鑰。
實際結果是,過期的金鑰在被其中一種機制捕捉之前仍會佔用記憶體。通常這過程非常快速,但大量金鑰同時過期可能會短暫佔用記憶體並造成刪除作業的尖峰。將過期時間分散(在 TTL 中加入少量隨機性)可避免同時過期的「雷鳴群集」效應,這也有助於防止稍後會討論的快取雪崩問題。
記憶體限制及其發生時的處理
您可以使用 maxmemory 限制 Redis 的記憶體。這在生產環境中至關重要:沒有設定時,Redis 會盡情使用所有可用 RAM,然後作業系統會終止它。請設定足夠的餘量給作業系統和 Redis 本身的額外開銷:
maxmemory 4gb
maxmemory-policy allkeys-lru
Enter fullscreen mode Exit fullscreen mode
maxmemory-policy 決定 Redis 達到限制且新寫入需要空間時的處理方式。這就是淘汰政策,正確選擇它決定了是優雅處理還是發生錯誤。
淘汰政策
當 Redis 達到 maxmemory 時,政策決定要移除哪些資料(或是否讓寫入失敗):
- noeviction:達到上限後拒絕寫入並回傳錯誤。讀取仍可正常運作。適用於 Redis 存放無法承受遺失的資料(佇列、資料來源)時,寧願寫入失敗也不願丟棄資料。
- allkeys-lru:淘汰所有金鑰中最近最少使用的金鑰。這是純快取的標準選擇:保留熱門資料,丟棄冷門資料。
- allkeys-lfu:淘汰所有金鑰中使用頻率最低的金鑰。通常比 LRU 更適合快取,因為它會保留持續受歡迎的金鑰,而非僅最近被存取一次的金鑰。
- volatile-lru / volatile-lfu / volatile-ttl:僅淘汰已設定 TTL 的金鑰,依據最近最少使用、最低使用頻率或最接近過期時間。適用於在單一實例中混合快取資料(有 TTL、可淘汰)與持久資料(無 TTL、受保護)的情況。
選擇取決於您如何使用該實例。專用快取適合 allkeys-lru 或 allkeys-lfu。混合快取與重要資料的實例適合 volatile-* 政策,讓只有可捨棄的 TTL 金鑰會被淘汰。存放無法承受遺失資料的實例適合 noeviction,並搭配監控以在填滿前發出警示。
LRU 與 LFU 簡介
LRU 淘汰最近未被碰觸的金鑰;LFU 則淘汰碰觸頻率最低的金鑰。LFU(自 Redis 4 開始加入)通常是更好的快取政策,因為它能保護長期受歡迎的金鑰,避免被單次突發存取所淘汰。如果您正在執行快取且尚未刻意選擇,allkeys-lfu 是良好的預設值。LRU 依然適用且更為人熟知。
整合快取的實務應用
運作良好的 Redis 快取會結合上述所有機制:每個快取金鑰都設定 TTL,讓資料自動刷新且過時項目自我清理;設定 maxmemory 並保留餘量,讓 Redis 不會耗盡機器記憶體;使用 allkeys-lru 或 allkeys-lfu 政策,在記憶體吃緊時優雅地淘汰冷門資料。有了這三項機制,Redis 就能自行管理記憶體:熱門資料保留,冷門資料老化或被淘汰,實例可在無需手動清理或記憶體不足當機的情況下無限期運作。
過期與淘汰是讓記憶體儲存實用的安全機制。TTL 讓快取資料保持新鮮且自我清理;maxmemory 與淘汰政策讓 Redis 在資料超出 RAM 時仍能維持在限制範圍內。根據實例是純快取還是存放無法承受遺失的資料,刻意設定這兩項機制,Redis 就能在真實負載下保持健康。
這完成了基礎模組。下一階段,我們將開始快取模組,介紹最常見的快取使用方式——cache-aside 模式。
重點摘要
- TTL(
EX、EXPIRE)讓金鑰自動刪除,這是 Redis 成為安全快取的關鍵;幾乎每個快取金鑰都應該有 TTL。 - Redis 透過惰性過期(存取時)和主動過期(背景取樣)來淘汰金鑰,因此過期金鑰在被回收前仍會短暫佔用記憶體。
- 生產環境務必設定
maxmemory並保留餘量,否則作業系統最終會因 Redis 使用所有 RAM 而終止它。 maxmemory-policy決定淘汰方式:allkeys-lru/allkeys-lfu適用於快取,volatile-*適用於混合實例,noeviction適用於無法承受遺失的資料。- LFU 通常優於 LRU,因為它能保護持續受歡迎的金鑰,避免因單次突發存取而被淘汰。
常見問題
如何讓 Redis 金鑰過期?
使用 SET key value EX seconds 或對現有金鑰執行 EXPIRE key seconds 來設定 TTL。時間到達時 Redis 會自動刪除金鑰。使用 TTL 檢查剩餘時間,並使用 PERSIST 移除過期時間。
為什麼快取金鑰需要 TTL?
讓快取資料自動刷新,且過時項目不會永久累積。TTL 到期後金鑰消失,下一個請求會重新填入。沒有過期時間的快取只是最終會填滿的記憶體。
當 Redis 達到記憶體限制時會發生什麼事?
它會套用 maxmemory-policy:可能淘汰金鑰(LRU、LFU 或基於 TTL,適用於所有金鑰或僅有 TTL 的金鑰),或在 noeviction 政策下拒絕新寫入並回傳錯誤。設定 maxmemory 讓此行為受控,而非讓作業系統終止 Redis。
應該使用哪種淘汰政策?
純快取建議使用 allkeys-lru 或 allkeys-lfu。混合快取與持久資料的實例建議使用 volatile-* 政策,讓只有帶 TTL 的金鑰會被淘汰。無法承受資料遺失時,建議使用 noeviction 並搭配監控。LFU 通常是快取的最佳預設值。
LRU 與 LFU 淘汰有什麼差異?
LRU 淘汰最近最少存取的金鑰;LFU 則淘汰使用頻率最低的金鑰。LFU 通常更適合快取,因為它能保留持續受歡迎的金鑰,而非在其他金鑰發生單次突發存取後將它們丟棄。
延伸閱讀
This article was originally published on amanksingh.com/blog/redis-key-expiration-ttl.
關於作者
Aman Kumar Singh 是印度諾伊達的團隊主管兼資深軟體工程師,撰寫有關全端工程、系統設計,以及使用 TypeScript、Next.js、NestJS、PostgreSQL 和 Redis 建置生產級 SaaS 的文章。
- Portfolio: amanksingh.com
- GitHub: amansingh1501
- LinkedIn: amansingh1597
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.