深度分析redis过期删除策略和内存淘汰机制

184 阅读5分钟

知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!


Redis 的 过期键删除策略内存淘汰机制 是保障其高效内存管理的核心设计。以下从工作原理、实现细节到应用场景的深度分析:

一、过期键删除策略

Redis 通过 惰性删除定期删除 两种策略协同工作,平衡性能与实时性。

1. 惰性删除(Lazy Expiration)

  • 触发时机:当客户端访问一个键时,Redis 会检查该键是否过期。
  • 执行流程
    1. 客户端执行 GET key 命令。
    2. Redis 先检查 key 是否已过期(比较当前时间与键的 expires 字典中的过期时间)。
    3. 如果过期,则删除键并返回空;否则返回键值。
  • 优点:对 CPU 友好,只在使用时消耗资源。
  • 缺点:可能导致大量过期键长期占用内存(如不再访问的键)。

2. 定期删除(Active Expiration)

  • 触发时机:Redis 周期性随机抽查部分过期键并删除。
  • 执行流程
    1. expires 字典中随机选择 20 个键。
    2. 删除其中已过期的键。
    3. 如果超过 25% 的键过期,则重复步骤 1。
  • 核心配置
    • hz 参数(默认 10):控制每秒执行定期删除的频率。
    • 每次检查的键数量和时间上限由算法动态调整。
  • 优点:减少内存浪费,平衡实时性。
  • 缺点:仍可能遗漏部分过期键。

3. 过期键存储结构

  • 过期字典(expires):一个哈希表,记录所有设置了过期时间的键及其到期时间戳。
    • Key:指向实际键的指针。
    • Value:64 位整数存储过期时间(Unix 时间戳)。

二、内存淘汰机制(Eviction Policies)

当内存达到 maxmemory 限制时,Redis 根据配置的策略淘汰键以释放空间。

1. 淘汰策略分类

Redis 提供 8 种策略,通过 maxmemory-policy 配置:

策略规则适用场景
noeviction(默认)拒绝所有写入操作(DEL 等可继续),返回 OOM 错误。数据绝对不允许丢失的场景。
allkeys-lru(常用)从所有键中淘汰最近最少使用(LRU)的键。热点数据分布明显的缓存。
volatile-lru从设置了过期时间的键中淘汰 LRU 键。需保留持久化数据的缓存。
allkeys-lfu(常用)从所有键中淘汰最不频繁使用(LFU)的键(Redis 4.0+)。长期保留高频访问键的场景。
volatile-lfu从过期键中淘汰 LFU 键。结合 TTL 和访问频率的场景。
allkeys-random随机淘汰任意键。数据访问无规律的场景。
volatile-random随机淘汰过期键。需保留部分持久化数据的场景。
volatile-ttl优先淘汰剩余存活时间(TTL)最短的键。需要快速清理短期数据的场景。

2. LRU 与 LFU 的实现优化

  • 近似 LRU/LFU:Redis 采用 概率性采样 而非精确计算,以节省内存。
    • 每次淘汰时随机选取 5 个(可配置)候选键,选择其中最久未使用(LRU)或最少使用(LFU)的键。
    • 通过 maxmemory-samples 参数调整采样数量(默认 5,增大更精确但消耗 CPU)。
  • LFU 的访问频率统计
    • 使用 24 位计数器(Logarithmic Counter)记录访问频率,随时间衰减。
    • 通过 lfu-log-factorlfu-decay-time 控制衰减速度。

3. 淘汰流程

  1. 客户端执行写入命令时触发内存检查。
  2. 若内存超限,根据策略选择待淘汰键。
  3. 删除键并释放内存,然后处理当前命令。

三、生产环境配置建议

1. 过期键管理

  • 合理设置 TTL:避免大量键同时过期(导致瞬时负载)。
  • 监控过期键:使用 INFO stats 查看 expired_keys 计数。
  • 调整定期删除频率:增大 hz(谨慎,会增加 CPU 负载)。

2. 内存淘汰优化

  • 策略选择
    • 缓存场景:allkeys-lruallkeys-lfu
    • 持久化 + 缓存混合:volatile-lru
    • 严格数据保护:noeviction
  • 关键配置
    maxmemory 4gb                           # 设置最大内存
    maxmemory-policy allkeys-lru            # 选择淘汰策略
    maxmemory-samples 10                    # 提高 LRU/LFU 精度
    

3. 大 Key 处理

  • 避免大 Key:单个 String 不超过 10KB,集合元素不超过 5000。
  • 拆分大 Key:如将 user:1:favorites 拆分为多个小 Hash。

四、常见问题与解决方案

1. 内存溢出但大量键已过期

  • 原因:惰性删除未触发,定期删除未清理完。
  • 解决:手动执行 SCAN + TTL 检查,或增加 hz 值。

2. 淘汰策略导致热点数据被误删

  • 现象allkeys-random 误删高频访问键。
  • 解决:切换为 allkeys-lruallkeys-lfu

3. LFU 计数不准确

  • 调整参数
    lfu-log-factor 10    # 降低计数器增长难度(默认 10)
    lfu-decay-time 1     # 计数器衰减时间(分钟,默认 1)
    

五、总结:Redis 内存管理设计哲学

  1. 惰性删除:优先保证性能,牺牲部分内存效率。
  2. 定期删除:平衡实时性与 CPU 开销。
  3. 近似 LRU/LFU:以极小内存开销实现高效淘汰。
  4. 灵活策略:根据业务特点选择淘汰逻辑。