Redis内存回收-内存淘汰策略

一、引言:当内存"爆仓"时,Redis如何抉择?

作为一款基于内存的高性能数据库,Redis 的速度令人惊叹。但内存是有限且昂贵的资源。当你的业务数据持续增长,最终触碰到 Redis 配置的 maxmemory 上限时,会发生什么?

是直接拒绝所有新写入请求?还是智能地删除一些"不那么重要"的旧数据来为新数据腾出空间?

这就是 Redis 内存淘汰策略(Eviction Policy) 要解决的核心问题。它定义了在内存不足时,Redis 应该如何做出取舍,以保证服务的持续可用性。理解并正确配置这些策略,是每一位 Redis 使用者的必修课。

💡 核心价值
本文将带你全面了解 Redis 的 8 大内存淘汰策略,深入其工作原理,并提供清晰的选型指南,助你避免线上 OOM 事故


二、前置知识:maxmemory 与淘汰触发时机

在讨论策略之前,必须明确两个前提:

  1. maxmemory 配置 :这是 Redis 内存使用的上限。你可以通过 CONFIG SET maxmemory <bytes> 动态设置,或在 redis.conf 中永久配置

    bash 复制代码
    # 设置最大内存为 2GB
    maxmemory 2gb
  2. 触发时机 :只有当 Redis 的已用内存达到或超过 maxmemory 时,才会根据配置的淘汰策略来决定如何处理新的写命令(如 SET, LPUSH 等)。读命令(如 GET)不受影响。


三、八大内存淘汰策略详解

Redis 提供了 8 种不同的淘汰策略,可通过 maxmemory-policy 参数进行配置。它们大致可分为三大类:

第一类:不淘汰任何数据 (noeviction)

  • 策略名 : noeviction
  • 行为 : 这是 Redis 的默认策略 。当内存达到上限时,拒绝所有会增加内存占用的写命令 ,并向客户端返回一个错误信息:(error) OOM command not allowed when used memory > 'maxmemory'
  • 适用场景 : 适用于那些不能容忍任何数据丢失的场景,或者你希望由应用程序层来处理内存不足的情况。但在生产环境中,这通常不是一个好选择,因为它会导致服务不可用。

第二类:仅从设置了过期时间(TTL)的 Key 中淘汰

这类策略只会在那些已经设置了过期时间的 Key 中进行筛选和淘汰,不会动那些永不过期的 Key。

  1. volatile-lru (Least Recently Used - 最近最少使用)

    • 行为 : 从设置了 TTL 的 Key 中,优先淘汰最近最久未被访问的 Key。
    • 原理: 利用近似 LRU 算法(见下文"实现细节")。
    • 适用场景: 经典的缓存场景,其中大部分数据都有明确的过期时间,且访问模式符合"热点数据常被访问"的局部性原理。
  2. volatile-lfu (Least Frequently Used - 最不经常使用)

    • 行为 : 从设置了 TTL 的 Key 中,优先淘汰访问频率最低的 Key。
    • 原理: 利用近似 LFU 算法(见下文"实现细节")。
    • 适用场景: 数据访问频率差异巨大,存在大量"一次性"或"低频"访问的数据。相比 LRU,更能识别出真正的冷数据。
  3. volatile-random

    • 行为 : 从设置了 TTL 的 Key 中,随机选择一个 Key 进行淘汰。
    • 适用场景: 当你认为所有带 TTL 的 Key 价值相当,或者无法预测访问模式时,可以作为一种简单粗暴的选择。
  4. volatile-ttl

    • 行为 : 从设置了 TTL 的 Key 中,优先淘汰剩余存活时间(TTL)最短的 Key。
    • 适用场景: 你希望尽快清理掉那些"即将过期"的数据,为新数据腾出空间。

第三类:从所有 Key 中淘汰(无论是否设置了 TTL)

当你的 Redis 实例中混合存储了有 TTL 和无 TTL 的 Key,或者所有 Key 都没有设置 TTL 时,就需要使用这类策略。

  1. allkeys-lru

    • 行为 : 从所有 Key 中,优先淘汰最近最久未被访问的 Key。
    • 适用场景 : 这是最常用的通用缓存策略。无论 Key 是否有 TTL,都将其视为缓存项,用 LRU 原则进行管理。
  2. allkeys-lfu

    • 行为 : 从所有 Key 中,优先淘汰访问频率最低的 Key。
    • 适用场景: 通用缓存场景,但数据访问模式更倾向于区分高频和低频,而非近期访问。
  3. allkeys-random

    • 行为 : 从所有 Key 中,随机选择一个 Key 进行淘汰。
    • 适用场景: 所有 Key 被认为具有同等价值,或者作为性能测试时的基准策略。

四、核心算法揭秘:LRU 与 LFU 的实现

Redis 并未使用传统的、精确的 LRU/LFU 算法,因为它们需要额外的数据结构(如链表、计数器),会带来巨大的内存和 CPU 开销。Redis 采用的是高效的近似算法

4.1 近似 LRU (Approximated LRU)

  • 原理 : Redis 在每个 RedisObject 结构体中用 24 位(3字节)来记录该对象最后一次被访问的时间戳(秒级精度)。
  • 淘汰过程 : 当需要淘汰时,Redis 会随机采样一小部分 Key(默认 5 个),然后比较它们的 LRU 时间戳,淘汰其中最旧的一个。这个过程会重复,直到释放出足够的内存。
  • 优点: 内存开销极小(每个 Key 只需 3 字节),性能极高。
  • 缺点 : 是一种概率性的近似,并非全局最优。

4.2 近似 LFU (Approximated LFU) - Redis 4.0+

  • 原理 : 同样利用 RedisObject 中的 24 位字段,但将其拆分为两部分:
    • 高 16 位: 记录最近一次递减计数器的时间(分钟级)。
    • 低 8 位: 一个 8 位的访问计数器。
  • 计数器更新 : 采用对数计数器概率性递增机制。访问越频繁,计数器增长越慢,防止其过快饱和。同时,计数器会随着时间缓慢衰减,以反映数据热度的变化。
  • 优点: 能有效区分高频和低频数据,且能适应数据访问模式随时间变化的情况。
  • 缺点: 配置相对复杂,需要理解其衰减逻辑。

五、如何选择合适的淘汰策略?

场景 推荐策略 理由
纯缓存,所有数据都有 TTL volatile-lruvolatile-lfu 只清理缓存数据,保护非缓存数据(如果有的话)。LFU 适合访问频率差异大的场景。
纯缓存,数据无 TTL 或混合 TTL allkeys-lru (首选) 或 allkeys-lfu 将所有数据都视为可淘汰的缓存项。LRU 简单高效,是通用首选。
不能接受任何数据丢失 noeviction 由应用层处理内存不足,但可能导致服务中断。
无法预测访问模式 allkeys-randomvolatile-random 作为兜底方案,简单直接。
希望尽快清理快过期的数据 volatile-ttl 专注于 TTL 维度进行淘汰。

📌 黄金法则 :对于绝大多数缓存场景,allkeys-lru 是最安全、最通用的选择


六、配置与验证

6.1 配置方法

bash 复制代码
# 临时生效
127.0.0.1:6379> CONFIG SET maxmemory-policy allkeys-lru
OK

# 永久生效 (修改 redis.conf)
maxmemory-policy allkeys-lru

6.2 查看当前配置

bash 复制代码
127.0.0.1:6379> CONFIG GET maxmemory-policy
1) "maxmemory-policy"
2) "allkeys-lru"

6.3 监控淘汰情况

通过 INFO 命令可以监控淘汰的详细信息:

bash 复制代码
127.0.0.1:6379> INFO memory
# Memory
...
evicted_keys:12345  # 自启动以来被淘汰的Key总数
...

七、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
用户900305093624 小时前
2026年Cursor平替工具推荐:免费高性价比替代方案
前端
我头上有犄角ovo4 小时前
HarmonyOS 测肤拍照页实战:Metadata 实时取景 + Core Vision 拍后校验,从 0.001 的 widthRatio 踩坑到可上线
前端·harmonyos
画画的阿飞4 小时前
里程碑三:基于 Vue3 领域模型架构建设
前端·node.js
玉米Yvmi4 小时前
大文件上传的基石:切片上传原理与实现详解
前端·javascript·面试
一只fish4 小时前
Oracle官方文档翻译《Database Concepts 26ai》第7章-数据完整性
数据库·oracle
Gauss松鼠会4 小时前
【GaussDB】GaussDB 常见问题及解决方案汇总
java·数据库·算法·性能优化·gaussdb·经验总结
云边云科技_云网融合4 小时前
云边云全栈 SD-WAN/SASE 运维服务:构建企业数字网络的坚实后盾
数据库·人工智能·云计算
用户4099322502124 小时前
Composable的命名规矩和参数约定,别再瞎写了
前端·javascript·后端
用户游民4 小时前
Flutter Provider原理以及用法
前端·flutter