Redis里键值过期那点事儿,怎么优化才能不掉链子
- 问答
- 2025-12-30 03:49:09
- 2
主要整合自Redis官方文档关于过期的说明、Antirez(Redis之父)的博客文章《Redis 过期机制详解》以及多位技术博主如“程序员囧辉”、“小林coding”在实践经验中总结的要点)
Redis这个高性能的钥匙串(键值存储),有个非常实用的功能,就是能给存放的数据设置一个“保质期”,时间一到,数据就自动消失了,这个功能在我们日常开发中用处极大,比如短信验证码五分钟失效、用户登录令牌七天有效、热点数据缓存一小时等等,它帮我们省去了手动清理的麻烦,让应用自己就能保持“整洁”。
但你要是觉得设个过期时间就一劳永逸,那可能就想简单了,这个自动过期的机制背后,是怎么工作的?万一它“掉链子”,该过期的没及时过期,导致内存撑爆,或者影响到了正常服务,那可就麻烦大了,我们得把它那点事儿摸清楚,然后想办法优化,让它老老实实干活。

Redis是怎么知道哪个钥匙(键)该过期了呢?它主要用了两种策略,有点像小区物业收物业费。
第一种策略,叫做“被动过期”,这就像物业不会每天挨家挨户查谁没交费,而是等你自己来交水电费的时候,顺便查一下:“哎,你家物业费好像到期了哦,该交了,不交可就停水停电了。” 在Redis里,就是当客户端尝试去访问一个键的时候,Redis才会顺便检查一下这个键是否已经过期,如果过期了,就立刻把它删掉,然后返回一个“查无此键”的信号给客户端,这个方法很懒,但很高效,因为只会在真正被用到的时候才干活,不浪费资源。

第二种策略,叫做“主动过期”,光靠“被动”检查不行啊,万一有个键永远没人来访问,但它又过期了,岂不是成了“钉子户”,永远占着地方?所以Redis还得有个主动巡查的机制,这就像物业也会定期派人在小区里转转,看看有没有长期不住人或者明显欠费的住户,Redis内部会定期(默认是每秒10次)随机抽取一定数量的键(默认是20个)进行检查,如果发现有过期的,就删除它们,如果在这一批抽查中,发现过期键的比例很高(超过25%),说明“钉子户”很多,Redis就会立刻再来一轮抽查,直到过期键的比例降下来为止,这个主动巡查的机制,保证了即使没人访问的过期键,最终也会被清理掉。
问题来了,在什么情况下这个机制会“掉链子”呢?我们又该怎么优化?

-
大量键同时过期,导致服务卡顿:这是最常见的一个坑,比如你有个需求,在每天凌晨零点要重置一批数据,于是你把成千上万个键的过期时间都设置在零点,结果时间一到,Redis的主动过期策略会瞬间发现大量的过期键,它会不停地、高频率地执行抽查和删除操作,这个删除过程是会占用主线程(负责处理命令的线程)资源的,如果删除任务过于繁重,就会导致Redis在那一瞬间无法及时响应其他的读写命令,感觉就是服务卡了一下,甚至超时,这就像物业在一天内要集中清理几百个欠费户,工作人员忙得晕头转向,连正常报修都没空处理了。
- 优化办法:给过期时间加个随机数,别让所有键在同一秒过期,原本都设24小时过期,你可以改成“24小时 + 一个1到60分钟的随机值”,这样就把过期时间打散了,压力会平摊到一段时间内,避免瞬间高峰,这是Antirez在博客中强烈推荐的做法。
-
内存耗尽,即使键过期了也来不及删:如果你的Redis实例内存设置得太满,或者写入速度远远大于过期速度,可能会导致一种更糟糕的情况:内存用尽了,这时候,Redis会触发它的“淘汰策略”,如果淘汰策略设置的是
noeviction(不淘汰),那么新的写命令就会直接报错,写不进去了,更关键的是,即使这时候有键已经过期了,但Redis可能因为内存压力太大,连执行过期删除操作的余力都没有了(因为删除操作本身也需要一点点内存和CPU),形成恶性循环。- 优化办法:第一,永远不要把内存用满,要设置一个安全上限,比如最大内存用到80%就报警,第二,根据业务需求,设置一个合适的、非
noeviction的淘汰策略,比如如果是缓存场景,完全可以设置为allkeys-lru,当内存不足时,自动淘汰最近最少使用的键,优先保证服务可用性,这就像仓库不能堆得满满当当,总要留出过道,并且定好规矩,新货来了没地方放,就把最久不动的旧货先清出去。
- 优化办法:第一,永远不要把内存用满,要设置一个安全上限,比如最大内存用到80%就报警,第二,根据业务需求,设置一个合适的、非
-
“僵尸”键问题:你会发现明明设置了一小时的过期时间,但某个键好几个小时了还在那里,这通常不是因为Redis的bug,而是这个键在过期后,又被频繁地访问或修改了,你的程序逻辑可能有问题:先判断键是否存在,如果不存在就从数据库取,然后
SET键并设置过期时间,但在高并发下,可能多个请求同时发现键不存在,然后都去执行SET操作,这会导致过期时间被一次次重置,或者,你对一个键执行了PERSIST(持久化)命令,也会移除它的过期时间。- 优化办法:检查代码逻辑,确保设置键和过期时间的操作是原子的,尽量使用像
SET key value EX seconds这样的复合命令,一步到位,避免先SET再EXPIRE,审查业务逻辑,避免意外地重置或清除了过期时间。
- 优化办法:检查代码逻辑,确保设置键和过期时间的操作是原子的,尽量使用像
-
从库的过期问题:在Redis主从架构中,过期键的删除是由主库主导的,主库在删除一个过期键时,会向从库发送一个
DEL命令,从库自己不会主动过期键,它只是被动接收主库的指令,这样设计是为了保证数据的一致性,但在网络出现分区(主从连接不稳定)时,从库可能无法及时收到主库的DEL命令,导致从库上的过期键会多存在一段时间,直到主从重新同步。- 优化办法:这个问题通常不需要应用层过多操心,但作为架构师需要了解这个特性,确保主从之间的网络质量,并设置合理的复制超时时间,在读取从库数据时,如果对数据的实时性要求极高,需要有心理准备或直接从主库读取。
Redis的过期机制很聪明,但也不是万能的,要想让它不掉链子,核心思路就几点:避免集中过期,给过期时间加上随机抖动;严防内存打满,设置监控告警和合适的淘汰策略;保证操作原子性,避免意外重置过期时间;了解主从特性,在架构设计时心中有数,把这些点做到位,Redis这个好帮手就能既帮你自动清理,又保持高性能,稳稳当当地为你服务。
本文由钊智敏于2025-12-30发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://haoid.cn/wenda/71037.html
