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

Redis里头那些过期问题咋整,聊聊几种实用的解决办法和思路

Redis这个内存数据库,速度快是快,但内存是有限的,不可能啥数据都永远放着,所以它提供了一个给数据设置过期时间的功能,到点就自动删除,这个功能非常关键,但有时候你会发现,有些键明明设了过期时间,怎么好像没及时消失?或者大量键同时过期导致服务器卡了一下?这些问题就是咱们要聊的“过期问题”。

核心机制:Redis是怎么清理过期键的?

要解决问题,得先知道它咋工作的,Redis删除过期键主要靠两种策略配合,这就像你打扫房间:

Redis里头那些过期问题咋整,聊聊几种实用的解决办法和思路

  1. 被动删除(惰性删除):这是“用到才检查”的策略,当客户端尝试访问一个键时,Redis会先检查一下这个键是否已经过期了,如果过期了,当场就把它删掉,然后返回空值给客户端,这个方式很及时,但有个大问题:如果一个过期键一直没人访问,那它就会一直占着内存不释放,成了“垃圾”。

  2. 主动删除(定期删除):这是“定期巡检”的策略,因为光靠被动删除会内存泄漏,所以Redis会每隔一段时间(默认是每秒10次,可配置)随机抽取一些设置了过期时间的键进行检查,如果发现有过期的,就删除掉,这次抽多少样本(默认20个),如果发现这批样本里过期键的比例很高(超过25%),它会立即再抽一批,循环下去,直到过期键的比例降下来,这个方式是为了解决“垃圾”一直堆积的问题。

常见的过期问题与解决思路

Redis里头那些过期问题咋整,聊聊几种实用的解决办法和思路

了解了基本机制,就能理解为什么会出问题了,主要有以下几种情况和应对办法:

大量键集中过期,导致服务间歇性卡顿(缓存雪崩的一种情况)

  • 现象:比如你有一批缓存数据,都设置在凌晨2点同时过期,到了2点,瞬间大量请求发现缓存没了,全都涌向数据库去查数据,数据库压力陡增,可能被打挂,Redis本身在2点这个时刻,主动删除策略会瞬间遇到海量过期键,清理工作会消耗大量CPU资源,可能导致Redis本身也响应变慢。
  • 解决思路:分散过期时间,这是最核心、最有效的一招,不要设置一个固定的过期时间。
    • 基础版:在设置过期时间时,加一个随机数,原本都缓存1小时,现在可以改成缓存 3600 + random.randint(0, 300) 秒(即1小时到1小时5分钟之间),这样就能把大量键的过期时间打散,避免同时失效。
    • 进阶版(考虑热点数据):对于特别热门的数据,甚至可以设置永不过期,然后通过后台逻辑或发布订阅机制,在数据源变更时主动更新缓存,这样就完全绕开了过期问题。

过期键删除不及时,内存占用居高不下

Redis里头那些过期问题咋整,聊聊几种实用的解决办法和思路

  • 现象:你可能通过info memory命令发现,有很多键明明应该过期了,但内存还是没有降下来,这通常是因为这些键很长时间没有被访问(躲过了被动删除),而主动删除的抽样又一直没抽到它们(运气好)。
  • 解决思路:调整删除策略或使用淘汰策略兜底
    • 调整主动删除参数:Redis的主动删除配置(hz参数)默认是10,表示每秒进行10次抽样清理,在Redis 4.0以后,你可以适当调高这个值(比如调到20或50),会增加CPU的消耗,但能更频繁地清理过期键,让内存回收更及时,这是个权衡(Trade-Off)。
    • 设置内存淘汰策略(终极保险):这是最重要的一步,光靠过期删除不保险,你必须配置maxmemory-policy,当Redis内存使用达到上限时,这个策略会生效,自动淘汰一些键来腾出空间,常用的策略有:
      • volatile-lru:从设置了过期时间的键中,淘汰最近最少使用的。
      • allkeys-lru:从所有键中,淘汰最近最少使用的。
      • volatile-ttl:从设置了过期时间的键中,淘汰剩余存活时间最短的。 推荐使用volatile-lru,它能保证只淘汰那些有过期时间的“候选”键,不会动你的永久数据,这个策略确保了即使有漏网的过期键,在内存不足时也会被清理掉,不会导致内存爆满。

如何知道一个键是因为不存在而返回nil,还是因为过期被删除了?

  • 现象:你获取一个键,返回了空(nil),你无法区分是这个键本来就不存在,还是它存在但刚刚过期被删掉了,在一些需要精确判断的业务场景下,这可能会造成困扰。
  • 解决思路:使用额外的数据结构或命令
    • 笨办法但有效:在设置键的同时,在另一个Set或Hash里也记录下这个键名,判断时,先检查这个“目录”,如果目录里有,但主键查不到,就说明是过期了;如果目录里也没有,就说明根本不存在。
    • 利用Redis的“坟场”标记(高级用法):当一个键过期时,可以通过Redis的keyspace notifications(键空间通知)功能发布一个事件,你的应用程序可以订阅这些事件,当收到某个键的expired事件时,就在另一个地方(比如另一个Redis键或数据库)做个记录,标记这个键是“已过期”状态,这样查询时就能精确判断了,注意,这个功能需要额外开启,并且会对性能有轻微影响。

总结一下

处理Redis的过期问题,核心思路就几点:

  1. 预防为主:通过给过期时间加随机值,避免大量键同时失效,这是成本最低、效果最好的办法。
  2. 配置兜底:一定要设置合理的内存淘汰策略(如volatile-lru),这是防止内存泄漏的最后一道坚固防线。
  3. 按需调优:如果对内存释放的及时性要求很高,可以适当调高主动删除的频率,但要监控CPU成本。
  4. 特殊需求特殊处理:对于需要精确感知过期事件的场景,可以考虑使用键空间通知等高级功能。

把这些结合起来用,Redis的过期问题基本就能被控制在可接受的范围内了。