根本原因是过期键清理与内存淘汰是两个独立机制;volatile-lru仅在内存满时从带TTL键中选LRU者淘汰,不保护未过期键,且已过期未清理的键仍参与LRU排序。

设置了过期时间的键仍被 volatile-lru 或 allkeys-lru 提前淘汰,根本原因不是“策略失效”,而是你混淆了 Redis 的两个独立机制:过期键清理(何时删掉已过期的键)和内存淘汰(内存满时删谁来腾空间)。它们触发条件、作用对象、执行时机完全不同。
volatile-lru 为什么没保护住你的过期键
volatile-lru 只在内存达到 maxmemory 限制时才启动,并且**只从设置了过期时间的键中选最久未使用的来淘汰**。它不保证“只要没过期就一定不被淘汰”。常见误判点:
- 你以为“有 TTL 就安全”——错。只要该键在采样中被判定为“最近最少使用”,哪怕还有 23 小时 TTL,也会被
volatile-lru淘汰 - 你用
EXPIRE设置了过期时间,但后续又用SET覆盖了同一个 key——新值默认无 TTL,原过期信息丢失,该 key 立即退出volatile-*策略候选池 - 你混用了
volatile-lru和allkeys-lru:如果配置的是allkeys-lru,那所有键(含带 TTL 的)都在淘汰池里,过期时间完全不构成保护
过期键没被及时清理,反而占着内存等 LRU 淘汰
Redis 不会主动扫全库删过期键。它靠“定期删除 + 惰性删除”配合:hz 默认 10(即每 100ms 抽样 maxmemory-samples 个键检测),而 maxmemory-samples 默认仅 5。这意味着:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 大量已过期但从未被访问的键,可能长期滞留在内存中,成为
allkeys-lru的淘汰候选 - 即使你用
volatile-lru,这些“已过期但未被清理”的键仍属于“设置了过期时间的键”,会被纳入 LRU 排序——而过期键通常访问频次极低,极易排在队首被优先淘汰 - 检查日志或监控时,
expired_keys指标增长缓慢,但evicted_keys却飙升,就是这个现象的典型信号
allkeys-lru 下,TTL 完全不参与决策
如果你配置的是 allkeys-lru(比如为了保热数据、不区分是否过期),那么 Redis 在淘汰时只看访问时间戳,TTL 值对排序零影响。此时:
- 一个刚写入、TTL 剩 1 天的键,如果自写入后从未被读取,它的 LRU 时间戳就是写入时刻——在活跃系统中极易比不过高频访问的短 TTL 键
-
redis-cli --stat中看到evicted_keys持续上涨,但expired_keys几乎不动,基本可断定是allkeys-lru+ 过期键积压共同导致 - 想验证:用
OBJECT IDLETIME key查看某键空闲秒数,再对比TTL key,若前者远大于后者,说明它“过期了但还活着”,正躺在 LRU 队列里等被踢
真正关键的不是“为什么淘汰”,而是你得明确:要保数据时效性,就依赖 expire 机制;要保内存水位可控,就靠 maxmemory-policy。两者叠加时,TTL 只决定“能否进 volatile 类策略池”,不提供任何豁免权。生产环境里,maxmemory-samples 10 和 hz 20 往往比换策略更能缓解“过期键卡在内存里等 LRU 清理”的尴尬。

















