详解Redis的内存淘汰策略

发布于:2025-06-21 ⋅ 阅读:(20) ⋅ 点赞:(0)

        Redis 的内存淘汰策略是当 Redis 的内存使用量达到配置的上限时,系统会根据预设的规则自动选择并删除一些键,以释放内存空间。Redis 提供了多种内存淘汰策略,每种策略都有其特定的适用场景和行为。

1. 不淘汰策略 (报错)

1.1 noeviction (默认策略):

  • 行为: 当内存不足时,所有会申请更多内存的命令(如 SETLPUSHSADD 等)都将返回错误(通常是 (error) OOM command not allowed when used memory > 'maxmemory')。

  • 适用场景: 数据绝对不能丢失,且开发者确信应用逻辑能处理内存不足错误(例如:优雅降级、清理逻辑、水平扩展)。生产环境使用需非常谨慎。

2.淘汰策略 - 在设置了过期时间的键中筛选

2.1 volatile-lru (Least Recently Used):

  • 行为: 从设置了过期时间 (EXPIRE/TTL) 的键中,淘汰最近最少使用的键。

  • 原理: 近似 LRU 算法(Redis 使用采样而非完全精确 LRU 以节省内存和 CPU)。

2.2 volatile-lfu (Least Frequently Used) (Redis 4.0+):

  • 行为: 从设置了过期时间的键中,淘汰访问频率最低的键。

  • 原理: 近似 LFU 算法,通过计数器和衰减机制估算访问频率。对突发访问后长期不用的键淘汰效果更好。

2.3 volatile-ttl (Time To Live):

  • 行为: 从设置了过期时间的键中,淘汰剩余生存时间 (TTL) 最短的键。

  • 原理: 优先移除即将过期的键,腾出空间给新键。适用于缓存场景,确保新缓存有空间。

2.4 volatile-random:

  • 行为: 从设置了过期时间的键中,随机淘汰一个键。

  • 原理: 简单随机选择。效率高但淘汰可能不合理。

3. 淘汰策略 - 在所有键中筛选:

3.1 allkeys-lru:

  • 行为: 从所有键(无论是否设置过期时间)中,淘汰最近最少使用的键。

  • 原理: 近似 LRU。适用于希望将 Redis 用作缓存的场景,认为所有数据都可被淘汰。

3.2 allkeys-lfu (Redis 4.0+):

  • 行为: 从所有键中,淘汰访问频率最低的键。

  • 原理: 近似 LFU。同样适用于纯缓存场景,且能更好地淘汰长期冷门数据。

3.3 allkeys-random:

  • 行为: 从所有键中,随机淘汰一个键。

  • 原理: 简单随机选择。效率最高但淘汰最不可控。

4. 关键机制详解

  • 近似算法 (Approximated Algorithms):

    • Redis 的 lru 和 lfu 都是近似实现,并非严格精确。这是为了在性能和内存消耗上取得平衡。

    • LRU 近似: Redis 维护一个候选池(默认大小 16),每次淘汰时从池中(而非全量数据)挑选最老的淘汰。新访问的键只有其“年龄”比池中最老的键还老时才会进入候选池。可通过 maxmemory-samples 配置候选池大小(增加提高精度但降低性能)。

    • LFU 近似: 使用 Morris 计数器(概率递增)和一个衰减时间(lfu-decay-time)来模拟访问频率。计数器值会随时间衰减,避免早期访问的键长期占据优势。通过 lfu-log-factor 控制计数器增长难度。

  • LFU vs LRU:

    • LRU: 只关心最近一次访问时间。一个过去访问频繁但现在冷门的键可能比一个新近访问但频率低的键更易被保留。

    • LFU: 关心整个生命周期内的访问频率。能更好地识别并保留真正的热点数据,即使它们最近没被访问(但历史上访问非常多),而淘汰那些偶尔访问或曾经热过但已过气的数据。更适合识别长期热点。

  • volatile-* vs allkeys-*:

    • volatile-*: 只淘汰有 TTL 的键。意味着没有设置过期时间的键永远不会被自动淘汰。如果这类键很多或很大,即使淘汰了所有过期键也可能无法释放足够内存,最终导致 noeviction 行为或 OOM。适用于有明确生命周期的缓存数据,同时存在不应丢失的永久数据。

    • allkeys-*: 所有键都是候选对象。没有设置过期时间的键也可能被淘汰。适用于纯缓存场景,所有数据都可以在需要时重新生成或从源头获取。

5. 如何选择策略?

  • 数据容忍度:

    • 数据绝对不能丢? -> noeviction (需确保有处理 OOM 的能力或足够内存)。

    • 所有数据都可重建? -> allkeys-lru 或 allkeys-lfu (推荐,最安全,避免永久键占满内存)。

    • 部分数据可丢(缓存),部分不能丢? -> volatile-* (需确保永久键有足够空间,否则仍可能 OOM)。

  • 访问模式:

    • 有明显热点数据? -> allkeys-lru / volatile-lru

    • 访问模式相对均匀,或需要淘汰长期冷门数据? -> allkeys-lfu / volatile-lfu (Redis 4.0+ 推荐)。

    • 访问模式完全随机或无关紧要? -> allkeys-random / volatile-random (效率最高)。

  • 数据过期特征:

    • 缓存项有明确且合理的 TTL? -> volatile-ttl 是一种高效选择,优先淘汰快过期的。

    • TTL 分布不均匀或没有意义? -> 避免 volatile-ttl

6. 总结与建议

  • 生产环境强烈推荐使用 allkeys-lru 或 allkeys-lfu (Redis 4.0+),除非有非常明确的理由不这么做。 这避免了未设置过期时间的键导致内存无法释放的风险。

  • allkeys-lfu 通常比 allkeys-lru 更能精准保留真正的热点数据。 如果使用 Redis 4.0 或更高版本,allkeys-lfu 是纯缓存场景的首选。

  • 谨慎使用 volatile-* 策略。必须确保永久键(未设置 TTL)的总大小远小于 maxmemory,并留出足够缓冲给缓存键,否则仍有 OOM 风险。

  • noeviction 仅适用于数据绝对不能丢失且应用能完美处理 OOM 错误的场景。大多数缓存场景不适用。

  • volatile-ttl 在缓存数据 TTL 设置合理且分布良好时效率很高。

  • 随机策略 (*-random) 效率最高但淘汰最不可控,一般只在其他策略效果不佳且性能压力极大时考虑。

  • 根据实际情况调整 maxmemory-samples (LRU/LFU 精度) 和 lfu-log-factor/lfu-decay-time (LFU 行为)。


网站公告

今日签到

点亮在社区的每一天
去签到