Redis不立刻删除过期key是因定时器或延迟队列会破坏单线程高吞吐设计;采用惰性删除(读时检查)和定期删除(每秒10次抽检,每次最多25轮,耗时≤1ms)平衡性能与内存。

为什么 Redis 不会立刻删掉过期 Key
因为立刻删除在工程上不可行——不是做不到,而是代价太高。如果每个 key 都配一个定时器(比如调用 setTimer),当有百万级 key 同时设了 EXPIRE,Redis 主线程会被大量定时器回调压垮;修改过期时间还要重置定时器,进一步加剧开销。延迟队列方案也类似:维护有序结构、调整位置、引入额外线程同步,全都会破坏 Redis 单线程 + 高吞吐的设计根基。
GET 或 EXISTS 时触发的惰性删除怎么工作
这是最轻量的“补刀”动作:每次执行读命令前,Redis 会检查目标 key 是否在 db->expires 字典中登记了过期时间,且当前时间已超过该时间戳。一旦确认过期,就当场调用 dbDelete() 删除它,并返回 (nil)。
- 只影响本次访问的那一个
key,不扫描其他数据 - 从不主动触发,所以冷数据(比如长期不用的验证码)可能永远留在内存里
- 主从复制下,从节点不会自己惰性删除——它依赖主节点发来的
DEL命令同步删除
定期删除任务 activeExpireCycle() 怎么控制开销
这个函数藏在 serverCron() 里,默认每秒跑 10 次(由配置项 hz 控制)。它不遍历全部 key,而是:
- 每次随机抽检 20 个设置了过期时间的
key(来自db->expires) - 如果其中过期比例 ≥ 25%,就立即再抽一轮,最多连续执行 25 次(避免单次耗时过长)
- 遍历完当前
db后,下次从下一个db继续,保证压力分散 - 整个过程有硬性耗时限制(默认不超过 1ms),超时即退出
这意味着:哪怕你把 hz 调到 100,它也不会“扫光”所有过期 key,只是提高抽检频率——本质是用可控的 CPU 换内存水位稳定。
真正“快速”清理大量过期 Key 的实操建议
别指望靠默认机制“快速”清空——惰性太懒,定期又太克制。生产环境遇到大批量过期积压(比如缓存预热后集中失效),得人工干预:
- 用
SCAN配合TTL批量识别:先SCAN 0 MATCH * COUNT 1000,再对每个结果跑TTL判断,脚本批量DEL - 临时调高
hz(比如到 50),但必须监控 CPU 使用率,尤其在流量高峰前回退 - 改用
volatile-ttl淘汰策略 +maxmemory限容,让内存快满时优先踢掉快过期的key - 避免让过期时间集中在整点(如全部设成
EX 3600),改用随机偏移:EX 3600 + random(300)
最常被忽略的一点:定期删除只作用于设置了过期时间的 key,而 DEL、FLUSHDB 这类命令才是真正的“立刻删除”,但它们不区分是否过期——该删的删,不该删的也删了。


















