0
0
0

Redis 学习笔记:缓存穿透及解决方案

2023-02-24
2026-10-24
文章摘要
|

一、什么是缓存穿透?

缓存穿透是指用户请求的数据在 Redis 和 MySQL 中都不存在,导致请求无法命中缓存,每次都直接访问数据库。

例如:

用户请求 ID = 99999
        ↓
Redis:不存在
        ↓
MySQL:不存在
        ↓
返回空数据

如果大量请求持续查询不存在的数据,就会给 MySQL 造成很大的压力,甚至导致数据库崩溃。

二、解决方案

1. 缓存空值

当 Redis 和 MySQL 都查询不到数据时,将一个特殊的空值标记写入 Redis。

例如:

Key:user:99999
Value:"__NULL__"
TTL:2分钟

这样后续请求就可以直接命中 Redis,不再访问 MySQL。

注意事项:

  • 空值需要设置较短的 TTL,例如 2~5 分钟。

  • 需要区分缓存不存在和缓存的空值标记。

  • 如果数据库新增了对应数据,需要删除或更新原来的空值缓存。

  • 如果大量请求查询不同的无效 ID,仍可能产生大量空值缓存。

优点: 实现简单,适合大多数业务场景。

缺点: 首次请求仍然会访问数据库,大量无效 ID 还可能占用 Redis 内存。

2. 布隆过滤器(Bloom Filter)

在查询 Redis 之前增加一层布隆过滤器,提前判断数据是否可能存在。

核心原理:位数组 + 多个哈希函数。

例如,初始化一个位数组:

位置:0 1 2 3 4 5 6 7 8 9
数值:0 0 0 0 0 0 0 0 0 0

添加用户 ID = 1001 时,通过三个哈希函数计算出位置 2、5、8,将对应位置设置为 1。

位置:0 1 2 3 4 5 6 7 8 9
数值:0 0 1 0 0 1 0 0 1 0

后续查询时,重新计算哈希位置:

  • 任意位置为 0:数据一定不存在。

  • 所有位置为 1:数据可能存在。

为什么布隆过滤器会误判?

因为不同数据经过哈希计算后,可能落到相同的位置。

例如,某个 ID 没有被添加过,但其他数据恰好已经将它对应的位置全部设置为 1,布隆过滤器就会误认为它可能存在。

这种现象称为假阳性(False Positive)。

布隆过滤器判断不存在,一定不存在;判断存在,不一定真的存在。

注意:以上结论建立在布隆过滤器正确初始化、数据完整同步的前提下。

注意事项:

  • 普通布隆过滤器不支持安全地直接删除单个元素。

  • 数据库新增数据时,需要同步更新布隆过滤器。

  • 如果同步失败,可能把真实存在的数据错误拦截,因此需要补偿、重建或降级机制。

优点: 内存占用较少、查询效率高,可以提前拦截大量无效请求。

缺点: 存在误判概率,需要维护布隆过滤器和数据库之间的数据一致性。

3. 主动防护措施

除了缓存空值和布隆过滤器,还可以从请求入口主动防止缓存穿透。

① 增加 ID 的复杂性

采用 UUID、雪花算法等方式生成 ID,避免使用简单、连续、容易遍历的数字 ID。

需要注意:

  • UUID v4 具有较高的随机性,不容易被猜测。

  • 雪花 ID 主要保证分布式唯一性,并不保证不可预测。

② 增加参数校验

在访问 Redis 之前,先对请求参数进行合法性校验。

例如:

  • ID 不能为空。

  • ID 必须符合规定的格式、长度及取值范围。

  • 对不符合规则的请求直接拒绝。

这样可以提前过滤一部分无效请求,避免访问 Redis 和 MySQL。

③ 增加接口限流

对高频查询接口进行限流,避免短时间内大量请求访问数据库。

常见方式:

  • 根据用户 ID 限流。

  • 根据 IP 地址限流。

  • 根据接口或业务资源限流。

例如,限制某个用户每秒最多请求 10 次,超过限制直接拒绝或提示稍后重试。

限流不只是解决缓存穿透,也能用于防止恶意刷接口和保护热点资源。

三、完整请求流程

         客户端请求
              ↓
       参数校验、接口限流
              ↓
         布隆过滤器
              ↓
       判断数据是否存在
         ├── 不存在 → 返回空结果
         │
         └── 可能存在
                 ↓
               Redis
                 ↓
         ├── 命中缓存 → 返回数据
         │
         ├── 命中空值 → 返回空结果
         │
         └── 缓存未命中
                 ↓
               MySQL
                 ↓
         ├── 存在 → 缓存正常数据
         │
         └── 不存在 → 缓存空值

实际项目中,可以将多种方案组合使用,但不是所有系统都必须引入布隆过滤器,应根据业务数据量和请求特点选择。

四、总结

缓存穿透是指请求的数据在 Redis 和数据库中都不存在,导致每次请求都会直接访问数据库。

一般可以采用以下方式解决:

  1. 缓存空值:将数据库查询不到的数据以特殊空值标记写入 Redis,并设置较短的过期时间,避免重复访问数据库。

  2. 布隆过滤器:在 Redis 前增加过滤机制,提前拦截不存在的数据,但需要注意哈希碰撞导致的误判问题。

  3. 主动防护:通过提高 ID 的不可预测性、请求参数校验以及接口限流,减少无效请求和恶意请求。

实际项目中,可以根据业务场景组合使用这些方案。

核心记忆:

  • 缓存空值:让不存在的数据也能命中缓存。

  • 布隆过滤器:提前过滤不存在的数据。

  • 参数校验:拒绝不合法的请求。

  • 接口限流:限制高频访问,保护系统资源。

  • ID 设计:增加恶意枚举数据的难度。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

评论