bigywater.tech
Redis · 20

过期 Key 为什么不会立刻消失

用DEBUG SET-ACTIVE-EXPIRE完全禁用后台过期扫描,验证一个逻辑上已过期的key还占用着内存,直到一次GET才触发清理。

48字节
禁用后台扫描后,过期key还占用的内存
GET立刻清理
被动过期是保底机制
抽查式清理
后台扫描是分批进行,不是即时全量

做的事:关掉后台清理,看一个"过期"的key会怎样

Redis有一个周期性运行的后台线程,专门抽查过期的key并删除它们——先把它关掉。

操作DEBUG SET-ACTIVE-EXPIRE 0 -- 完全禁用后台过期扫描 SET k1 v1 PX 100 sleep 1秒(TTL 100ms早就过去了)

验证:TTL过期1秒后,key还占用着的内存

没有后台线程来清理,会发生什么。

结果1秒后(TTL早已过期100ms),active-expire已禁用: DBSIZE → :1(key还在!) MEMORY USAGE k1 → :48字节(占用着物理内存)

验证了:一个"逻辑上"早就该消失的key,如果没有任何机制去主动清理它,会一直躺在内存里——TTL 只是给这个key打上了一个"过期时间戳",本身不会触发任何清理动作,得靠别的机制来做这件事。

验证:一次GET,立刻让它消失

被动过期(lazy expire)是保底机制。

结果GET k1 → 返回空(nil) DBSIZE → :0(立刻被清理了)

这次的GET访问,触发了Redis的"惰性检查":命令执行前先看一眼这个key的TTL是不是已经过了,过了就先删除它,再假装这个key从来没存在过,返回空结果。这就是为什么哪怕完全没有后台线程,Redis也能保证"你永远不会读到一个已过期的值"——被动过期是的最后一道保险。

验证:重新打开后台扫描,即使不访问也会被清理

正常情况下(不手动关掉它),后台扫描才是主要在工作的机制。

结果DEBUG SET-ACTIVE-EXPIRE 1 -- 恢复后台扫描 SET k2 v2 PX 100 sleep 1秒,完全不访问k2 DBSIZE → :0(自动被清理了,没有任何人访问过它)
的完整逻辑是:Redis有一个后台线程,会周期性地从"设了过期时间的key"里随机抽一小批出来检查,过期就删——这个抽查是分批、限时进行的,不会一次性扫描全部key卡住服务,所以一个过期的key不一定立刻被清理,可能要等下一轮抽查轮到它;被动过期则保证哪怕后台一直没抽到它,只要有人来访问,也不会读到脏数据。两者配合,才是"过期key为什么不会立刻消失,但迟早会消失"完整的答案。

分步演示:关掉后台清理会发生什么

每一步都是本文上面用redis-cli跑出来的原始结果。

没有后台扫描,过期key会一直占着内存
这一步新验证的状态

    本文用到的工具

    • 本机 Redis 8.10.1 单节点
    • DEBUG SET-ACTIVE-EXPIRE 0/1:控制后台过期扫描的开关
    • MEMORY USAGE:验证过期key依然占用内存
    京ICP备2026067502号-1京公网安备11010502063487号