Redis 过期键区分主动过期与访问时删除的实现方法
来源:17golang原创
时间:2026-09-19 23:07:29 324浏览 收藏
线上运维缓存的时候经常碰到这类怪事:监控里看键的剩余存活时间还没到0,下次请求过来读这个键直接返回nil,甚至有时候跨节点统计过期键的数量对不上,排查半天找不到手动删键的痕迹。这类现象本质上就是Redis对过期键用了两套独立的删除逻辑,区分后台主动过期清理、和用户访问键时才触发的被动删除,两种机制并行跑才会出现我们感知上的"数据不一致"。
缓存排查里最容易误判的一幕是:刚给 Redis 键设置了过期时间,过一会儿再看却发现键不见了;另一个低频键则像“过期了还占着位置”。这通常不是 EXPIRE 失效,而是 Redis 同时采用了访问时处理和后台主动清理两条路径。
官方文档地址:https://redis.io/docs/latest/commands/expire/
判断过期键先看 TTL:正数表示仍有剩余时间,-1表示键存在但没有过期时间,-2表示键不存在。不要把“TTL 到零的瞬间”和“后台删除完成的瞬间”当成同一件事。
- 客户端访问到已经到期的键时,Redis 会在访问路径上处理它。
- 没有再次访问的过期键,由后台周期性抽样清理,删除时刻不是业务定时器回调。
TTL适合秒级判断,PTTL适合毫秒级排查;-1与-2的含义完全不同。
先固定 TTL 的三种返回值
排查时不要先用业务复杂的 key。先建立三个最小状态:一个带 10 秒过期时间的键、一个永久键、一个不存在的键。下面的命令是示例检查顺序,注释说明每条命令想确认什么。
# 创建一个短期键,便于观察剩余时间 redis-cli SET demo:expire "v" # 设置 10 秒超时;返回 1 代表设置成功 redis-cli EXPIRE demo:expire 10 # 返回剩余秒数,通常是 9 到 10 之间 redis-cli TTL demo:expire # 创建一个没有过期时间的永久键 redis-cli SET demo:persistent "v" # -1 表示键存在,但没有关联 expire redis-cli TTL demo:persistent # 检查不存在的键;-2 与 -1 不要混淆 redis-cli TTL demo:missing
表格可以把结果直接固定下来:
| TTL 结果 | 键状态 | 排查含义 |
|---|---|---|
| 大于等于 0 | 存在过期时间 | 仍在倒计时,数值会随时间减少 |
| -1 | 键存在但无过期时间 | 检查是否被 SET、RENAME 或 PERSIST 改成持久键 |
| -2 | 键不存在 | 可能已过期、被 DEL,或根本没有创建成功 |

主动过期与访问时删除的分工
Redis 官方把过期处理分成两种方式。第一种是访问时处理:客户端执行读取或其他会触碰该 key 的命令,Redis 发现它已经超时,就把它视作过期并完成删除。第二种是主动过期:后台周期性从带过期时间的键中抽样,把已经到期的键清掉。
两条路径解决的是不同问题。访问时处理保证“读到过期值”这件事不会继续发生;主动清理则负责处理那些再也不会被访问的冷键。如果只依赖访问,冷键会长期占用过期索引和内存;如果只依赖后台逐个扫描,又会带来不必要的扫描成本。因此,低频键在过期后短暂留存于内部结构,并不等于业务读取会拿到旧值。

按低干扰顺序排查业务缓存
真实故障里最重要的是先记录状态,再做读取。读取本身可能触发访问时处理,所以不要只凭一次 GET 后看到的 nil 推断后台已经扫过该键。
# 先检查剩余时间,保留原始判断依据 redis-cli TTL cache:user:42 # 用 PTTL 补充毫秒级剩余时间,适合临界点排查 redis-cli PTTL cache:user:42 # 再判断 key 是否仍存在;-1、-2 的语义仍以 TTL 为准 redis-cli EXISTS cache:user:42 # 最后读取值;到期键可能在这一步被访问时处理 redis-cli GET cache:user:42
如果第一次 TTL 返回正数,紧接着 GET 返回空,先考虑两个可能:键在两条命令之间自然到期,或者客户端访问的是不同数据库、不同节点。若 TTL 返回 -1,不要继续等待过期,而要追查写入逻辑是否用不带 EX 选项的 SET 覆盖了原键;官方文档也说明,覆盖内容的命令可以清除原有超时。
时间精度和边界要单独记录
TTL 返回秒,适合业务级观察;PTTL 返回毫秒,适合判断是否正在跨过过期临界点。Redis 的过期信息以绝对时间保存,实例重启或加载持久化数据时,机器时钟仍然会影响“现在是否已到期”的判断,因此排查跨机器问题时要同时记录节点和系统时间。
复制场景也不要把副本上的观察等同于主节点的过期动作。常规复制中,过期由主节点处理并传播删除效果,副本等待主节点的删除操作。缓存业务只需要记住一个实用边界:过期策略决定“何时不再向客户端提供该键”,不承诺每个键都在同一毫秒从所有内部结构中消失。
最终检查清单可以压缩为:先 TTL,再用 PTTL 观察临界点;确认数据库和节点;检查写入命令是否覆盖或 PERSIST;最后才用 GET 验证业务值。
相关问题
TTL 返回 -1 是不是马上就会过期
不是。-1 表示键存在但没有过期时间,通常需要检查写入、覆盖或 PERSIST 逻辑。
TTL 返回 -2 能证明键是被过期删除的吗
不能。-2 只表示当前键不存在,也可能是 DEL、覆盖其他数据库或创建失败造成的。
过期时间到了,为什么内存监控不会同步下降
键会先在访问路径或主动抽样中被处理,内存分配器和监控采样又有自己的回收与刷新节奏,二者不是同一时刻。
什么时候应该使用 PTTL
当问题发生在秒级临界点、需要判断两个命令之间是否已经跨过到期时刻时,使用 PTTL;普通缓存巡检使用 TTL 即可。
-
117 收藏
-
426 收藏
-
171 收藏
-
113 收藏
-
195 收藏
-
223 收藏
-
218 收藏
-
436 收藏
-
493 收藏
-
440 收藏
-
279 收藏
-
361 收藏
-
340 收藏
-
277 收藏
-
415 收藏
-
135 收藏
-
215 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习