Redis 缓存穿透场景下如何配合 MySQL 设计防护策略

文章导读
针对 Redis 缓存穿透,最推荐的防护策略是在缓存层存储空对象并设置较短过期时间,适用于查询 key 存在但数据库数据不存在的场景。风险边界在于空缓存会占用额外内存且需接受数据一致性存在短暂延迟。
📋 目录
  1. 命令速用版
  2. 为什么会这样
  3. 分步处理
  4. 怎么验证是否生效
  5. 常见坑
  6. 常见问题
A A

针对 Redis 缓存穿透,最推荐的防护策略是在缓存层存储空对象并设置较短过期时间,适用于查询 key 存在但数据库数据不存在的场景。风险边界在于空缓存会占用额外内存且需接受数据一致性存在短暂延迟。

先说结论:缓存空对象是成本最低的止血方案,布隆过滤器适合 key 空间固定的高频查询场景。

  • 先判断:恶意攻击查询不存在 ID 或业务逻辑导致大量空查询。
  • 优先做:在代码层拦截空结果写入 Redis,设置随机过期时间。
  • 再验证:观察 MySQL QPS 是否回落及 Redis 内存增长是否在预期内。

命令速用版

若使用 RedisBloom 模块,可在查询前执行以下命令判断 key 是否存在:

BF.ADD myBloom "key_123"
BF.EXISTS myBloom "key_123"

若无布隆过滤器模块,代码逻辑中需在数据库查询返回空后执行:

SET key_123 "" EX 300

为什么会这样

缓存穿透的根本原因是请求的数据在缓存和数据库中均不存在,导致所有请求直接穿透到数据库。

当攻击者故意构造不存在的 ID 请求,或业务中存在大量无效数据查询时,Redis 无法命中缓存,MySQL 被迫处理每次查询。数据库连接池可能因此耗尽,导致正常业务请求无法获取连接。

分步处理

步骤 1:定位穿透 Key

检查应用日志或 MySQL 慢查询日志,筛选出返回结果为空但频率极高的查询条件。

步骤 2:实施空对象缓存

在代码逻辑中,当数据库查询返回 null 时,仍将空值写入 Redis,并设置较短过期时间(如 5 分钟)。

配置片段示例(伪代码):

if (data == null) {
    redis.set(key, "", expire_time);
    return null;
}

步骤 3:引入布隆过滤器(可选)

对于 key 空间可枚举的场景,在请求到达缓存前使用布隆过滤器拦截不存在的关键字。

Redis 缓存穿透场景下如何配合 MySQL 设计防护策略

步骤 4:限制 MySQL 连接

在数据库层面设置 max_connections,防止异常流量耗尽资源,配合应用层熔断机制。

怎么验证是否生效

监控 MySQL 的 QPS 指标,确认异常高峰期间 QPS 是否平稳。

检查 Redis 内存使用率,确认空对象缓存未导致内存溢出。

查看应用错误日志,确认数据库连接超时错误不再频繁出现。

常见坑

空缓存过期时间设置过长,导致数据库数据更新后缓存仍为空,造成数据不一致。

布隆过滤器存在误判率,可能将存在的 key 判断为不存在,导致业务逻辑错误。

未对空缓存 key 做隔离命名,导致清理缓存时误删正常业务数据。

常见问题

缓存穿透和缓存雪崩有什么区别?

缓存穿透是查询不存在的数据导致请求直达数据库,缓存雪崩是大量缓存同时过期导致数据库压力骤增。

布隆过滤器删除 key 怎么办?

标准布隆过滤器不支持删除操作,若需删除需使用支持计数的布隆过滤器或重建过滤器。

空对象缓存会影响内存吗?

会占用少量内存,但相比数据库压力,通常可接受,需设置较短过期时间控制总量。