Redis 作為高性能的內存數據庫,其內存資源的高效管理直接關系到系統的穩定性和性能。當 Redis 的內存使用達到配置的最大值(maxmemory)時,新的寫入操作將觸發內存淘汰機制(Eviction Policy),以釋放空間存儲新數據。本文將深入探討 Redis 的內存淘汰策略、實現原理、適用場景及最佳實踐。
一、 內存淘汰策略概述
Redis 的內存淘汰策略決定了在內存不足時,如何選擇需要刪除的鍵來釋放空間。這些策略可以分為兩大類:
- ?基于過期時間的淘汰?(volatile-*):僅針對設置了過期時間的鍵。
- 全局淘汰?(allkeys-*):針對所有鍵,無論是否設置過期時間。
Redis 支持以下 8 種內存淘汰策略:
?noeviction:默認策略,禁止寫入新數據,直接返回錯誤。
?volatile-lru:淘汰最近最少使用(LRU)的設置了過期時間的鍵。
?volatile-lfu:淘汰最不經常使用(LFU)的設置了過期時間的鍵。
?volatile-random:隨機淘汰設置了過期時間的鍵。
?volatile-ttl:優先淘汰剩余生存時間(TTL)最短的鍵。
?allkeys-lru:淘汰所有鍵中最近最少使用的鍵。
?allkeys-lfu:淘汰所有鍵中最不經常使用的鍵。
?allkeys-random:隨機淘汰任意鍵。
二、內存淘汰策略詳解
2.1 ?noeviction(不淘汰)?
?行為:當內存不足時,拒絕所有寫入命令(如 SET、LPUSH),但允許讀取操作。
?適用場景:適用于數據不可丟失的場景(如持久化存儲),需確保內存足夠或配合持久化機制。
?缺點:若內存不足且無持久化,可能導致服務不可用。
2.2 ?LRU(Least Recently Used)?
?原理:淘汰最近最久未被訪問的鍵。
?Redis 實現:Redis 使用近似 LRU 算法,通過隨機采樣(默認取 5 個鍵)選擇最久未使用的鍵,而非遍歷所有鍵,以減少計算開銷。
?適用場景:適用于緩存場景,優先保留熱點數據。
?命令示例:
CONFIG SET maxmemory-policy volatile-lru # 針對帶過期時間的鍵
CONFIG SET maxmemory-policy allkeys-lru # 針對所有鍵
2.3 ?LFU(Least Frequently Used)?
?原理:淘汰訪問頻率最低的鍵(Redis 4.0 引入)。
?Redis 實現:通過計數器統計鍵的訪問頻率,并隨時間衰減歷史計數,避免長期累積導致無法淘汰舊鍵。
?適用場景:適合長期緩存,如高頻訪問的靜態數據。
?命令示例:
CONFIG SET maxmemory-policy volatile-lfu # 針對帶過期時間的鍵
CONFIG SET maxmemory-policy allkeys-lfu # 針對所有鍵
2.4 ?TTL(Time To Live)?
?原理:優先淘汰剩余生存時間(TTL)最短的鍵。
?適用場景:適用于明確知道鍵生命周期的場景(如臨時會話數據)。
?限制:僅對設置了過期時間的鍵生效。
?命令示例:
CONFIG SET maxmemory-policy volatile-ttl
2.5 ?Random(隨機淘汰)?
?原理:隨機選擇鍵進行淘汰。
?適用場景:內存壓力大且數據重要性均等時,快速釋放內存。
?命令示例:
CONFIG SET maxmemory-policy volatile-random # 針對帶過期時間的鍵
CONFIG SET maxmemory-policy allkeys-random # 針對所有鍵
三、 內存淘汰的底層實現
3.1 ?LRU/LFU 的近似算法
- Redis 通過 ?evictionPoolEntry?結構維護候選淘汰鍵池。每次淘汰時,隨機采樣一組鍵,更新其訪問時間或頻率信息,選擇最不活躍的鍵刪除。
- ?LRU 時鐘:Redis 使用全局 24 位時鐘(精度為秒)記錄鍵的最近訪問時間。內存中每個對象存儲與全局時鐘的差值(lru字段),而非精確時間戳。
- ?LFU 計數器:每個鍵的 lru 字段被拆分為兩部分:
- 高 16 位:最近訪問時間的分鐘級精度。
- 低 8 位:訪問頻率計數器(0~255),通過概率遞增,隨時間衰減。
3.2 ?淘汰流程
- 客戶端執行寫入命令觸發內存檢查。
- Redis 檢查 maxmemory 是否已超出。
- 根據配置的策略選擇待淘汰鍵。
- 刪除鍵并觸發相關事件(如 evicted 通知)。
四、 如何選擇合適的內存淘汰策略?
4.1 ?緩存場景
?推薦策略:allkeys-lru 或 allkeys-lfu
?理由:優先保留熱點數據,最大化緩存命中率。
4.2 ?持久化存儲
?推薦策略:noeviction(需確保內存足夠或啟用持久化)。
?替代方案:若允許部分數據丟失,可使用 volatile-lru 結合過期時間。
4.3 ?臨時數據場景
?推薦策略:volatile-ttl
?理由:自動清理生命周期明確的數據(如驗證碼、會話信息)。
4.4 ?混合型數據
?推薦策略:allkeys-lru + 部分鍵設置過期時間。
?示例:電商系統中,商品詳情用 allkeys-lru 緩存,購物車數據設置 TTL。
五、最佳實踐與注意事項
5.1 ?配置建議
?設置合理的 maxmemory:通常為物理內存的 80%~90%,避免 OOM。
?監控內存使用:
INFO memory # 查看內存指標(used_memory、maxmemory)
INFO stats # 查看 evicted_keys(淘汰鍵數量)
5.2 ?避免大規模淘汰
?分片設計:通過集群分散數據,減少單個節點的內存壓力。
?預熱緩存:重啟后預加載高頻數據,避免冷啟動時集中淘汰。
5.3 ?常見誤區
?volatile-ttl 不依賴惰性刪除:該策略僅在內存不足時觸發,仍需依賴定期/惰性刪除清理過期鍵。
?LFU 計數器并非精確值:訪問頻率通過概率遞增,適用于相對比較而非絕對計數。
六、總結
Redis 的內存淘汰策略是平衡內存使用與性能的關鍵機制。理解不同策略的原理和適用場景,結合業務需求合理配置,可顯著提升系統的穩定性和效率。在高并發場景下,建議通過監控工具(如 RedisInsight、Prometheus)實時跟蹤內存和淘汰指標,動態調整策略和資源配置。
通過本文的深度解析,希望您能掌握 Redis 內存淘汰的核心機制,并在實踐中靈活運用,構建高效可靠的 Redis 服務。
參考資料
Redis 官方文檔:https://redis.io/docs/reference/eviction/
《Redis 設計與實現》——黃健宏
Redis 源碼解析(evict.c、object.c)