Redis面试里缓存击穿到底是啥,怎么破,还有那些坑你知道吗
- 问答
- 2025-12-25 17:00:20
- 2
说到Redis面试,缓存击穿是个高频问题,很多人容易把它和缓存穿透、缓存雪崩搞混,咱们今天就把这个“击穿”说透,它到底是啥,怎么发生的,怎么解决,以及还有哪些相关的坑。
缓存击穿到底是啥?
你可以想象一下,缓存就像你家门口的一个小信箱,平时你收的信(数据)都放在里面,很方便,缓存击穿呢,不是说信箱被砸了(那是雪崩),也不是说有人不停往信箱里塞垃圾广告你每次都不得不开门去扔(那是穿透)。
缓存击穿更像是这种情况:有一封非常重要的信(比如高考录取通知书),这封信你必须要亲手拿到,而且每天只送一次,不巧的是,你今天去看信箱的时候,发现这封信还没到(缓存过期了),更不巧的是,这个时候,你家门口突然围了一大群关心你录取结果的亲戚朋友(高并发请求),他们都在问你:“信到了没?快看看!”。
因为你没在信箱里找到信,你只能亲自跑到楼下的物业收发室去问(查询数据库),问题就在于,不是你一个人跑去问,而是你这一大群亲戚朋友(大量并发请求)在同一时刻发现信箱里没有信,于是他们全都一窝蜂地冲向那个小小的物业收发室(数据库),收发室的大爷一下子被围得水泄不通,压力巨大,可能直接就忙不过来了,甚至被问懵了(数据库压力激增,可能崩溃)。
缓存击穿的核心定义是:某一个非常热点的数据(key)在缓存中过期(失效)的瞬间,同时有大量的请求涌向这个key,由于缓存里没有,这些请求会全部直接打到数据库上,导致数据库瞬间压力过大。
怎么破?解决缓存击穿的常见方法
既然问题出在“热点key失效”和“高并发同时查询”这两点上,那解决方案就从这两方面入手。
-
永不过期(逻辑过期) 这是最简单粗暴的方法,我不是怕它过期吗?那我干脆不设置过期时间,让它永远待在缓存里,这样就不会有失效的那一刻了。
- 但这里有个坑:数据不是一成不变的,如果数据库里的数据更新了,缓存里的旧数据怎么办?
- 解决办法(逻辑过期):我们不给key设置Redis自带的物理过期时间,而是在存储的value里面,自己加一个字段来表示过期时间。
value = {data: 真实数据, expireTime: 1730000000},当业务线程读取缓存时,先判断这个自定义的expireTime是否已经过了当前时间。- 如果没过期,直接返回数据。
- 如果过期了,那么业务线程会尝试去更新缓存,为了避免击穿,可以只让一个线程去数据库拉取新数据、更新缓存和过期时间,其他线程暂时返回旧数据或稍作等待,这其实就引出了下一个方案。
-
互斥锁(Mutex Lock) 这是最经典的解决方案,直接解决“一窝蜂”的问题,思路是:当第一个发现缓存失效的请求到来时,它并不急着去查数据库,而是先在Redis里设置一个特殊的锁key(
lock:user_123),设置一个很短的过期时间(比如10秒),表明“我已经去查数据库了,你们别去了”。- 后续的请求在发现缓存失效后,会先去尝试获取这个锁。
- 如果获取失败,说明已经有别的线程在查询数据库了,那么这个请求可以等待一小会儿(比如睡眠50毫秒),然后重新从缓存获取数据,这时候可能第一个线程已经把数据加载好了。
- 如果获取成功,那么这个线程就去查数据库,更新缓存,最后删除这个锁。
- 优点:能很好地保护数据库,保证只有一个线程去查询。
- 缺点:没拿到锁的线程需要等待,性能上有轻微损耗,如果去查数据库的线程挂了,可能导致锁无法释放,所以锁一定要设置过期时间。
- 后续的请求在发现缓存失效后,会先去尝试获取这个锁。
-
缓存预热 这是一种“防患于未然”的策略,既然知道某个数据是热点(比如明星离婚新闻、秒杀商品),那就提前把它加载到缓存中,并设置一个合理的过期时间(比如在活动开始前),避免在活动期间因缓存失效而导致击穿。
还有哪些相关的坑?(缓存穿透和缓存雪崩)
面试官问你击穿,很可能会顺带考察你对这三个“缓存兄弟”的理解。
-
缓存穿透
- 是什么:它和击穿的区别在于,击穿是缓存里本来有但刚好过期了,而穿透是查询一个根本不存在的数据,比如请求一个不存在的用户ID
user_id = -1,因为数据不存在,所以缓存里肯定没有(缓存null值的情况稍后说),每次请求都会穿透缓存直达数据库,如果有人恶意用大量不存在的key攻击你,数据库就危险了。 - 怎么破:
- 参数校验:最简单有效的方法,在API层就把明显不合法的参数(如负数ID)过滤掉。
- 缓存空值:即使数据库查不到,也在缓存里存一个空值(比如
null),并设置一个较短的过期时间(如1-5分钟),这样后续相同的无效请求在过期时间内就会命中缓存,直接返回空,而不会访问数据库。 - 布隆过滤器:这是一个高级工具,可以非常高效地判断一个元素“一定不存在”或“可能存在”于某个集合中,在查询缓存前,先让请求经过布隆过滤器,如果它说“一定不存在”,就直接返回,保护了数据库。
- 是什么:它和击穿的区别在于,击穿是缓存里本来有但刚好过期了,而穿透是查询一个根本不存在的数据,比如请求一个不存在的用户ID
-
缓存雪崩
- 是什么:击穿是单个热点key失效,雪崩则是大量的key在同一时间点或时间段内集体失效,缓存服务器重启,或者你设置缓存时偷懒,给很多key设置了相同的过期时间(比如都设置在凌晨2点过期),到了凌晨2点,缓存大面积失效,海量请求直接涌向数据库,后果比击穿严重得多,很可能直接导致数据库宕机。
- 怎么破:
- 错开过期时间:这是关键,给缓存设置过期时间时,加上一个随机值(比如基础时间+随机1-5分钟),让key的失效时间点尽量均匀分布。
- Redis高可用:搭建Redis集群(如主从复制、哨兵模式),即使一台Redis机器挂了,还有其他机器能顶上,防止所有缓存同时不可用。
- 服务降级和熔断:在系统检测到数据库压力过大时,可以暂时停止部分非核心服务,或者直接返回默认值(如“服务繁忙,请稍后再试”),牺牲部分用户体验来保证系统不崩溃。
缓存击穿是“点”的问题(一个热点key),缓存雪崩是“面”的问题(大量key),缓存穿透是“无中生有”的问题(查询不存在的数据),理解了它们的不同场景和成因,就能对症下药,设计出更健壮的缓存系统。

本文由钊智敏于2025-12-25发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:http://waw.haoid.cn/wenda/68279.html
