先说结论
过期删除处理 TTL 到期的 Key,Redis 通过访问时惰性检查和后台主动采样删除组合控制成本;内存淘汰则在达到
maxmemory后,根据 noeviction、LRU、LFU、random 或 TTL 等策略为新写入腾出空间。
过期 Key 不保证到期瞬间立刻物理删除,因此监控内存时要理解后台清理节奏。
淘汰策略
策略还分为 allkeys 和 volatile 范围:前者在所有 Key 中选择,后者只在设置 TTL 的 Key 中选择。纯缓存常用 allkeys-lru 或 allkeys-lfu;缓存与不可丢数据混用会让策略难以设计,最好拆分实例。
过期删除不是定时精确删除
Redis 不会为每个 Key 创建一个定时器。访问 Key 时会检查过期时间并惰性删除;后台周期性抽样带 TTL 的 Key,发现一定比例已过期就继续清理。这种组合避免为海量 Key 维护精确时间轮,但意味着过期 Key 可能短暂留在内存中。
因此业务不能把“TTL 到 0”理解为那一毫秒一定释放内存,也不能依赖过期作为毫秒级任务调度。需要精确延时执行时应使用延时队列或持久化任务系统。
maxmemory 触发后的行为
当 Redis 尝试分配内存且超过 maxmemory,才会按配置尝试淘汰或拒绝写入。不同策略意味着不同业务后果:
| 策略类型 | 含义 | 适合场景 |
|---|---|---|
| noeviction | 不淘汰,写命令报错 | 不能静默丢数据的实例 |
| allkeys-lru/lfu | 所有 Key 中近似淘汰 | 纯缓存 |
| volatile-lru/lfu | 只淘汰带 TTL 的 Key | 明确只有缓存 Key 设 TTL 的混合场景 |
| allkeys-random | 随机淘汰 | 很少作为首选,成本低但命中率不可控 |
| volatile-ttl | 优先淘汰更快过期 Key | TTL 与价值强相关时 |
如果选择 volatile 策略但大量缓存忘记设置 TTL,候选集合可能很小,写入仍会失败。策略配置必须与写入规范一起评审。
LRU 和 LFU 的现实含义
Redis 使用近似采样算法,不维护全量严格的访问排序。LRU 偏好最近访问,LFU 偏好长期频繁访问,LFU 还会对计数进行衰减以避免历史热点永久霸占缓存。采样大小、访问分布和内存压力都会影响实际淘汰结果。
选型前应看业务数据的访问曲线:短期突发热点可能更适合 LRU,长期稳定热门内容可能受益于 LFU;最可靠的依据是线上命中率、淘汰量和回源成本,而非算法名称。
TTL 设计方法
TTL 应反映数据的新鲜度、重建成本、写入失效机制和容量目标。可以用“基础 TTL + 随机抖动”打散到期,但不要为了抖动让低价值数据长期占内存。对强一致写后读,TTL 不能替代主动失效;对逻辑过期数据,物理 TTL 应比最大允许陈旧时间更长一些作为安全清理。
商品详情:30 分钟 + 0~5 分钟抖动
登录验证码:5 分钟且单次使用后立即删除
分布式锁:仅覆盖临界区预期时长,必须有续期/超时策略
监控与容量预案
持续观察 used_memory、maxmemory、mem_fragmentation_ratio、evicted_keys、expired_keys、keyspace_hits/misses、命令延迟和回源 QPS。淘汰开始持续增长并不只是 Redis 问题,往往说明容量预算、TTL 设置或流量模型发生变化;应提前扩容、清理低价值数据或调整应用缓存策略,而不是等写入大量报错。
TTL 设计
TTL 应来自业务新鲜度要求,并增加随机抖动避免集中到期。更新 TTL 时注意覆盖写是否保留或清除原过期时间;使用命令时应明确版本语义并测试。
容易踩坑的地方
Redis 的 LRU/LFU 通常是近似算法,不会维护全量精确排序。设置淘汰策略也不是容量治理的替代品,频繁驱逐会造成命中率下降和数据库压力。
常见问题
追问:怎么判断正在发生严重淘汰?
观察 evicted_keys 增速、命中率、内存使用、写入错误和下游回源量,并关联实例 maxmemory 与业务流量变化。
追问:EXPIRE 会覆盖已有 TTL 吗?
通常会按命令语义更新过期时间,某些带条件选项的版本可限制仅在无 TTL、已有 TTL 等条件下生效。关键业务应明确使用的命令版本并写测试。
追问:删除一个大 Key 为什么会卡顿?
某些复杂类型释放大量元素需要较长时间,可能阻塞事件循环。可使用异步释放、渐进删除或按业务维度拆小 Key。
追问:缓存实例可以存永久 Key 吗?
可以,但在 allkeys 淘汰下它仍可能被淘汰;在 volatile 策略下它可能挤占内存却不成为候选。永久业务状态和缓存通常应分实例或至少分清可靠性边界。