首页/数据/数据库速查/缓存穿透(查不存在的数据)
Redis 运维与场景124 条

缓存穿透(查不存在的数据)

请求查永远不存在的键:缓存不命中、每次打库,恶意流量下数据库被压垮;防控=空值缓存 + 布隆过滤器 + 参数校验。

数据库速查数据更新于 2026-10-13

缓存穿透是什么

缓存穿透指请求查询根本不存在的数据:缓存永远不命中,每次都要落到数据库,恶意构造大量不存在的 ID 时数据库会被压垮。它与击穿(单个热点键失效)和雪崩(大面积同时失效)的区别在于对象——穿透面对的是本来就不存在的数据,因此再大的缓存容量也挡不住。

基本信息

典型特征缓存命中率低而数据库 QPS 异常升高
主要对策空值缓存、布隆过滤器、前置参数校验与限流
空值标记用不会与真实值冲突的字符串,TTL 要短
布隆过滤器判断「一定不存在」可靠,判断「可能存在」有误判
常见来源爬虫扫号、账号枚举、非法参数传入

典型使用场景

  • 商品详情接口:被扫不存在的商品 ID 时长期打库
  • 用户查询接口:被遍历用户 ID 时回源压力骤增
  • 对外 API:第三方传入大量非法或越界参数
  • 注册与登录:账号枚举攻击会查询不存在的用户名
  • 缓存命中率监控:miss 数量异常升高时先怀疑穿透
  • 批量任务:导入前未校验的 ID 直接进入查询链路

空值缓存示例

空值缓存示例
v = redis.get('user:%s' % uid)
if v == '__NULL__':
    return None                       # 空值也缓存,短 TTL,避免反复回源
if v is None:
    row = db.query(uid)
    if row is None:
        redis.set('user:%s' % uid, '__NULL__', ex=60)   # 60 秒防穿透
        return None
    redis.set('user:%s' % uid, json.dumps(row), ex=300)
    return json.loads(v)

# 前置校验放在最外面:格式、范围、长度都不合法就直接拒绝
# 命中率监控:miss 数量异常升高时先看是否有不存在的 ID 被批量请求

三种缓存异常对照

异常触发条件主要对策
穿透查询的数据在库中也不存在空值缓存加布隆过滤器加参数校验
击穿单个热点键失效瞬间并发回源互斥重建或逻辑过期
雪崩大批键同时失效或缓存层不可用TTL 抖动、多级缓存与回源限流
空值缓存的标记值要选不可能与真实值冲突的字符串(如 __NULL__),并统一封在数据访问层,别散落业务代码。
布隆过滤器只能给出「一定不存在」与「可能存在」两种结论,且标准实现不支持删除元素;新增数据要同步写入过滤器,重建前先评估时间与内存成本。

易错点

  • 只做空值缓存不做参数校验,攻击者换成随机合法格式的 ID 仍然穿透
  • 空值缓存 TTL 设得过长,数据后来被创建仍返回空结果
  • 布隆过滤器与业务数据不同步,新写入的数据被误判为不存在
  • 把穿透与击穿、雪崩混为一谈,用错防护手段
  • 没有监控缓存未命中率,穿透发生数小时才通过数据库告警发现

数据来源与参考

  • Redis 官方文档: 使用模式
  • RedisBloom 文档: 布隆过滤器

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌