这是 Beta 探索课程,内容结构、实验步骤和示例可能会继续调整。
缓存空对象
问题引入
在了解了什么是缓存穿透之后,我们需要找到一种方法来防护这种攻击。
核心问题:
- 查询不存在的数据时,无法将结果写入缓存
- 每次请求都会调用外部 API
- 恶意攻击者可以构造大量不存在的 key
解决思路:即使数据不存在,也把”空结果”缓存起来
这就是缓存空对象方案的核心思想。
什么是缓存空对象?
缓存空对象是指:当查询的数据不存在时,在缓存中存储一个特殊的标记,表示”该数据不存在”。
核心原理
正常情况:
- 数据存在 → 缓存真实数据
- 数据不存在 → 无法缓存 → 每次都查询外部 API
缓存空对象方案:
- 数据存在 → 缓存真实数据
- 数据不存在 → 缓存空标记 → 避免重复查询与其他方案对比
| 方案 | 内存占用 | 判断准确率 | 适用场景 | 实现难度 |
|---|---|---|---|---|
| 缓存空对象 | 中 | 100% | 少量非法 key | 简单 |
| 布隆过滤器 | 极低 | 99%+ | 海量数据、key 空间大 | 中等 |
| 数据库预检查 | 中 | 100% | 合法数据集合小 | 简单 |
工作原理
用户请求
Redis 缓存
invalid_001 __NULL__
beijing_weather {"temp": 25}
数据库
受到保护
效果验证
实施缓存空对象方案后,系统指标明显改善:
| 指标 | 攻击前 | 被攻击时 | 防护后 |
|---|---|---|---|
| 外部 API 调用失败率 | 1% | 80% | 2% |
| API 响应时间 | 200ms | 5000ms | 250ms |
| 错误率 | 0.1% | 30% | 0.5% |
当前技术架构
练习
练习 1
缓存空对象方案中,为什么空值的过期时间要设置得比较短?
参考答案 (2 个标签)
缓存空对象 过期时间
答案:
空值过期时间设置较短(如 60 秒)的原因:
数据一致性:如果数据在空值缓存期间被创建,用户会一直看到”不存在”的结果
内存效率:空值也占用内存,过长的过期时间会浪费存储空间
安全性平衡:
- 60 秒足以抵挡大部分攻击
- 攻击者等待 60 秒后再次攻击,成本已经很高
- 如果需要更强防护,可以结合布隆过滤器
推荐设置:
- 空值缓存:30-120 秒
- 正常数据:根据业务设置(如 3600 秒)
练习 2
缓存空对象方案和布隆过滤器方案有什么区别?分别适用于什么场景?
参考答案 (3 个标签)
缓存空对象 布隆过滤器 方案对比
答案:
| 维度 | 缓存空对象 | 布隆过滤器 |
|---|---|---|
| 内存占用 | 中(每个空值约 90 字节) | 极低(10 万条约 100KB) |
| 准确率 | 100% | 99%+(存在误判) |
| 实现难度 | 简单 | 中等 |
| 维护成本 | 低 | 中(需要维护合法数据集合) |
| 适用场景 | 少量非法 key | 海量数据、key 空间大 |
场景选择:
使用缓存空对象:
- 非法 key 数量较少(< 1 万)
- 希望实现简单
- 需要 100% 准确
- 内存资源充足
使用布隆过滤器:
- 非法 key 数量巨大(> 10 万)
- 内存资源紧张
- 可以接受小幅误判
- 有明确的合法数据集合
组合使用:
- 第一层:布隆过滤器拦截大部分非法请求
- 第二层:缓存空对象处理误判和漏网之鱼
练习 3
请设计一个带有缓存空对象防护的用户查询流程,要求:
- 复用统一的缓存防护流程
- 空值缓存 60 秒,正常数据缓存 1 小时
- 包含错误处理
参考答案 (2 个标签)
缓存空对象 方案设计
参考答案:
设计流程
练习 3
- 步骤 1:读取、刷新或失效缓存数据
- 步骤 2:写入缓存值、空值标记或热点保护状态
- 步骤 3:记录命中率、回源次数和异常 key 分布
- 步骤 4:记录命中率、回源次数和异常 key 分布
关注点:命中率、回源压力、TTL 和异常 key 处理。
关键点:
- 复用统一的缓存防护流程,流程复用性强
- 空值使用特殊标记
NULL_MARKER - 空值缓存时间短(60 秒)
- 正常数据缓存时间长(3600 秒)
- 完善的错误处理(Redis 连接失败、JSON 解析错误)
- 参数校验在查询入口完成
- 支持复杂数据类型(自动序列化)