在Redis的实际使用中,给key设置过期时间是非常常见的操作,比如缓存自动失效、验证码五分钟有效、登录态保持七天等场景都离不开过期时间。但不少开发者对过期删除机制存在误解,以为到了时间key就会立刻消失,结果线上出现了内存占用异常或者读到已过期数据的问题。本文将系统讲解Redis中设置过期时间的各类命令、TTL查看方式的返回值含义,以及底层过期删除策略的原理。

一、设置过期时间的几种命令
Redis提供了多个与过期时间相关的命令,最常用的是EXPIRE,它以秒为单位给已存在的key设置存活时间。与之对应的PEXPIRE则以毫秒为单位,适合需要更精细控制的场景。除了设定相对时间,Redis还支持设定绝对时间点,EXPIREAT接受一个Unix时间戳(秒),PEXPIREAT接受毫秒级时间戳,表示key在指定时刻过期。
127.0.0.1:6379> SET session:user1001 "abc" OK 127.0.0.1:6379> EXPIRE session:user1001 300 (integer) 1 127.0.0.1:6379> PEXPIRE session:user1001 300000 (integer) 1 127.0.0.1:6379> EXPIREAT session:user1001 1735689600 (integer) 1 127.0.0.1:6379> PERSIST session:user1001 (integer) 1
需要注意的是,如果对一个不存在的key执行EXPIRE,命令会返回0而不是报错。另外PERSIST命令可以移除key的过期时间,让它变成永久key,执行成功返回1。还有一个容易忽略的细节:对已经设置过期时间的key再次执行EXPIRE,会重置过期时间为新的值,而不是保持原来的时间。
二、SET命令与EXPIRE的原子性问题
很多教程会教大家先用SET写入key,再调用EXPIRE设置过期时间。这种写法在单客户端环境下没有问题,但如果两条命令之间出现异常,比如应用崩溃或网络中断,就可能出现一个没有过期时间的永久key,长期积累会占满内存。为了解决这个问题,Redis从2.6.12版本开始允许在SET命令中直接携带过期参数。
127.0.0.1:6379> SET cache:product:88 "data" EX 3600 OK 127.0.0.1:6379> SET cache:product:88 "data" PX 60000 NX OK
EX表示秒,PX表示毫秒,配合NX或XX选项使用时也非常方便,例如实现分布式锁时常用的SET lock:key value NX EX 10就是一条原子命令。建议在业务代码中养成使用带过期参数的SET的习惯,避免两步操作带来的隐患。此外,对string类型还可以使用SETEX和PSETEX这两个快捷命令,效果等同于SET加EXPIRE的组合,但功能上是原子的。
三、TTL与PTTL的返回值含义
设置过期时间之后,查看剩余存活时间使用TTL命令,返回值以秒为单位;PTTL则返回毫秒级精度。这两个命令的返回值有三种情况,理解清楚才能正确排查问题。
| 返回值 | 含义 |
|---|---|
| -2 | key不存在(从未创建或已被删除,包括已过期被回收) |
| -1 | key存在但没有设置过期时间,即永久key |
| 大于0的整数 | 剩余存活时间 |
特别要区分-1和-2的含义,这是排查问题时的常见困惑点。当你发现TTL返回-1,说明这个key不会自动过期,需要检查业务代码是否遗漏了过期设置,或者是否被PERSIST清除了过期时间。还有一个隐蔽的情况:某些命令执行后会清除key的过期时间,例如对list执行LPUSH或对string执行SET覆盖旧值,都会导致原本的过期时间被移除,key重新变成永久key。
127.0.0.1:6379> SET code:phone138 "123456" EX 300 OK 127.0.0.1:6379> TTL code:phone138 (integer) 298 127.0.0.1:6379> GET code:phone138 "123456" 127.0.0.1:6379> SET code:phone138 "654321" OK 127.0.0.1:6379> TTL code:phone138 (integer) -1
上面的例子清楚地展示了覆盖写入后过期时间丢失的过程。验证码场景中如果不注意这一点,可能出现验证码长期有效的安全漏洞,正确做法是每次写入都带上EX参数。
四、过期key的删除策略与内存影响
很多人以为过期时间一到Redis会立刻删除key,实际上Redis采用的是惰性删除与定期删除相结合的策略。惰性删除是指当客户端访问某个key时,Redis先检查它是否已过期,如果过期就立即删除并返回空结果。这种方式的问题是,如果一个key过期后始终没有被访问,它会一直残留在内存中。
为了弥补这个缺陷,Redis还会定期删除:默认每秒执行10次(可通过hz参数调整),每次从设置了过期时间的key中随机抽取一部分检查,删除其中已过期的。如果这一轮过期key的比例超过25%,会继续抽查,形成一个自适应的循环。这种设计在CPU占用和内存回收效率之间做了平衡。
在集群和主从复制场景下还有更多细节:从节点不会主动删除过期key,而是等待主节点删除后同步DEL命令,因此读取从节点时如果发现key已过期,会直接返回空而不返回value。在持久化方面,RDB快照保存时会把已过期的key跳过,载入时也会忽略过期key;AOF重写时同样会剔除过期key,但AOF追加阶段如果key过期被删除,会记录一条DEL命令保证一致性。理解这些机制,有助于在内存暴涨或数据不一致问题出现时快速定位是否与过期策略有关。
五、实践建议与常见坑总结
综合上面的原理分析,给出几条实践建议。第一,所有缓存类key务必设置过期时间,避免内存无限增长,即使业务上不确定合适的时长,也建议设置一个兜底值。第二,写代码时优先使用SET key value EX seconds的原子写法,而不是SET加EXPIRE两步操作。第三,排查问题时先看TTL返回值,-1和-2分别指向不同的原因,不要混为一谈。
第四,注意覆盖操作会重置过期时间这个特性,特别是在更新缓存内容时,记得每次更新都显式带上过期参数。第五,如果需要批量查看key的剩余时间,Redis没有原生的批量TTL命令,可以在客户端用pipeline循环执行TTL,减少网络往返开销。第六,对于要求精确到某一时刻过期的业务(如秒杀活动结束),使用EXPIREAT比计算相对秒数更直观也更不容易出错。
掌握Redis过期时间的设置与查看方法,不仅是记住几条命令,更重要的是理解背后的删除策略与各种边界行为。这样才能在缓存设计、内存治理等场景中做出正确的决策,避免线上问题。
Redis过期时间Redis TTLRedis key过期策略修改时间:2026-09-01 04:20:53