Redis内存淘汰策略如何设置才能减少缓存穿透风险?

作者:袖梨 2026-08-10

内存淘汰策略本身不能减少缓存穿透风险,选错策略反而会加剧问题;缓存穿透是请求查不到数据(缓存和DB均无),而淘汰策略仅作用于已存在的key,对根本不存在的key无效,二者作用域完全不同。

直接说结论:内存淘汰策略本身不能减少缓存穿透风险,选错策略反而会加剧问题。 缓存穿透是请求查不到数据(缓存和 DB 都没有),而淘汰策略只管“已有数据该删谁”,两者作用域完全不同。强行用 volatile-ttlallkeys-lru 去“防穿透”,属于误用。

为什么淘汰策略对缓存穿透无效?

缓存穿透的本质是「空查询放大」——比如恶意构造大量不存在的 user_id=999999999 请求,这些 key 本来就没存过,自然不会被任何淘汰策略处理。Redis 根本没机会“淘汰”它们,因为它们压根不在内存里。

常见误解是:以为设置 volatile-ttl 让 key 快过期,就能“提前清掉潜在穿透请求”。但问题在于:没存过的 key 就没有 TTL,也不会被纳入淘汰候选集。

  1. 淘汰策略只作用于已存在的 key
  2. 穿透请求对应的 key 在 Redis 中根本不存在(EXISTS 返回 0)
  3. TTL 命令对不存在的 key 返回 -2,不是 -1

哪些淘汰策略可能让穿透更难察觉?

某些策略会让空查询行为更隐蔽,间接拖慢问题暴露速度:

  1. noeviction:写失败会立刻报错 (error) OOM command not allowed when used memory > 'maxmemory',容易发现容量瓶颈,但不解决穿透
  2. volatile-lruvolatile-ttl:如果误把空结果缓存成 NULL 并设了过期时间,这些 NULL 值会被纳入淘汰范围,导致后续真实请求命中空缓存,掩盖了穿透源头
  3. allkeys-random:随机淘汰可能踢掉有效缓存,增加 DB 查询概率,放大穿透影响

注意:NULL 缓存必须显式写入(如 SET user:999999999 "null" EX 60),Redis 不会自动存空值。

真正该做的三件事(和淘汰策略无关)

防穿透要绕开淘汰机制,从请求入口和数据存在性判断入手:

  1. 用布隆过滤器(Bloom Filter)前置拦截:初始化时把所有合法 user_id 加入 Redis 的 Bloom 结构,请求先 BF.EXISTS,返回 0 就直接拒绝
  2. 缓存空对象时明确控制生命周期:写 SET user:999999999 "" EX 300,避免用 EXPIRE 单独设 TTL,防止因淘汰策略误删后又反复穿透
  3. 监控 keyspace_hitskeyspace_misses 指标:如果 keyspace_misses 突增且对应 key pattern 高度离散(如 user:*9999*),基本就是穿透信号

淘汰策略只负责“有数据时怎么删”,穿透防御得靠“没数据时怎么拦”。别指望 maxmemory-policy 解决它,那是给错对象递刀子。

相关文章

精彩推荐