Redis快速清理过期数据的小技巧,省时又高效不费力
- 问答
- 2026-01-02 03:49:40
- 3
说到清理Redis的过期数据,很多人可能第一反应是等着Redis自己慢慢清,Redis确实有个自己的懒人方法,就是当一个带过期时间的键被访问时,如果发现它已经过期了,就顺手把它删掉,这方法听起来挺聪明,但问题也很大,你想啊,要是一个键过期后一直没人去碰它,那它不就一直像个幽灵一样占着内存不走了吗?这肯定不行,时间一长,内存就会被这些“僵尸数据”塞满。(来源:Redis官方文档对被动过期策略的描述)
光靠这个懒人方法是不够的,Redis还有个主动清理的招式,这个主动清理的过程,Redis自己会定期偷偷摸摸地干,这就是所谓的“定期删除”,Redis会每隔一段时间(默认是100毫秒,这个频率可以调,但一般不建议动)就抽查一部分设置了过期时间的键,看看谁过期了,然后把过期的挑出来干掉。(来源:Redis官方文档对主动过期策略的描述)
这个抽查的活儿也挺有讲究,它可不是每次都把所有的键检查一遍,那太累了,会影响Redis正常干活儿,Redis会设置一个检查的时间上限,防止自己检查起来没完没了,它还会控制每次检查的键的数量,如果发现上一轮抽查中,过期的键比例很高(比如超过25%),说明过期键很多嘛,那它就会很自觉地在下一轮检查中,继续抽查同样多的键,直到过期的键比例降下来为止,这就像扫地,发现一个角落特别脏,你就会多扫几下。(来源:Redis源码中关于activeExpireCycle函数的实现逻辑)
听起来Redis自己已经把清理工作安排得明明白白了,那我们是不是可以当甩手掌柜了呢?也不是,有时候我们会遇到一些特殊情况,需要咱们手动推它一把,让它清理得更快更彻底,下面就分享几个小技巧。
第一个小技巧,叫做随机抽样扫描,这个方法特别适合当你感觉Redis里可能有一大批键都已经过期了,但内存占用却迟迟降不下来的时候,我们可以利用Redis提供的SCAN命令来实现,这个命令可以安全地遍历数据库里所有的键,而不会像老式的KEYS *命令那样一下子把Redis搞卡住。(来源:Redis官方文档对SCAN命令的说明)

具体怎么做呢?我们可以写一个简单的脚本(比如用Python、Shell都行),用SCAN命令循环读出所有的键名,对每一个键,使用TTL命令查看它还剩多少秒过期,如果TTL返回的值是 -2,恭喜你,这个键其实已经过期了,只是还没被Redis清理掉,这时候,你可以直接用DEL命令手动删除它,如果TTL返回-1,说明这个键压根没设置过期时间,是永久键,那你可别乱删,删了可能就找不回来了,通过这种方式,你可以主动帮助Redis清理掉那些“漏网之鱼”,不过要注意,这个操作本身也会消耗一些CPU资源,最好在业务低峰期(比如半夜)悄悄进行。
第二个小技巧,针对的是同一模式的大量键,你的系统里用了一种命名规则,像“user_session:12345”、“user_session:67890”这样的,代表了成千上万个用户会话,当你想批量让这些会话键过期时,一个个设置过期时间或者等它们自然过期后再清理,可能不够快,这里有个更高效的做法:不要直接设置每个键的过期时间,而是把它们放在一个Redis的集合(Set)或者哈希(Hash)里。(来源:基于Redis最佳实践的一种常见模式)
举个例子,你可以创建一个名为“all_active_sessions”的集合,把所有用户会话键的完整名字都塞进这个集合里,你只需要给这个“all_active_sessions”集合本身设置一个过期时间就行了,这样做的代价是,当你需要检查某个具体会话是否存在时,你得同时检查会话键本身和这个集合,当你需要一次性让所有会话全部失效时,效率就非常高了,因为只需要删除一个集合键(或者等它自动过期),所有相关的会话数据虽然还在内存里,但已经失去了“身份证明”,可以通过后续的清理机制被回收,或者,更彻底一点,你可以用DEL命令配合通配符(但DEL命令本身不支持通配符,所以通常需要结合SCAN脚本来批量删除匹配模式的键),直接干掉所有“user_session:*”的键,这种方法在需要瞬间清理大量同类数据时,比等待单个键逐个过期要干脆利落得多。

第三个小技巧,其实是个预防针,也就是在设置键的时候多动一点小心思,在给数据设置过期时间时,尽量避免让海量的键在同一秒内同时过期,如果你有个缓存机制,所有缓存都设置成正好一小时过期,而你的系统又是在整点时刻集中预热了大量缓存,那么下一个整点来临的时候,Redis就会面临一个巨大的过期键清理高峰,可能会导致瞬间的延迟升高,影响服务。(来源:Redis延迟问题排查指南中的常见原因)
一个简单的解决办法是,在设置过期时间时,加一点随机数,不是固定设置3600秒过期,而是设置成3600 + random(300),也就是在3600到3900秒之间随机取一个值,这样,键的过期时间就被打散了,不会集中在一个时间点爆发,给Redis的清理工作减轻了压力,让它能平缓地处理过期数据,这叫从源头上避免问题的发生。
还有一个终极大招,虽然听起来有点简单粗暴,但在某些极端情况下非常管用,那就是重启Redis或者切换主从,如果你的Redis实例里积压了天量的过期数据,导致内存严重不足,而且常规的清理方法见效太慢,你可以考虑一下这个方案,因为当Redis重启之后,它会从持久化文件(比如RDB文件)中恢复数据,在恢复的过程中,它只会加载那些未过期的键,所有已经过期的键在加载阶段就会被直接忽略掉,这样,重启完成后的Redis实例就是一个“瘦身”成功的干净实例,这个方法的风险在于重启期间服务不可用,所以一定要在做好备份、并且业务允许停机维护的情况下才能使用,对于有高可用要求的系统,可以先在从节点上执行重启,然后进行主从切换。(来源:Redis持久化机制及数据恢复原理)
Redis清理过期数据,不能光靠它自己“懒”和“定期抽查”,我们可以在关键时刻推它一把:用SCAN和TTL手动清理残留过期键;对批量同类键采用集合管理或模式化删除提高效率;设置过期时间时加入随机因子避免集中过期风暴;在万不得已时,通过安全重启来彻底“甩掉包袱”,这些方法结合使用,就能让Redis的内存管理变得既省时又高效。
本文由度秀梅于2026-01-02发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/72849.html
