缓存穿透,击穿,雪崩

核心思想:为什么用缓存?

首先,我们要明白为什么要在数据库(DB)前面加一个缓存(比如 Redis)。

  • 数据库:数据持久化在硬盘上,读写速度慢。

  • 缓存:数据放在内存里,读写速度极快。

所以我们把经常被访问的"热点数据"放在缓存里,这样绝大部分请求可以直接从缓存中读取数据,直接返回,大大减轻数据库的压力,提升系统性能

正常的访问流程是:
读请求 -> 访问缓存 -> 缓存有数据(命中)-> 直接返回
读请求 -> 访问缓存 -> 缓存无数据(未命中)-> 访问数据库 -> 将数据写入缓存 -> 返回

当这个流程出现异常时,就产生了我们下面要说的三个问题。


1. 缓存穿透

🎯 什么是缓存穿透?

缓存穿透 是指查询一个数据库中根本不存在的数据。由于缓存中也不会有这个数据,所以每次请求都会穿过缓存,直接去查询数据库。如果有人恶意地大量发起这种请求,就会导致数据库压力过大甚至崩溃。

简单比喻

你去图书馆(缓存)借一本《如何成为亿万富翁》。图书馆没有,你就去总书库(数据库)找,总书库也没有。第二天你又来借同一本书,流程又重复一遍... 如果有成千上万的人每天都来借这本根本不存在的书,总书库的管理员就累垮了。

❓ 为什么会发生?
  • 恶意攻击:黑客故意请求大量不存在的数据 ID(如 -1, 0, 或非常大的数字)。

  • 业务逻辑错误:用户输入了不合法的参数或ID。

🛡️ 解决方案
  1. 缓存空对象(null caching)

    • 做法 :当数据库也查不到数据时,我们仍然将这个"空结果"(比如 null)进行缓存,并设置一个较短的过期时间(例如 5分钟)。

    • 效果:下次再请求这个不存在的数据时,缓存中就有这个"空值"了,可以直接返回,而不用访问数据库。

    • 缺点:可能会占用额外的缓存空间;如果恶意请求的 key 是海量且不重复的,此方案效果会打折扣。

  2. 布隆过滤器(Bloom Filter)

    • 做法 :在缓存之前,加一个布隆过滤器。布隆过滤器可以高效地判断一个元素"一定不存在"或"可能存在"于某个集合中。系统启动时,将所有可能存在的 key(比如所有商品ID)初始化到布隆过滤器中。

    • 流程:请求来了,先让布隆过滤器判断。

      • 如果布隆过滤器说"一定不存在",则直接返回空,拒绝访问数据库。

      • 如果布隆过滤器说"可能存在",才允许去查询缓存和数据库。

    • 效果:可以从根本上解决恶意的不存在 key 攻击。

    • 缺点:需要额外的组件和空间;实现稍微复杂;有极低的误判率("可能存在"意味着它也可能真的不存在,但概率极低)。

总结:缓存穿透是"查无此数",解决方案的核心是【在缓存层挡住无效请求】。


2. 缓存击穿

🎯 什么是缓存击穿?

缓存击穿 是指一个热点 key (访问量非常高的数据)在缓存中过期的那一刻,突然有大量的请求涌向这个 key。此时缓存失效,所有这些请求都会直接打到数据库上,瞬间可能压垮数据库。

简单比喻

一家超级网红奶茶店(热点key)每天限量供应。平时大家都是在"预约券"(缓存)上排队。下午3点整,今天的"预约券"突然失效了(缓存过期)。此时所有在门口排队的人(大量请求)瞬间全部涌向柜台(数据库),柜台瞬间被挤爆。

❓ 为什么会发生?
  • 某个 key 是热点数据,并发访问量非常大。

  • 这个 key 在缓存中过期了。

🛡️ 解决方案
  1. 设置热点数据永不过期

    • 做法:对于一些极热点数据,可以设置其永不过期。然后通过后台任务或消息队列,在数据更新时主动去刷新缓存。

    • 效果:从根本上避免了因过期而导致的击穿问题。

    • 缺点:需要额外的逻辑来维护数据一致性;如果数据永远不淘汰,可能会占用内存。

  2. 互斥锁(Mutex Lock)

    • 做法 :当发现缓存失效时,不是所有线程都去查数据库,而是先尝试去获取一个分布式锁(比如用 Redis 的 SETNX 命令)。

      • 只有一个线程(比如线程A)能成功获取到锁。

      • 线程A去数据库查询数据,并重建缓存。

      • 其他线程等待,然后重试从缓存中获取数据。

    • 效果:将高并发的数据库请求串行化,保证只有第一个请求会去访问数据库,其他请求等待并复用缓存结果。

    • 缺点:如果获取锁失败,线程需要等待或重试,性能会有一定损耗;系统复杂性增加。

总结:缓存击穿是"热点数据过期",解决方案的核心是【防止瞬间大量请求落到数据库上】。


3. 缓存雪崩

🎯 什么是缓存雪崩?

缓存雪崩 是指在同一时间,大量的缓存 key 同时失效 ,或者 Redis 缓存服务直接宕机。导致所有请求都无法从缓存中获取数据,全部转向数据库,引起数据库瞬时压力过大而崩溃。

缓存雪崩是缓存击穿的"升级版",击穿是一个点,雪崩是一片。

简单比喻

双十一零点,所有商品的"购物车优惠券"(缓存)都设置在了同一时间过期。零点一过,海量用户发现优惠券没了,同时点击"重新加载"或"查询最新优惠"(大量请求),瞬间把商城的结算系统(数据库)冲垮了。

❓ 为什么会发生?
  • 大量 key 设置了相同的过期时间(TTL)。

  • Redis 集群宕机。

🛡️ 解决方案
  1. 设置不同的过期时间

    • 做法 :给缓存数据的过期时间加上一个随机值。比如,基础过期时间是 1 小时,然后再加上一个 1-10 分钟的随机数。这样就能保证大量 key 不会在同一时间点失效。

    • 效果:将大量 key 的失效时间分散开,避免集体失效。

  2. 构建高可用的缓存集群

    • 做法:通过 Redis 的哨兵(Sentinel)模式或集群(Cluster)模式,实现缓存服务的高可用。即使个别节点宕机,整个集群依然可以提供服务。

    • 效果:防止因缓存服务宕机而导致的雪崩。

  3. 服务降级和熔断

    • 做法:在应用层,当发现数据库压力过大时,对于非核心业务的数据请求,直接返回预定义的默认值(降级),或者暂时停止服务(熔断),保护数据库不被拖垮。

    • 效果:"弃车保帅",保证核心业务的可用性。

  4. 缓存预热

    • 做法:在系统上线或重启前,先将部分高频数据加载到缓存中。

    • 效果:避免系统刚启动时,大量请求直接落到冷启动的数据库上。

总结:缓存雪崩是"大规模缓存失效",解决方案的核心是【分散风险,保证缓存服务高可用】。

相关推荐
伯明翰java2 小时前
Redis学习笔记-List列表(2)
redis·笔记·学习
a123560mh2 小时前
国产信创操作系统银河麒麟常见软件适配(MongoDB、 Redis、Nginx、Tomcat)
linux·redis·nginx·mongodb·tomcat·kylin
Elias不吃糖2 小时前
总结我的小项目里现在用到的Redis
c++·redis·学习
leonardee3 小时前
Spring Security安全框架原理与实战
java·后端
一个处女座的程序猿O(∩_∩)O3 小时前
Spring Boot、Redis、RabbitMQ 在项目中的核心作用详解
spring boot·redis·java-rabbitmq
q***5183 小时前
Spring Cloud gateway 路由规则
java
空空kkk3 小时前
SpringMVC框架——入门
java·spring
liyi_hz20083 小时前
云原生 + 国产化适配:O2OA (翱途)开发平台后端技术栈深度解析
java·后端·开源软件
合作小小程序员小小店4 小时前
web网页开发,在线%考试管理%系统,基于Idea,html,css,jQuery,java,jsp,servlet,mysql。
java·前端·intellij-idea