web学习笔记编程技术学习与分享

缓存穿透、击穿、雪崩的区别与对策

2026 年 08 月 02 日约 3 分钟阅读 后端与数据库

这三个词经常被混着说,但它们描述的是三种不同的问题:穿透是查不存在的数据,击穿是热 key 过期,雪崩是大面积失效。

穿透:查询不存在的数据

请求的 key 在缓存和数据库里都不存在,每次都打到数据库。攻击者可以构造大量随机 ID 扫库。

对策:

// 1. 空值缓存:查不到也写入短 TTL 的占位
$redis->setex('user:' . $id, 60, json_encode(['__null__' => 1]));

// 2. 布隆过滤器:肯定不存在直接拒绝(有一定误判率)
if (!$bloom->mightContain('user:' . $id)) return null;

另外在入口做参数校验(ID 格式、范围),能挡掉大部分恶意流量。

击穿:热 key 过期瞬间

某个被高频访问的 key 恰好过期,瞬时大量请求同时回源,数据库被打出尖峰。

对策是「只放一个请求去回源」,其余等待:

$lockKey = 'lock:' . $k;
if ($redis->set($lockKey, 1, ['nx', 'ex' => 5])) {
    try {
        $v = loadFromDb($k);
        $redis->setex($k, 3600, $v);
    } finally {
        $redis->del($lockKey);
    }
} else {
    usleep(100000);
    return $redis->get($k);
}

更省事的做法是逻辑过期:缓存不设 TTL,value 里带过期时间,发现过期由单个线程异步重建,期间返回旧值。

雪崩:大量 key 同时失效

两类成因:一是同一批 key 相同 TTL 集中过期;二是 Redis 实例宕机,全部流量涌向数据库。

对策:

  • TTL 加随机抖动:$ttl = 3600 + mt_rand(0, 600);
  • 多级缓存:本地缓存挡第一波,Redis 挡第二波;
  • 熔断限流:数据库前加限流,宁可降级也不雪崩;
  • 高可用:哨兵或集群模式,避免单点。

快速对照

问题 触发条件 核心对策
穿透 数据不存在 空值缓存 / 布隆过滤器 / 参数校验
击穿 热 key 过期 分布式锁 / 逻辑过期
雪崩 批量失效或宕机 TTL 抖动 / 多级缓存 / 熔断

排查线上问题时,先看监控里「缓存未命中率的变化」和「数据库 QPS 的尖峰是否与某个 key 的过期时间对齐」,三个问题自然能区分开。

Redis缓存高可用