Redis面试题3:解析缓存穿透、击穿与雪崩

在千万级 QPS 的高并发系统中,缓存(Cache)是提升性能的银弹。然而,当流量洪峰真正到来时,缓存系统的任何一个微小漏洞都可能导致数据库瞬间崩溃。

作为后端工程师,我们不仅要会用 Redis,更要懂得如何设计一个“打不穿”的缓存架构。本文将系统讲解“缓存穿透、击穿、雪崩”的本质区别,并从工程落地角度给出解决方案。

一、概念辨析:三座大山

很多开发者容易混淆这三个概念,我们用一个“防弹衣”的生动比喻来区分它们:数据库(DB)是人,缓存(Cache)是穿在人身上的防弹衣。

1. 缓存穿透

  • 现象:子弹打在了防弹衣没有覆盖的地方,直接伤到了人。
  • 定义:请求的数据在缓存中不存在,在数据库中也不存在
  • 后果:流量直接打穿缓存和 DB,通常由恶意攻击(如查询 ID=-1)或代码 Bug 导致。

2. 缓存击穿

  • 现象:防弹衣完好,但某一个点被机枪持续扫射,最终被烫穿。
  • 定义:某个极度热点 Key(如秒杀商品)在失效的瞬间,海量并发请求同时涌入。
  • 后果:DB 瞬间压力过载,常见于电商大促、微博热搜。

3. 缓存雪崩

  • 现象:由于不可抗力,防弹衣整个脱落了。
  • 定义:海量 Key 在同一时间集中过期,或缓存节点宕机。
  • 后果:原本由缓存承担的流量全部转移到 DB,导致 DB 宕机。

核心差异对比表

特性 缓存穿透 缓存击穿 缓存雪崩
数据状态 DB 和 Cache 都没有 DB 有,Cache 刚好失效 DB 有,Cache 大面积失效
Key 的特征 很多非法的 Key 少数几个热点 Key 海量的 Key
流量特征 恶意流量或代码 Bug 高并发读 正常流量的瞬间转移

二、解决方案:构建防御纵深

要解决这三个问题,不能指望单一的技术手段,而需要构建多层防御体系。

1. 防御穿透:布隆过滤器 (Bloom Filter)

针对缓存穿透,最有效的手段是在访问缓存之前,先校验 Key 是否可能存在

  • 机制:利用位数组和多次 Hash,判断 Key 是否存在。
  • 特点:说“不存在”则一定不存在(100% 准确);说“存在”则可能不存在(有误判率)。
  • 误判原因(为什么不准?):“位置共享”。你查询的 k 个位置全是 1,但这可能是其他几个元素凑巧填满的,不是你留下的。
  • 进阶:使用布谷鸟过滤器 (Cuckoo Filter),相比布隆过滤器,它支持删除操作,且空间利用率更高。

Java 实战 (Redisson)

// 依赖:org.redisson:redisson
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("idBlockList");

// 初始化:预计 1000 万数据,误差率 1%
bloomFilter.tryInit(10000000L, 0.01);

// 业务使用
if (!bloomFilter.contains("user_123")) {
    return null; // 直接拦截,不查 Redis 也不查 DB
}

2. 防御击穿:请求合并 (SingleFlight)

对于热点 Key 失效导致的击穿,核心思路是:哪怕有一万个并发请求,只允许一个去查数据库,其他人等待结果共享。

在 Go 语言中,singleflight 是处理此类问题的神器。

Go 实战 (SingleFlight)

import "golang.org/x/sync/singleflight"

var g singleflight.Group

func GetProduct(id string) (string, error) {
    // Do 方法确保针对同一个 id,同时只有一个执行逻辑在跑
    v, err, _ := g.Do(id, func() (interface{}, error) {
        // 只有第一个到达的请求会执行这里(查 DB)
        return db.Query(id) 
    })
    
    if err != nil {
        return "", err
    }
    return v.(string), nil
}

3. 防御雪崩:随机过期与多级缓存

  • 随机 TTL:在设置过期时间时,加上一个随机值(如 1-5 分钟),避免集体失效。
  • 多级缓存:L1 本地缓存(Guava)+ L2 分布式缓存(Redis)。即使 Redis 挂了,本地缓存还能抗住短时间的热点流量。

三、工程化:监控与预警

没有监控的系统就是在“裸奔”。要预防这些问题,必须建立完善的指标监控(Metrics)。

  1. 缓存命中率 (Cache Hit Rate)
    • 异常:如果命中率从 99% 骤降到 60%,往往是雪崩的前兆。
  2. 空查次数 (DB Null Count)
    • 异常:如果 DB 返回 Null 的次数激增,说明遭到了缓存穿透攻击。
  3. 回源 QPS (Back-to-Source QPS)
    • 异常:缓存层流量平稳,但 DB 流量暴涨,需排查是否有大 Key 过期。