谈谈 Redis 的过期策略?

#中间件#面试八股

谈谈 Redis 的过期策略?

结论:Redis 用惰性删除 + 定期删除两种方式回收过期 key——访问时才判断是否过期,同时每 100 ms 抽样清理一部分。这套组合是"CPU 与内存的折中":既不为每个 key 起定时器,也不保证到期立刻释放。因此过期不等于内存立即释放,业务上必须把"逻辑过期"和"物理删除"分开理解。

一、三种可能的设计,为什么只选两种

策略做法问题
定时删除每个 key 到期就起定时器删海量 key 时定时器本身吃掉大量 CPU 与内存,Redis 不采用
惰性删除访问 key 时检查,过期则删并返回空不访问就永不删除,内存泄漏
定期删除周期性随机抽样检查并删除抽样不彻底,只能近似
组合(实际采用)惰性 + 定期兼顾 CPU 与内存,代价是"删除有延迟"

二、定期删除的抽样细节

每 100 ms 触发一次(hz 默认 10,即 serverCron 周期)
  │
  ├─ 从每个 db 的 expires 字典中随机抽 20 个 key
  ├─ 删除其中已过期的
  ├─ 若过期比例 > 25%,再抽 20 个继续(同一轮)
  └─ 单轮有时间上限(约 25% CPU 时间),超时就让出,下轮再来

这个"25% 阈值 + 时间上限"的设计保证:过期 key 密集时清理得快,但绝不会长时间霸占主线程。副作用是——大量 key 同时过期(集中过期)时,清理会反复触发,表现为延迟毛刺,这也是"TTL 要加随机抖动"的原因。

惰性删除路径(读命令执行时):
  lookupKeyRead(key)
      ├─ 未过期 → 返回
      ├─ 已过期 → 删除该 key,返回 nil(同时向从库/AOF 传播 DEL)
      └─ 从库不主动过期,等主库的 DEL 传播

三、观测与配置

# 看某个 key 的剩余存活时间(-1 永不过期,-2 不存在)
redis-cli TTL order:1001
redis-cli PTTL order:1001

# 统计过期 key 数量与内存
redis-cli INFO keyspace
redis-cli INFO memory | grep -E "used_memory_human|mem_fragmentation_ratio"

# 采样观察"已过期但仍在内存"的 key
redis-cli --scan --pattern 'session:*' --count 1000 | head -50 | \
  xargs -I{} redis-cli TTL {}
# 定期删除的频率与力度
hz 10
dynamic-hz yes
# 内存到顶后的兜底:淘汰策略(过期 key 不是"被淘汰",是被清理)
maxmemory 4gb
maxmemory-policy allkeys-lru

四、几个容易答错的点

  • 过期 key 还在占内存:used_memory 不会因为 TTL 到期立刻下降,要等惰性或定期删除命中;MEMORY USAGE 仍能查到,TTL 返回负数。
  • 从库的过期:从库不主动删除过期 key,只等主库传播 DEL/UNLINK;主从延迟期间读到的是"逻辑过期但仍存在"的数据,关键业务要容忍或用主库读。
  • 持久化文件里的过期 key:RDB 加载时主库会丢弃已过期 key,从库则保留(等主库 DEL);AOF 重写时也会剔除已过期 key。
  • EXPIRE 覆盖 SET:SET 会清掉原有 TTL,改值又想保 TTL 要用 KEEPTTL。
  • PERSIST:把 key 变永久,容易埋下内存泄漏,使用前先确认业务真的需要。

五、常见追问

  • 问:过期策略和淘汰策略有什么区别?答:过期策略处理"已到 TTL 的 key",目的是回收该删的数据;淘汰策略处理"内存到 maxmemory 时该挤掉谁",目的是控制内存上限。两者独立生效,可以同时存在。
  • 问:为什么不用定时器精确删除?答:每个 key 一个定时器在百万级 key 下内存与调度成本都不可接受,且 Redis 单线程无法承受频繁的定时回调。
  • 问:集中过期怎么避免?答:给 TTL 加随机抖动(如 30 分钟 ± 5 分钟)、把预热数据的过期时间打散、对超热 key 用逻辑过期(value 里带过期时间,异步重建)。
  • 问:hz 调大能加快过期清理吗?答:能提高抽样频率,但也会增加 CPU 占用与延迟抖动;一般保持 10,配合 dynamic-hz 在空闲时自动提高。
  • 问:内存没到 maxmemory,为什么还是看到内存不降?答:可能是过期 key 未被采样到、碎片率高(mem_fragmentation_ratio > 1.5 可考虑 activedefrag),或有大量永不过期的 key。
  • 问:大 key 过期会阻塞吗?答:会。释放大集合是同步的,可能造成百毫秒级卡顿;应使用 UNLINK(异步释放)或在业务低峰让大 key 过期。

小结

  • 过期删除 = 惰性 + 定期抽样,是"够用就好"的近似方案,不承诺实时。
  • 内存不降先区分三件事:没采样到、碎片、永不过期。
  • 设计侧主动做两件事:TTL 加抖动、避免大 key 与集中过期。
笔记加载中…