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

Redis里过期数据怎么处理才快又稳,聊聊那些策略和细节

Redis里的数据过期,就像我们家里的食品一样,放久了会变质,需要及时清理掉,不然会占用空间甚至引发问题,但清理的时机和方法很有讲究,弄不好要么是垃圾堆积如山(内存满了),要么是打扫卫生时把客人都赶跑了(影响正常服务),Redis为了平衡效率和稳定性,用了好几招组合拳。

第一招:被动过期——等你自己坏掉

这是最基本的方式,简单说就是,当客户端尝试访问一个键的时候,Redis才会顺带检查一下这个键是否已经过期,如果过期了,就立刻删除它,然后返回一个空值给客户端。

(根据Redis官方文档)这种方式的好处是超级省心省力,如果没人碰这个过期键,Redis就不会主动去管它,节省了CPU时间,这就像厨房里的调料,你只有做菜时去拿,才发现某瓶酱油已经过期了,然后扔掉,如果你一直不做那道菜,那瓶过期酱油就会一直放着。

Redis里过期数据怎么处理才快又稳,聊聊那些策略和细节

但它的缺点也很明显:太被动了,如果有一大批键同时过期,但之后很长一段时间都没有被访问到,那么它们就会成为“僵尸键”,一直占用着宝贵的内存空间,直到有一天内存被塞满,引发更严重的问题。

第二招:主动过期——定期来个大扫除

光靠被动检查肯定不够,Redis还得主动出击,它内部有一个“定时任务”,会周期性地随机抽查一批设置了过期时间的键,然后把其中已经过期的删除掉。

Redis里过期数据怎么处理才快又稳,聊聊那些策略和细节

(根据Redis官方文档描述的逻辑)这个主动清理过程是分步骤、有限度进行的,以避免对正常服务造成太大影响:

  1. 慢速模式: Redis默认每100毫秒(一秒10次)就会执行一次这样的抽查,每次不会检查所有键,而是随机抽取一定数量的键(默认20个)进行检查,如果发现这批键里过期的比例超过25%,那就说明内存中垃圾不少,它会立即再抽20个键,继续清理,就这样循环,直到某一次抽查中过期键的比例降到了25%以下,它才认为“这次打扫得差不多了”,然后休息一下,等待下一轮。
  2. 快速模式: 在Redis的某些版本中,还引入了一种更频繁但更短暂的检查模式,它执行得更快(间隔更短),但每次检查的键数量更少,目的是为了能更及时地发现并清理那些刚刚过期的键,尤其是在过期键很多的情况下,能更快地释放内存。

这种主动策略的核心思想是:用可控的、少量的CPU时间,去换取内存的及时释放,防止内存被无限占用。 它就像一个管家,不会不停地打扫整个房子,而是每隔一会儿就去几个重点房间看一眼,看到有灰尘就擦掉,如果发现某个房间特别脏,就多花点时间打扫一下那个区域。

第三招:内存满了的终极手段——驱逐策略

Redis里过期数据怎么处理才快又稳,聊聊那些策略和细节

上面两招是日常维护,但当内存真的被用完(达到maxmemory限制)时,Redis就要下狠手了,这时候会触发“内存驱逐机制”,这就好比家里空间有限,又买了新家具,必须扔掉一些旧东西腾地方,扔哪些、怎么扔,就是驱逐策略决定的。

(根据Redis配置文档)你可以在配置文件里通过maxmemory-policy来指定策略,常见的有以下几种:

  • allkeys-lru: 尝试淘汰最近最少使用的键,不管这个键有没有设置过期时间,这是最常用的策略之一,比较公平。
  • volatile-lru: 只从设置了过期时间的键中,淘汰最近最少使用的。
  • allkeys-random: 随机淘汰所有键。
  • volatile-random: 随机淘汰设置了过期时间的键。
  • volatile-ttl: 淘汰那些设置了过期时间的键中,剩余寿命最短的,这个策略比较“智能”,专门挑快过期的扔。
  • noeviction: 不淘汰任何键,当内存不够时,新写入的操作会报错,读操作正常,这是最保守的策略,能保证已有的数据不丢,但服务可能会受影响。

选择哪种策略,取决于你的业务场景,如果你的数据重要性都差不多,用allkeys-lru挺好,如果你的热点数据永不过期,冷数据才设过期时间,那么volatile-lruvolatile-ttl可能更合适。

让我们做得又快又稳的实战细节

了解了原理,在实际使用中我们需要注意些什么呢?

  1. 避免大量键集中过期: 这是最需要注意的一点,如果你有一百万个键都在同一秒到期,那么Redis的主动定期删除任务会在那一刻压力巨大,可能会瞬间占用大量CPU,导致服务出现短暂的延迟抖动,解决办法是给过期时间加一个随机数,让它们的过期时间分散开,比如本来是都设置1小时后过期,可以改成在1小时的基础上,加上一个0到300秒的随机值。
  2. 监控是关键: 一定要监控Redis的expired_keys指标(累计过期键数量)和evicted_keys指标(累计驱逐键数量),如果evicted_keys开始持续增长,说明你的内存已经长期不足,一直在靠驱逐策略删数据,这是个危险信号,你需要考虑扩容或者优化数据了。
  3. 合理设置内存大小和策略: 根据你的数据量和业务容忍度,设置合适的maxmemory,对于策略,除非有特殊要求,否则allkeys-lru通常是个稳妥的选择,如果数据绝对不能丢,可以选择noeviction,但要确保你的系统能处理好写入失败的情况。
  4. 善用持久化: 虽然过期策略是内存里的操作,但它和持久化(RDB、AOF)也有关联,在生成RDB快照时,过期的键不会被保存进去,而在AOF日志中,当一个键被过期删除后,Redis会追加一条删除命令到AOF文件里,这样在恢复时数据状态才是正确的。

Redis的过期数据处理是一个以空间换时间、再在时间消耗上做精细权衡的过程,它通过“被动检查”、“主动定期抽样”和“内存不足时强制驱逐”这三层机制,在绝大多数场景下都能做得既快速又稳定,而我们开发者要做的,就是理解这些机制,并通过避免集中过期、做好监控和配置,来配合Redis,让它运行得更加顺畅。