用Redis给无限期记录设个过期时间,省点空间也别太久了
- 问答
- 2026-01-23 22:31:12
- 4
说到用Redis给那些本来没有过期时间的记录设个过期时间,这个想法很实际,说白了就是“打扫房间”,你想想,家里有些东西,你觉得可能以后会用得上,就一直堆在角落里,结果一年、两年过去了,你碰都没碰过,它还占着地方,Redis里的这些无限期记录就是这些东西,时间一长,Redis这个“房子”的内存就被这些可能再也不会用的“杂物”给占满了,导致真正需要空间的时候捉襟见肘。
那为什么一开始不设过期时间呢?情况有很多种,有时候是开发的时候图省事,觉得先让数据存着呗,以后再说,有时候是业务逻辑上,这个记录理论上就是应该永久存在的,比如一个用户的默认配置,除非他主动修改,否则就一直用这个,但问题是,用户可能注册了一下就再也没来过,这个“永久”的配置记录对这个“僵尸”用户来说就没意义了,可它还占着Redis的地方,还有一种情况,数据是从别的系统同步过来的,同步的时候只考虑了把数据塞进去,忘了考虑清理策略。
给这些“永久”住户加个“租赁合同”,到期不续就清走,是个非常必要的管理手段,怎么加呢?Redis本身提供了非常简单的命令,主要就是EXPIRE家族的命令。
最直接的就是EXPIRE key seconds命令,你有一条记录叫user:1000:profile,你想让它30天后自动消失,你可以先算一下30天有多少秒(302460*60=2592000),然后执行EXPIRE user:1000:profile 2592000,这样,Redis就会给这个钥匙挂上一个倒计时牌子,时间一到,它就自动把这个钥匙和对应的数据都扔掉了。

这里有个小细节,如果你在30天内,又去修改了user:1000:profile这个记录的值,比如更新了用户的昵称,在Redis的某些旧版本里,这个过期时间可能会被清除掉,它又变成永久的了,这显然不是我们想要的,更稳妥的做法是使用SET命令本身的可选参数,在写入数据的时候就直接设置过期时间,比如SET user:1000:profile "{"name":"张三"}" EX 2592000,这样一步到位,数据和过期时间绑定在一起写入,清晰又不容易出错。
除了EXPIRE,还有个EXPIREAT命令也很有用。EXPIRE是设置一个相对时间(从当前时间开始多少秒后过期),而EXPIREAT是设置一个绝对时间(在某个具体的时间戳过期),你想让一条促销信息在今年的双十一晚上12点整准时失效,你就可以算出那个时间点的时间戳,然后用EXPIREAT promotion:double11 1731340800(假设这个数字是双十一的时间戳),这在处理有明确截止时间的业务场景时特别方便。
那如果我们已经有一大堆永久key了,难道要一个一个手动去加过期时间吗?那肯定不现实,这时候就需要一些批量处理的技巧,一个常见的思路是“借鸡生蛋”,利用Redis的持久化机制,你可以先在一个测试环境,对生产环境的数据做一个备份(RDB文件),写一个小脚本,遍历这个RDB文件里的所有key(有一些开源工具可以解析RDB文件),对你的业务key进行判断,如果发现某个key没有设置过期时间,就模拟一个合理的过期时间,当前时间+一个随机分布的时长(比如半年到一年)”,为什么要随机分布?是为了避免所有key在同一时刻大量过期,给Redis带来不必要的压力,生成一个批量执行的脚本,然后再找业务低峰期,在从库或者主库上谨慎执行。

另一个更温和的批量处理方法是“访问时续期,不访问就淘汰”,我们可以写一个Lua脚本,在应用程序每次读取一个key的时候,都先检查一下它有没有TTL(过期时间),如果没有,就在返回数据的同时,给它设置一个过期时间,这样,那些被频繁访问的、活跃的key,会自动获得“续命”,而真正的“冷数据”,在下次被偶然访问到的时候会被加上TTL,如果一直没人访问,它就永远没机会被加TTL,最终会被我们后面要提到的另一种方式清理掉,这种方式对现有业务代码有侵入性,需要修改访问Redis的代码逻辑。
除了手动和半自动的方法,Redis从4.0版本开始,自身也提供了主动清理机制,就是MEMORY PURGE命令(依赖于Jemalloc分配器)和更重要的MEMORY USAGE分析,但这更多是底层机制,在架构层面,更通用的方法是开启Redis的volatile-lru或allkeys-lru这类最大内存淘汰策略,当你把Redis的最大内存限制设置好后,如果内存满了,Redis会根据策略自动淘汰一些key来腾空间。allkeys-lru会淘汰最近最少使用的key,不管它有没有设置过期时间,如果我们给大多数key都加上了过期时间,那么使用volatile-lru策略可能更合适,它只会在那些设置了过期时间的key中间进行淘汰,这样,即使我们的批量设置过期时间有遗漏,或者有些key确实需要永久保存,这个机制也能作为一个安全网,确保内存不会爆掉。
在做任何批量操作之前,有一步是绝对不能省的:备份和数据验证,你一定要先在测试环境反复演练,确认你的脚本不会误删重要数据,对于核心的、绝对不能丢的key,一定要有一个白名单机制,跳过它们,要和业务方充分沟通,评估设置过期时间后可能产生的影响,比如会不会有用户一年后回来发现数据没了造成投诉?这就需要业务上做一个权衡了。
给Redis的无过期时间记录加TTL,就像给仓库做一次大盘点和大扫除,目的就是让空间利用更高效,成本更低,方法上有直接下命令的,有利用命令特性一步到位的,有批量扫描处理的,也有借助Redis自身淘汰机制做兜底的,关键是根据自己业务的实际情况,选择一个组合拳,平稳地、安全地把这件“家务事”办好。
本文由芮以莲于2026-01-23发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://www.haoid.cn/wenda/84716.html
