极简工具箱

免费在线工具 · 本地计算 · 无需上传

Redis 过期键是怎么删的:TTL、惰性删除与内存回收

给 key 设了 1 小时过期,两小时后 `INFO memory` 显示内存没降——不是过期没生效,而是「过期」和「释放内存」不是同一时刻发生的。

广告位(百度联盟)

一、设置与查询

EXPIRE user:1:session 3600      # 秒
PEXPIRE user:1:session 3600000  # 毫秒
EXPIREAT user:1:session 1767225600   # 到某个时间戳
TTL user:1:session              # 剩余秒数,-1 表示永不过期,-2 表示 key 不存在
PERSIST user:1:session          # 去掉过期时间
SET cache:k v EX 3600           # 设置值的同时指定过期

SET 时带 EX 是原子操作,比 SET 完再 EXPIRE 更安全——两步之间如果进程挂掉,就会留下一个永不过期的 key。

二、两种删除策略

Redis 不会为每个 key 起一个定时器(那样开销太大),而是两条路配合:

惰性删除:访问某个 key 时先检查是否过期,过期就删掉并返回空。好处是零额外开销,坏处是不访问就永远不删。

定期删除:每 100 毫秒随机抽取一部分设了过期时间的 key 检查,删掉过期的。抽样而非全扫,所以清不完所有过期键。

两者结合的结果:过期键最终会被清理,但时间不确定。这就是「TTL 到了内存没立刻降」的原因。

三、内存为什么没降

几个可能:

  1. 大量过期键还没被抽样到(尤其是设置了过期时间的 key 特别多时)。
  2. 内存碎片:删了数据,分配器没把内存还给操作系统。看 INFO memory 的 mem_fragmentation_ratio,大于 1.5 可考虑 activedefrag yes(Redis 4.0+)。
  3. 客户端输出缓冲区占用:慢客户端积压了大量待发送数据。
  4. 主从复制缓冲区、AOF 重写缓冲区。

排查顺序:INFO memory → INFO clients → MEMORY DOCTOR。

四、过期键与持久化

这意味着:不要从从库读取强依赖过期语义的数据(比如一次性验证码),要么读主库,要么在应用层再校验一次。

五、缓存过期的实践建议

六、清理大量 key 的正确方式

# 错误:KEYS 阻塞主线程
KEYS user:old:*

# 正确:SCAN 渐进遍历 + UNLINK 异步删除
SCAN 0 MATCH "user:old:*" COUNT 200
UNLINK user:old:1001

删除大 key 一定要用 UNLINK,DEL 是同步释放内存,几百万元素的 Hash 能卡住几百毫秒。

七、监控

INFO keyspace          # 各库 key 数量与过期比例
INFO stats             # expired_keys 计数
CONFIG GET maxmemory-policy

expired_keys 持续高速增长说明 TTL 设得太短或写入量太大,需要重新评估缓存策略。

小结

过期靠「惰性 + 定期抽样」两条路,TTL 到点不等于内存立刻释放;从库不主动删过期键;TTL 要加随机抖动;清大批 key 用 SCAN + UNLINK。

本文相关工具(本地计算,无需上传)

更多使用教程