当前位置:首页 > 问答 > 正文

Redis里那些过期算法到底有哪些,怎么选才合适呢?

在Redis里,管理那些设置了过期时间的键(key)并准时删除它们,是一个非常重要的任务,直接影响到内存的使用效率和系统的性能,Redis并不是在键过期的那一刻就立刻删除它,因为那样会对性能造成很大冲击,相反,它使用了几种策略组合在一起工作,主要可以归结为两类:被动过期和主动过期。

核心的过期删除算法(策略)

Redis里那些过期算法到底有哪些,怎么选才合适呢?

  1. 被动过期(惰性删除)

    • 怎么做的:当客户端尝试访问一个键的时候,Redis才会去检查这个键是否已经过期,如果过期了,就立刻删除这个键,然后返回空值给客户端,就像这个键从来不存在一样。
    • 优点:非常省资源,只在使用时检查,如果某个过期键永远不再被访问,那么它就会一直待在内存里,直到被其他策略清理掉,这种方式把删除操作分散到了各个命令中,没有额外的CPU开销。
    • 缺点:对内存不友好,如果有很多键过期后再也没有被访问,它们就会成为“僵尸键”,白白占用着宝贵的内存空间,可能导致内存无法释放。
  2. 主动过期(定期删除)

    Redis里那些过期算法到底有哪些,怎么选才合适呢?

    • 怎么做的:由于惰性删除的缺点,Redis还需要一个主动扫描的机制,它会定期地(默认是每100毫秒一次)随机抽取一部分设置了过期时间的键,检查它们是否过期,如果过期就删除,这个过程是循环进行的。
    • 优点:在一定程度上弥补了惰性删除的不足,能够清理掉那些不再被访问的过期键,减少了内存的浪费。
    • 缺点
      • 难以完全精确:因为是抽样检查,不可能在一次循环中检查所有键,所以总会有一部分过期的键不能立刻被删除,会有一定的延迟。
      • CPU消耗的权衡:如果抽样的键数量太多或者检查得太频繁,就会消耗较多的CPU资源,可能影响正常请求的处理,反之,如果抽样的太少或频率太低,又会导致大量过期键堆积。

当内存不足时的应对算法:淘汰策略

上面两种策略是日常维护过期键的,但当内存使用达到Redis设置的上限时,即使有定期删除,也可能来不及清理出足够空间,这时候,就需要“淘汰策略”来强行删除一些键以腾出空间,这里的删除对象不仅包括过期的键,也可能包括未过期的键,Redis提供了8种淘汰策略供用户选择:

  1. noeviction:默认策略,当内存不足时,新写入的操作会报错,读、删除操作正常进行,这适用于你希望数据绝对可靠,宁愿报错也不能丢失的场景。
  2. allkeys-lru:从所有key中,使用LRU算法(最近最少使用)淘汰最久未被访问的键。
  3. volatile-lru:只从设置了过期时间的key中,使用LRU算法淘汰最久未被访问的键。
  4. allkeys-lfu:从所有key中,使用LFU算法(最不经常使用)淘汰访问频率最低的键。(这是4.0版本后新增的)
  5. volatile-lfu:只从设置了过期时间的key中,使用LFU算法淘汰访问频率最低的键。
  6. allkeys-random:从所有key中,随机选择并删除键。
  7. volatile-random:只从设置了过期时间的key中,随机选择并删除键。
  8. volatile-ttl:只从设置了过期时间的key中,淘汰剩余存活时间(TTL)最短的键。

怎么选才合适?

选择哪种淘汰策略,完全取决于你的业务场景和数据特性,以下是一些常见的选型建议:

  • 如果你的数据重要性有区分,且热点数据比较明显:这是最常见的场景,推荐使用 allkeys-lru,它能有效地将热点数据保留在内存中,淘汰掉那些长期不被访问的冷数据,从而保证缓存的命中率。
  • 如果你能明确区分缓存和持久数据:即你希望设置了过期时间的只是缓存,而没设置过期时间的是重要的持久数据,绝对不能丢,那么可以选择 volatile-lruvolatile-lfu,这样Redis只会在缓存数据里进行淘汰,保护了你的核心数据。
  • 如果你的数据访问频率比访问时间更能体现其价值:一个键在短时间内被疯狂访问了无数次,但之后很久没用了,你希望它能比一个偶尔被访问一次的键留存更久,这种情况下,allkeys-lfuvolatile-lfu 是比LRU更好的选择。
  • 如果你希望数据尽可能存活得久一点,且不关心淘汰顺序:可以考虑 volatile-ttl,它会优先淘汰即将过期的键,让那些还有很长时间才过期的键多存活一会儿。
  • 如果你的数据没有明显的访问模式,所有键被访问的概率都差不多:那么使用 allkeys-randomvolatile-random 这种随机淘汰的策略,反而能获得一个比较平均的结果,而且实现简单,开销小。
  • 如果你的数据绝对不能丢失,内存不足时你更愿意收到错误:比如在进行一些金融交易时,确保数据正确性比可用性更重要,那么就应该坚持使用默认的 noeviction 策略,同时你需要有监控机制,确保内存不会轻易用尽。

总结一下(来源:基于Redis官方文档对过期和内存淘汰机制的描述综合得出),Redis的过期管理是一个组合拳:惰性删除 + 定期删除 是基础保障,用于日常清理,而内存淘汰策略是在极端情况下的最后防线,选择哪个策略,没有绝对的对错,关键是想清楚你的业务中“什么样的数据在内存不足时可以被牺牲掉”。allkeys-lru 是一个普适性较强的安全选择。

Redis里那些过期算法到底有哪些,怎么选才合适呢?