缓存穿透基础

一个真实的故事

那是一个普通的周二下午。

突然,监控系统发出警报:

Inbox
From: 监控系统
To: 运维值班
Time: 周二 14:23
Subject: [P1] Weather API 异常告警
  • 警告:外部 API 调用失败率飙升到 80%
  • 警告:API 响应时间超过 5 秒
  • 警告:错误率上升到 30%

我冲回电脑前,查看日志,发现了异常:

2025-03-15 14:23:15 - GET /weather?city=invalid_city_1 → 404
2025-03-15 14:23:15 - GET /weather?city=invalid_city_2 → 404
2025-03-15 14:23:16 - GET /weather?city=fake_city_999 → 404
...

有人在恶意攻击我的 API!

什么是缓存穿透?

缓存穿透攻击流程
恶意攻击者
构造大量不存在 key
缓存层 (Redis)
invalid_001 不存在
invalid_002 不存在
invalid_003 不存在
外部 API
GET /api/weather?city='invalid_001' 200ms
GET /api/weather?city='invalid_002' 195ms
GET /api/weather?city='invalid_003' 210ms
限流状态:
已触发
外部 API 触发限流!

缓存穿透是指:

  • 查询一个不存在的数据
  • 缓存层没有(因为数据不存在)
  • 请求直接穿透到外部 API
  • 如果大量这样的请求同时到来,外部 API 可能被压垮

关键特征

  • 查询的数据在缓存和外部 API 中都不存在
  • 每个请求都会访问外部 API
  • 恶意攻击者可以利用这个漏洞

练习

练习 1

缓存穿透的根本原因是什么?它与缓存击穿、缓存雪崩有什么区别?

参考答案 (2 个标签)
缓存穿透 基础概念

答案

缓存穿透的根本原因查询的数据在缓存和外部 API 中都不存在

具体分析:

  1. 请求的 key 在缓存中不存在
  2. 请求的 key 在外部 API 中也不存在(返回 404)
  3. 因此无法将结果写入缓存
  4. 每次请求都会调用外部 API

恶意利用: 攻击者可以构造大量不存在的 key,使所有请求都调用外部 API,导致 API 限流或服务不可用。

与缓存击穿、缓存雪崩的区别

问题根本原因数据是否存在典型场景
缓存穿透查询不存在的数据不存在恶意攻击、参数错误
缓存击穿热点数据过期存在热点新闻、明星数据
缓存雪崩大量数据同时过期存在批量导入、定时任务

练习 2

假设你的 API 遭受了缓存穿透攻击,每秒有 1000 个请求查询不存在的数据,外部 API 的限流是每秒 100 次。请分析:

  1. 如果不防护,会发生什么?
  2. 如果使用缓存空对象方案(空值缓存 60 秒),能减少多少 API 调用?
  3. 如果使用布隆过滤器(误判率 0.1%),能减少多少 API 调用?
参考答案 (2 个标签)
缓存穿透 效果分析

答案

1. 不防护的情况

  • 每秒 1000 个请求都调用外部 API
  • API 限流是每秒 100 次
  • 结果:900 个请求失败(90% 失败率)
  • 系统表现:大量错误,用户体验极差

2. 使用缓存空对象方案

  • 第 1 秒:1000 个请求都调用 API(100 成功 + 900 被限流)
  • 第 2-60 秒:所有请求从缓存返回,0 次 API 调用
  • 第 61 秒:如果攻击持续,重复上述循环

API 调用减少

  • 无防护:1000 请求/秒 × 60 秒 = 60,000 次
  • 有防护:1000 请求(第 1 秒)+ 0(第 2-60 秒)= 1,000 次
  • 减少:59,000 次(约 98.3%)

3. 使用布隆过滤器

  • 误判率 0.1%,意味着 0.1% 的不存在数据会被误判为可能存在
  • 每秒 1000 个请求,布隆过滤器拦截 999 个
  • 只有 1 个请求(0.1%)会穿透到 API

API 调用减少

  • 无防护:1000 请求/秒 × 60 秒 = 60,000 次
  • 有布隆过滤器:1 请求/秒 × 60 秒 = 60 次
  • 减少:59,940 次(约 99.9%)

结论

  • 缓存空对象:减少约 98% 的 API 调用,简单有效
  • 布隆过滤器:减少约 99.9% 的 API 调用,防护更强

练习 3

在什么情况下应该选择缓存空对象方案?在什么情况下应该选择布隆过滤器方案?

参考答案 (2 个标签)
缓存穿透 方案选择

答案

选择缓存空对象方案的情况

✅ 非法 key 数量较少(< 1 万) ✅ 希望实现简单,快速上线 ✅ 需要 100% 准确,不能有误判 ✅ 内存资源充足 ✅ 数据一致性要求高(不能有误判)

典型场景

  • 小型 API 服务
  • 内部系统
  • 参数格式固定的场景
  • 可以接受短暂的数据不一致

选择布隆过滤器方案的情况

✅ 非法 key 数量巨大(> 10 万) ✅ 内存资源紧张 ✅ 可以接受小幅误判(0.1% 左右) ✅ 有明确的合法数据集合 ✅ 需要极高的拦截率

典型场景

  • 大型互联网应用
  • 面向公网的 API
  • 用户 ID、商品 ID 等海量 key
  • 对性能要求极高的场景

组合使用的情况

✅ 防护要求极高的场景 ✅ 不能容忍单点失效 ✅ 资源充足,追求极致性能

组合架构

请求 → 布隆过滤器(第一层)→ 缓存空对象(第二层)→ 外部 API
       拦截 99.9%           拦截剩余 0.09%