Redis八种淘汰策略与两种淘汰模式

4 阅读9分钟

Redis 淘汰策略详解

引言

Redis 作为一款高性能的内存数据库,广泛应用于缓存、消息队列等场景。由于内存资源有限,当 Redis 的内存使用量达到上限时,就需要通过淘汰策略来清理部分数据,以腾出空间存储新数据。本文将详细讲解 Redis 的淘汰策略,解答 fast 模式和 slow 模式是否属于淘汰策略,并通过模拟面试场景深入剖析相关知识点。

Redis 的淘汰策略有哪些?

Redis 提供了以下八种数据淘汰策略,分为主动淘汰被动淘汰两种场景。这些策略主要由 maxmemory-policy 参数配置,决定了当内存不足时如何选择要删除的键。

1. noeviction

  • 描述:不淘汰任何数据。当内存不足时,新写入操作会返回错误(OOM,Out Of Memory)。
  • 适用场景:适用于数据绝对不能丢失的场景,例如金融系统中的关键数据。
  • 特点:内存满后,写操作失败,读操作不受影响。

2. allkeys-lru

  • 描述:对所有键使用 LRU(Least Recently Used,最近最少使用) 算法,优先淘汰最近最少使用的键。
  • 适用场景:适用于热点数据频繁访问的场景,例如缓存系统。
  • 特点:通过访问时间判断键的“活跃度”,优先删除不活跃的键。

3. allkeys-lfu

  • 描述:对所有键使用 LFU(Least Frequently Used,最不经常使用) 算法,优先淘汰访问频率最低的键。
  • 适用场景:适合数据访问频率差异较大的场景,优先保留高频访问的数据。
  • 特点:基于访问频率而非单一时间戳,适合长期运行的系统。

4. allkeys-random

  • 描述:从所有键中随机选择键进行淘汰。
  • 适用场景:适用于对数据无明显访问模式的场景,或者测试环境。
  • 特点:实现简单,性能开销低,但可能误删高价值数据。

5. volatile-lru

  • 描述:仅对设置了过期时间(TTL)的键使用 LRU 算法进行淘汰。
  • 适用场景:适用于部分键有明确生命周期的场景,例如临时缓存。
  • 特点:不会影响未设置过期时间的键,适合混合数据场景。

6. volatile-lfu

  • 描述:仅对设置了过期时间的键使用 LFU 算法进行淘汰。
  • 适用场景:与 volatile-lru 类似,但更注重访问频率。
  • 特点:结合了 TTL 和访问频率的双重筛选。

7. volatile-random

  • 描述:从设置了过期时间的键中随机选择键进行淘汰。
  • 适用场景:适用于设置了 TTL 的键无明显访问模式的场景。
  • 特点:简单高效,但随机性可能导致重要数据被删除。

8. volatile-ttl

  • 描述:从设置了过期时间的键中,优先淘汰剩余存活时间(TTL)最短的键。
  • 适用场景:适用于希望优先清理即将过期的键的场景。
  • 特点:基于过期时间排序,逻辑清晰,适合短生命周期数据。

配置方式

在 Redis 配置文件(redis.conf)中或通过 CONFIG SET 命令设置:

CONFIG SET maxmemory-policy allkeys-lru

默认策略为 noeviction

Fast 模式和 Slow 模式是淘汰策略吗?

答案:不是淘汰策略

Fast 模式和 Slow 模式并不是 Redis 的淘汰策略,而是 Redis 在执行内存淘汰时的算法运行模式,决定了淘汰算法的执行效率和精度。它们由 maxmemory-eviction-tenacity 参数控制(Redis 7.0 及以上版本引入)。

理解 Fast 模式和 Slow 模式

  • Fast 模式

    • 特点:快速执行,采样较少的数据(通常是少量随机样本)。
    • 优点:性能开销低,适合高并发场景。
    • 缺点:精度较低,可能导致次优的淘汰选择。
    • 适用场景:对淘汰精度要求不高,但需要快速响应的场景。
  • Slow 模式

    • 特点:采样更多的数据,执行更精确的算法(如 LRU/LFU 的完整计算)。
    • 优点:淘汰选择更准确,能更好地保留高价值数据。
    • 缺点:性能开销较高,可能影响响应速度。
    • 适用场景:对数据保留精度要求高的场景。

参数控制

maxmemory-eviction-tenacity 的值范围为 0 到 100:

  • 0:始终使用 Fast 模式。
  • 100:始终使用 Slow 模式。
  • 中间值(如 10、50):根据内存压力动态选择 Fast 或 Slow 模式,值越大越倾向于 Slow 模式。

如何记忆 Fast 和 Slow 模式?

  • 类比记忆

    • Fast 模式就像“快餐”,追求速度,牺牲精度。
    • Slow 模式像“精致料理”,追求质量,耗时更长。
  • 场景联想

    • 高并发、实时性要求高的场景(如秒杀系统)用 Fast 模式。
    • 数据价值高、访问模式复杂的场景(如推荐系统缓存)用 Slow 模式。
  • 口诀

    • “Fast 快而糙,省资源;Slow 慢而准,保价值。”

模拟面试:Redis 淘汰策略的拷打

以下是一个模拟的面试场景,面试官逐步深入提问,答案详细且结构化,涵盖技术细节和实际应用。

面试官:Redis 的内存淘汰策略有哪些?分别适用于什么场景?

候选人
Redis 提供了八种淘汰策略,分为两类:针对所有键(allkeys)和仅针对设置了过期时间的键(volatile)。具体如下:

  1. noeviction:不淘汰,内存满时拒绝写入,适合数据不可丢失的场景,如金融数据存储。
  2. allkeys-lru:对所有键使用 LRU 算法,适合热点数据频繁访问的缓存系统。
  3. allkeys-lfu:对所有键使用 LFU 算法,适合访问频率差异大的场景,如推荐系统。
  4. allkeys-random:随机淘汰所有键,适合无明显访问模式的测试环境。
  5. volatile-lru:对设置了 TTL 的键使用 LRU,适合混合数据场景,如临时缓存。
  6. volatile-lfu:对设置了 TTL 的键使用 LFU,适合关注频率的临时数据。
  7. volatile-random:随机淘汰设置了 TTL 的键,适合简单临时数据场景。
  8. volatile-ttl:优先淘汰 TTL 最短的键,适合短生命周期数据。

这些策略通过 maxmemory-policy 配置,默认是 noeviction

面试官:LRU 和 LFU 的区别是什么?Redis 是如何实现 LRU 的?

候选人

  • LRU(Least Recently Used) :根据最近访问时间淘汰,优先删除最久未访问的键。适合热点数据场景。
  • LFU(Least Frequently Used) :根据访问频率淘汰,优先删除访问次数最少的键。适合长期运行、频率差异大的场景。

Redis 的 LRU 实现
Redis 并未实现严格的 LRU,而是采用近似 LRU算法,具体步骤:

  1. 采样:从键空间中随机抽取一小部分键(默认 5 个,可通过 maxmemory-samples 配置)。
  2. 时间戳记录:每个键维护一个访问时间戳(基于 LRU_CLOCK)。
  3. 比较淘汰:比较采样键的访问时间,淘汰最旧的键。
  4. 优化:Redis 使用一个全局 LRU 时钟(精度为 1 秒),减少维护开销。

这种近似 LRU 算法在性能和精度间取得平衡,采样越多,精度越高,但开销也越大。

面试官:Fast 模式和 Slow 模式是什么?它们会影响淘汰效果吗?

候选人
Fast 模式和 Slow 模式不是淘汰策略,而是 Redis 执行淘汰算法时的运行模式,影响淘汰的效率和精度:

  • Fast 模式:采样少量数据,快速执行,适合高并发场景,但精度较低。
  • Slow 模式:采样更多数据,执行更精确的算法(如完整的 LRU/LFU 计算),适合数据价值高的场景。

影响

  • Fast 模式可能导致次优淘汰(误删高价值数据),但响应快。
  • Slow 模式淘汰更精准,但性能开销大。
  • 通过 maxmemory-eviction-tenacity(0 到 100)控制,0 为纯 Fast,100 为纯 Slow,中间值动态调整。

实际应用

  • 高并发场景(如电商秒杀)用 Fast 模式。
  • 高精度场景(如推荐系统缓存)用 Slow 模式。

面试官:如果我要实现一个自定义淘汰策略,应该怎么做?

候选人
Redis 本身不支持直接自定义淘汰策略,但可以通过以下方式实现:

  1. 修改源码

    • Redis 是开源的,可以修改 evict.c 中的淘汰逻辑,添加自定义算法。
    • 例如,基于键的大小、业务优先级等设计新策略。
    • 缺点:需要重新编译,维护成本高。
  2. 外部控制

    • 使用 Redis 的 INFO 命令监控内存使用情况。
    • 通过客户端脚本定期扫描键空间,结合业务逻辑(如优先级、访问频率)执行 DEL 命令删除键。
    • 优点:无需改源码,灵活性高。
    • 缺点:需要额外开发,可能影响性能。
  3. 结合 Lua 脚本

    • 使用 Redis 的 Lua 脚本实现简单的淘汰逻辑,通过 EVAL 命令定期运行。
    • 例如,扫描特定模式的键,基于自定义规则删除。
    • 局限性:复杂逻辑可能受 Lua 脚本性能限制。

推荐方案

  • 小规模场景:使用外部控制 + Lua 脚本。
  • 大规模场景:修改源码并充分测试。

面试官:实际生产环境中,你会如何选择淘汰策略?

候选人
选择淘汰策略需要综合考虑业务场景、数据特性和服务要求:

  1. 明确数据特性

    • 数据是否有明确的生命周期?如果有,优先考虑 volatile-* 策略。
    • 数据访问是否有热点?如果有,LRU 或 LFU 更适合。
  2. 评估业务需求

    • 数据丢失是否可接受?如果不可接受,选择 noeviction
    • 性能敏感度高?选择 Fast 模式或 allkeys-random
  3. 典型场景

    • 电商缓存:热点数据多,选 allkeys-lru + Fast 模式。
    • 推荐系统:频率差异大,选 allkeys-lfu + Slow 模式。
    • 临时数据:有 TTL,选 volatile-ttlvolatile-lru
  4. 监控与调优

    • 使用 INFO MEMORY 监控 evicted_keysmem_used
    • 调整 maxmemory-samplesmaxmemory-eviction-tenacity 优化淘汰效果。

我的经验
在某电商项目中,我们使用 allkeys-lru 结合 Fast 模式,配合 maxmemory-samples=10,在高并发场景下保持了低延迟,同时通过监控调整 maxmemory 避免频繁淘汰。

总结

Redis 的淘汰策略为开发者提供了灵活的内存管理方式,涵盖了从简单随机淘汰到复杂 LRU/LFU 算法的多种选择。Fast 模式和 Slow 模式进一步优化了淘汰的性能部分,快慢模式不是淘汰策略,而是执行效率的权衡。理解和记忆这些概念需要结合场景和类比,实际应用中需根据业务需求选择合适的策略。通过模拟面试的深度剖析,我们可以看到 Redis 淘汰策略的实现细节和生产环境中的应用技巧。希望本文能帮助大家更深入地理解 Redis 的内存管理机制!