Redis 命中率 99%,数据库却 100% CPU,是谁在捣鬼



大家好,我是小米,今年 31 岁,干 Java 也干了不少年头。

前阵子我在公司排查一次线上事故,事情特别"离谱":

Redis 正常、代码没改、数据库却 CPU 100%,连接数飙升,服务雪崩式超时。

当时我第一反应是:"是不是缓存挂了?是不是缓存雪崩?"

结果一查 Redis:稳如老狗,命中率还挺高。

那数据库为啥还能被打爆?最后答案四个字:缓存穿透。

这也是 Java 社招面试里,Redis 相关问题出现频率极高的一道题。

先讲个故事:快递站被"查不存在的包裹"查垮了

为了把缓存穿透讲清楚,我先给你讲个生活中的故事。

假设你家楼下有一个快递站:

  • 前台:小哥(缓存 Redis)
  • 仓库:后面的仓储区(数据库 MySQL)
  • :用户发起查询请求

正常流程是这样的:

  • 你问小哥:"有我这个包裹吗?"
  • 小哥一查系统(Redis)
    • 有 → 直接给你
    • 没有 → 去仓库查(数据库)
  • 仓库查到了 → 拿出来给你
  • 同时小哥把信息记下来,下次就不用再跑仓库

但有一天,事情开始不对劲了......突然来了一群人,天天问:

  • "有单号 -1 的包裹吗?"
  • "有单号 999999999999 的包裹吗?"
  • "有单号 0 的包裹吗?"

这些包裹 根本不存在。结果发生了什么?

  • 小哥查不到(缓存没有)
  • 每次都得跑仓库
  • 仓库也查不到
  • 但你没有任何机制告诉小哥:这玩意本来就不存在

于是:所有请求,100% 穿过前台,全部打到仓库, 仓库再大,也扛不住这种"查空气"的请求。这,就是缓存穿透。

什么是缓存穿透(面试标准答案)

缓存穿透是指:查询的数据在缓存和数据库中都不存在,导致每次请求都会绕过缓存,直接访问数据库,从而在高并发下对数据库造成巨大压力,甚至导致数据库崩溃。

特点非常明显:

  • 查的是 不存在的数据
  • 缓存里没有
  • 数据库里也没有
  • 缓存失效策略对它完全没用

缓存穿透为什么这么危险?

我们用一张表来对比一下几种常见缓存问题:

缓存穿透最恶心的一点是:它是"可被人为制造"的攻击。

只要有人不断请求不存在的 ID,你的数据库就会被持续消耗。

解决方案一:接口层增加基础校验(第一道防线)

先说一句非常现实的话:

很多缓存穿透,本来就不应该进系统。

1、常见的非法请求长这样:

  • id <= 0
  • id 不是数字
  • 用户未登录
  • token 非法
  • 参数明显不合理

2、在接口层直接拦截

这是最便宜、最有效的一层防护。

如果你连 id <= 0 都放进 Redis 和数据库里查:

  • 那不是技术问题
  • 是态度问题

3、这一层的特点

但注意: 这一层 永远不够

解决方案二:缓存空值(key-null 策略)

这是面试里必考的一种方案。

1、核心思想一句话

既然这个数据不存在,那我就明确告诉缓存:它不存在。

2、查询流程升级版

  • 查 Redis
    • 有值 → 返回
    • 是 null → 直接返回空
  • Redis 没有 → 查数据库
  • 数据库也没有?
    • 把 key-null 写进 Redis
    • 设置较短过期时间,比如 30 秒

3、示例代码

4、为什么过期时间要短?

因为:

  • 这个数据 可能未来会被创建
  • 如果你缓存 null 一小时
  • 那真实数据出现后,用户一小时都查不到

5、优缺点分析

解决方案三:布隆过滤器(终极方案,面试加分项)

如果你在面试中能把 Bloom Filter 讲清楚,面试官大概率会在心里默默给你加分。

1、先回到刚才的快递故事

如果快递站门口有一块 超大的白板

  • 所有可能存在的包裹单号
  • 都提前在白板上"打过标记"

那么:

  • 你来查一个单号
  • 白板一看:根本没标记
  • 小哥直接告诉你:不用进站,肯定没有

这块白板,就是 布隆过滤器

Bitmap 与 Bloom Filter 的极致空间利用

1、Bitmap 是什么?

Bitmap 本质上是:用 1 bit 表示一个元素是否存在

典型应用:

  • 用户是否签到
  • 某天是否打卡
  • 某个 ID 是否出现过

2、Bitmap 的问题

布隆过滤器(Bloom Filter)原理详解

1、核心思想

用多个 Hash 函数,降低冲突概率。 不是一个 Hash,而是 k 个 Hash 函数

2、工作流程

插入元素时:

  1. 用 k 个 Hash 函数计算
  2. 得到 k 个位置
  3. 把 bitmap 对应位置全部置为 1

查询元素时:

  • 只要有 一个 bit 为 0,一定不存在
  • 如果全部为 1,可能存在

3、关键结论(面试必背)

  • 不存在 → 一定准确
  • 存在 → 有一定误判率

为什么 Bloom Filter 能防缓存穿透?

因为:

  • 所有 可能存在的数据
  • 在系统启动时就已经加入 Bloom Filter
  • 请求来了先过 Bloom Filter

请求流程变成:

  1. 请求进来
  2. 先查 Bloom Filter
  • 不存在 → 直接返回
  • 可能存在 → 再查 Redis / DB

数据库终于可以喘口气了。

Bloom Filter 的优缺点总结

Redis 中使用 Bloom Filter(实践建议)

在实际项目中,一般有两种方式:

  1. 自己实现 Bitmap + Hash
  2. 使用 RedisBloom 插件

示意代码(简化):

在接口最前面加一层:

三种方案如何组合使用?(标准答案)

真正的生产环境,从来不是"选一个"。 而是:

一句话总结:层层过滤,让请求越早死越好。

面试时怎么一句话总结缓存穿透?

你可以这么说:

缓存穿透是指查询缓存和数据库中都不存在的数据,导致请求绕过缓存直接访问数据库。

我通常通过接口参数校验、缓存空值以及使用布隆过滤器三种方式结合解决。

其中布隆过滤器用于从源头拦截不存在的数据,是大规模系统中最常见的方案。

如果你这么说,面试官基本会点头。

总结

缓存穿透这件事,本质上不是 Redis 的问题,而是你有没有认真思考过:哪些请求根本不该进数据库。

数据库很贵,也很脆弱。Redis 再快,也扛不住"查空气"。

END

希望这篇文章,能帮你在面试中稳稳拿下这一题,也能在真实项目里,少踩一次坑。

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号"软件求生",获取更多技术干货!

相关推荐
indexsunny19 小时前
互联网大厂Java面试实战:Spring Boot微服务在电商场景中的应用与挑战
java·spring boot·redis·微服务·kafka·spring security·电商
逍遥德19 小时前
如何学编程之01.理论篇.如何通过阅读代码来提高自己的编程能力?
前端·后端·程序人生·重构·软件构建·代码规范
MX_935920 小时前
Spring的bean工厂后处理器和Bean后处理器
java·后端·spring
小迷糊的学习记录20 小时前
0.1 + 0.2 不等于 0.3
前端·javascript·面试
程序员敲代码吗20 小时前
面试中sessionStorage问题引发深度探讨
面试·职场和发展
程序员泠零澪回家种桔子21 小时前
Spring AI框架全方位详解
java·人工智能·后端·spring·ai·架构
源代码•宸1 天前
大厂技术岗面试之谈薪资
经验分享·后端·面试·职场和发展·golang·大厂·职级水平的薪资
晚霞的不甘1 天前
CANN 编译器深度解析:UB、L1 与 Global Memory 的协同调度机制
java·后端·spring·架构·音视频
马猴烧酒.1 天前
【面试八股|JVM虚拟机】JVM虚拟机常考面试题详解
jvm·面试·职场和发展
喵叔哟1 天前
06-ASPNETCore-WebAPI开发
服务器·后端·c#