Redis「缓存穿透」是一个很经典的问题,本质是:请求的数据在缓存和数据库里都不存在,导致每次请求都会直接打到数据库。

一、什么是缓存穿透

场景:

  • 用户请求一个 不存在的 key(比如 id = -1 或乱造的 id)
  • Redis 查不到 → 去数据库查
  • 数据库也查不到 → 返回空
  • Redis 不缓存这个空结果

结果就是,每次请求都会重复打数据库

如果被恶意利用(比如刷不存在的 ID),数据库会被打爆。


二、常见解决方案

1.缓存空值(最简单有效)

做法:

  • 数据库查不到 → 也写入 Redis
  • 但存一个空值(比如 null 或特殊标记)
  • 设置较短过期时间(如 60 秒)
1
2
3
if data == nil {
    redis.set(key, "NULL", 60s)
}

优点:

  • 立刻生效
  • 实现简单

缺点:

  • 会占用少量缓存空间

2.使用布隆过滤器(Bloom Filter)(推荐)

在请求进入 Redis 前,先判断「这个 key 是否可能存在」

流程:

  1. 把所有合法 key(比如用户 ID)加入布隆过滤器
  2. 请求来了先判断:
    • 不存在 → 直接返回(拦截)
    • 可能存在 → 再查 Redis

特点:

特性说明
不会漏判一定不会把“存在的数据”判错
可能误判可能把不存在的当存在
极省内存非常适合大规模 key

常见实现:

  • RedisBloom 模块
  • 本地布隆过滤器(Go 用 willf/bloom

3.参数校验(第一道防线)

在接口层直接过滤非法请求

例如:

  • ID < 0
  • ID 超出范围
  • 字符串不合法
1
2
3
if id <= 0 {
    return error
}

优点:

  • 成本最低
  • 最容易做

4.接口限流 / 风控

防止恶意攻击

常见方式:

  • IP 限流(如 1 秒 100 次)
  • 用户限流
  • 网关限流(如 Nginx / API Gateway)

三、完整防穿透方案

「组合拳」:

1
请求 → 参数校验 → 布隆过滤器 → Redis → 数据库 → 缓存结果

再加上:

  • 空值缓存兜底
  • 限流防攻击

四、总结

缓存穿透的本质:

查询不存在的数据,导致请求绕过缓存直击数据库

最优实践:

  • 小项目:空值缓存 + 参数校验
  • 大规模系统:布隆过滤器 + 空值缓存 + 限流