Redis 缓存击穿是什么?90% 的 Java 程序员都说不完整

40 阅读6分钟



大家好,我是小米,今年 31 岁,一个写代码写到凌晨、也被线上事故教育过无数次的普通 Java 程序员。

今天想跟你聊一个特别爱在 Java 社招面试里出现,而且特别容易在真实线上把你按在地上摩擦的问题:

Redis 缓存击穿,到底是什么?怎么解决?

为了让你真正记住这个东西,我不打算一上来就丢概念、丢术语。我想先讲一个故事。

故事开始:凌晨三点,数据库被“围殴”了

想象这样一个画面。你开了一家24 小时营业的网红奶茶店。生意最好的是一款「爆款招牌奶茶」,每天能卖上万杯。

为了不让后厨太累,你提前准备好了很多奶茶,放在前台的保温柜里。客人一来,直接从保温柜拿,快、稳、省事

这个保温柜,就是 Redis 缓存。后厨,就是 数据库

1、平时的日子,一切都很美好

  • 客人点奶茶
  • 前台一看:保温柜里有
  • 直接交付
  • 后厨继续悠闲地切珍珠、煮椰果

这,就是缓存命中的美好世界。

2、问题出在一个细节:这杯奶茶“过期了”

有一天凌晨三点,这杯爆款奶茶,在保温柜里放太久了,被系统自动清空了。偏偏就在这时:

  • 外卖平台搞活动
  • 推送了这杯奶茶
  • 瞬间来了 几千个订单

前台一看:咦?保温柜里没有!于是发生了可怕的一幕:

  • 几千个前台员工
  • 同时冲进后厨
  • 同时找厨师现做这同一杯奶茶

后厨当场崩溃。这,就是缓存击穿

什么是缓存击穿?一句话讲清楚

在面试中,如果你只能说一句话,请记住这一句:

缓存击穿:缓存中没有,但数据库中有的数据,在高并发下被大量同时访问,导致所有请求直接打到数据库。

它有几个非常关键的特征

  • 数据 在数据库中是存在的
  • 缓存中 刚好没有(通常是过期)
  • 大量并发请求
  • 查的是同一条数据
  • 数据库压力瞬间暴增

缓存击穿 vs 缓存雪崩,别再说混了

很多同学在面试里,一说缓存击穿,面试官马上追一句:那缓存雪崩呢?

如果你这时开始支支吾吾,基本就要扣分了。我们用一个直观对比表,一眼就懂。

缓存击穿 vs 缓存雪崩对比表

一句话记忆法:

  • 击穿:一个明星被围殴
  • 雪崩:一整个城市停电

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

很多人会问一句:不就是查一次数据库吗?有这么夸张?

问题就出在 “并发” 两个字上。

1、单线程思维 vs 并发现实

在你本地测试时:

  • 缓存没了
  • 查一次数据库
  • 写回缓存
  • 一切正常

但线上是:

  • 1000 个线程
  • 同一毫秒发现缓存为空
  • 同时查数据库
  • 同时回写缓存

数据库的 QPS 不是被“使用”,而是被围殴

2、热点数据是“固定被打的点”

缓存击穿往往发生在:

  • 商品详情
  • 首页配置
  • 活动规则
  • 系统参数

这些数据的特点是:

  • 访问量极高
  • 数据变化不频繁
  • 一旦失效,后果极大

解决方案一:热点数据,永远不过期

这是最简单、也是最常见的一种方案。

1、思路很直接

既然问题出在:热点数据过期, 那我就让它:永远不过期

2、具体怎么做?

Redis 中设置永不过期

不设置过期时间,数据就会一直存在。

3、那数据怎么更新?

很多人一听“永不过期”,马上慌了:那数据变了怎么办?

答案是:主动更新,而不是被动失效。

常见方式有:

  • 后台管理系统更新商品时
  • 直接更新数据库
  • 同时更新 Redis 缓存

4、优点与缺点

解决方案二:互斥锁,挡住后面的洪水

如果你只说“永不过期”,在社招面试里是不够的。面试官更想听的是这个方案:互斥锁(Mutex Lock)

1、互斥锁的核心思想

一句话:同一时间,只允许一个线程去查数据库。

其他线程:

  • 要么等待
  • 要么直接返回旧值
  • 要么稍后重试

2、用生活类比一下

还是奶茶店。发现保温柜空了之后:

  • 店长立刻拉上一个 “正在补货,请稍等” 的牌子
  • 只允许 一个员工 进后厨现做
  • 其他人站在门口等

这,就是互斥。

基于 Redis 的互斥锁实现思路

实现互斥锁,核心就三步:

  1. 先查缓存
  2. 缓存没命中,尝试加锁
  3. 拿到锁的人查数据库、回写缓存、释放锁

1、伪代码流程

2、Java 示例代码(核心逻辑)

为什么要“双重检查”?

这是一个高频面试加分点。原因很简单:

  • A 线程拿到锁,查数据库,写缓存
  • B 线程在等锁
  • A 释放锁
  • B 拿到锁
  • 如果不重新查缓存,就会重复查数据库

所以必须:查缓存 → 加锁 → 再查缓存

互斥锁方案的优缺点

我们用一张表总结一下。

面试中怎么回答,才像“干过活”的人?

如果你在面试中这样说:缓存击穿就是缓存没了打数据库,用互斥锁解决。

那基本只能算 “背过八股” 。更好的回答结构是:

  1. 先定义缓存击穿
  2. 区分缓存击穿和缓存雪崩
  3. 强调“热点数据 + 高并发”
  4. 讲两种解决方案
  5. 重点展开互斥锁 + 双重检查
  6. 提一句实际项目中的取舍

实战经验总结

最后,说点我自己踩过坑之后的结论。

  • 不要迷信一种方案
  • 热点数据少:直接永不过期
  • 热点数据多:互斥锁
  • 核心原则只有一个:宁可慢一点,也别把数据库打死

END

缓存击穿,本质上不是 Redis 的问题,而是:我们低估了并发的破坏力。

如果你能把这个故事、这个逻辑、这个代码,在面试里讲清楚,Java 社招里关于 Redis 的问题,你至少能赢 80%。

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

好朋友们,我们下篇见。