阿里面试:缓存击穿、缓存穿透、缓存雪崩 3大问题,如何彻底解决?

文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :

免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,完成职业升级, 薪酬猛涨!加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领

免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取


阿里面试:缓存击穿、缓存穿透、缓存雪崩 3大问题,如何彻底解决?

尼恩特别说明: 尼恩的文章,都会在 《技术自由圈》 公号 发布, 并且维护最新版本。 如果发现图片 不可见, 请去 《技术自由圈》 公号 查找
在40岁老架构师 尼恩的100+读者交流群 中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:

  • 什么是缓存击穿、缓存穿透、缓存雪崩?
  • 缓存击穿、缓存穿透、缓存雪崩 碰到过吗?如何 彻底解决?

最近有小伙伴在面试 阿里,又遇到了相关的面试题。小伙伴懵了,因为没有遇到过,所以支支吾吾的说了几句,面试官不满意,面试挂了。

所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。

当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V171版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。

最新《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技术自由圈】获取,回复:领电子书

这里,把这个答案共享出来,帮大家内力猛增,让面试官爱到 “不能自已、口水直流”。

CacheAside 模式 —— 使用Redis 缓存常用模式

CacheAside 模式, 是使用Redis 缓存常用模式。 一般情况下,CacheAside 的查询过程如下:

  • 先查询 Redis,如果Redis 查询成功,直接返回
  • 如果Redis 查询不存在,去查询 DB;
  • 如果 DB 查询成功,数据回写 Redis,返回
  • 如果 DB 查询不存在,直接返回。

在这里插入图片描述

使用Redis 缓存的3大核心问题

使用Redis作为缓存时,常见的三大核心问题包括:

  1. 缓存击穿(Cache Breakdown)

    缓存击穿是指一个备受欢迎的缓存数据突然失效或宕机,导致重建缓存时,由于是热点Key,会有大量的线程来查和重建缓存,导致大量数据请求直接到达数据库,这种我们称为缓存击穿。

  2. 缓存穿透(Cache Penetration)

    缓存穿透是指查询的Key压根不存在,所以每次都未命中缓存,直接到数据库,这我们称为缓存穿透。

  3. 缓存雪崩(Cache Avalanche)

    缓存雪崩是指在同一时间段,大量缓存数据同时过期或Redis服务宕机,导致大量请求同时到达数据库,从而给数据库带来非常大的压力。

在这里插入图片描述

使用Redis 缓存的3大核心问题,关乎到 系统的稳定性和性能。

所以 缓存击穿、缓存穿透、缓存雪崩 是 高并发的重点问题, 也是面试的重点问题。

尽管如此, 在尼恩 的 技术自由圈 社群中, 还是有很多小伙伴, 傻傻分不清。 面试回答不好。

这里 ,尼恩给大家做一下系统化、体系化的梳理,帮助大家 毒打面试官,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。

第一大核心问题: 缓存击穿

一个并发访问量比较大的key(也就是常说的 HotKey)在某个时间过期,导致所有的请求直接打在DB上。

具体来是,就是说某个 key 非常热点(HotKey),访问非常频繁,处于集中式高并发访问的情况,

当这个 key 在失效的瞬间,大量的请求就击穿了缓存,直接请求数据库,就像是在一道屏障上凿开了一个洞。

结果是:

请求会直接访问数据库,并回设到缓存中,高并发访问数据库会导致数据库崩溃。

在这里插入图片描述

缓存击穿的原因

缓存击穿的概念就是单个hotkey过期, 所有请求直接打到db上,

缓存击穿 方案1: 加DCL锁更新 缓存

加锁 DCL(双检索) 更新缓存。

加分布式锁(DCL)更新缓存,是一种常见的解决缓存击穿的策略。以下是这个方案的大致步骤:

  1. 加锁检查缓存(第一次检查):当用户请求数据时,首先检查缓存中是否存在该数据。
  2. 加锁:如果缓存中没有数据,那么走DB。但是,在尝试从数据库查询数据之前,使用本地锁(或者分布式锁)来确保只有一个请求能够执行数据库查询操作。
  3. 数据库查询:如果成功获取到锁,那么第二次检查缓存,如果确实缓存中没有数据, 执行数据库查询操作,获取最新的数据。
  4. 更新缓存:将查询到的数据写入缓存,并设置一个合理的过期时间。
  5. 释放锁:完成缓存更新后,释放分布式锁,以便其他请求可以继续执行。
  6. 返回数据:将查询到的数据返回给用户。
  7. 处理其他请求:对于在等待锁释放期间到达的请求,它们可以直接从缓存中获取数据,而不需要再次查询数据库。

//伪代码
public object GetProductListNew() {
  int cacheTime = 30;
  String cacheKey ="product_list";
  String ockKey = cacheKey;
  String cacheValue = CacheHelper.get(cacheKey);//第一次检查
  if (cacheValue != null) {
    return cacheValue!
  }else {
    synchronized(lockKey) cacheValue = CacheHelper.get(cacheKey); //第二次检查
    if (cacheValue != null){
      return cacheValue;
      }else {
        //这里一般是sql查询数据
        cacheValue = GetProductListFromDB();
        CacheHelper.Add(cacheKey,cacheValue, cacheTime);
      }
    }
    return cacheValue;
  }
}

加锁排队只是为了减轻数据库的压力,并没有提高系统吞吐量。

假设在高并发下,缓存重建期间key是锁着的,这是过来1000个请求999个都在阻塞的。

同样会导致用户等待超时,这是个治标不治本的方法!

注意:在高并发场景下尽可能不用!

缓存击穿 方案2:热点数据,永不过期

热点数据,永不过期 的策略如下:

(1)预先设置热门数据:

在redis高峰访问时期,提前设置热门数据到缓存中,或适当延长缓存中key过期时间。

(2)对于热点key设置,永不过期。

要值得注意的是,这里说到的永不过期并不是将热点数据存在时间设置为无限制。而是将过期时间存在key对应的value里,如果发现要过期了,通过一个后台的异步线程进行缓存的重建。

其实,这个方案不太可行: 因为这个解决不了一个核心问题, 哪些数据是 hotkey?

缓存击穿 方案3: 多级缓存 + 热点探测 + 数据预加载

对于有条件的团队, 如果并发量确实比较高 , 下面这个才是比较有效的方案。

在这里插入图片描述

多级缓存 + 热点探测 + 数据预加载 的方案比较复杂, 具体请参见尼恩架构团队的文章:

阿里面试:如何 彻底解决Redis热点Key问题?

第二大核心问题: 缓存穿透

缓存穿透是指查询不存在缓存中的数据,每次请求都会打到DB,就像缓存不存在一样。

缓存穿透的原因:

当 某个key在 数据库没有数据时, 正常情况下,缓存也无数据可存.

所以对于这种value 不存在的 key, 缓存 形同虚设, 每次都查DB 。

为什么?

因为 数据库无数据,当然也不会有对应key 的数据写入缓存, 所以,缓存中也不会有数据。

既然缓 存中也不会有数据,因此每次请求都会去查询数据库,这种情况就出现缓存穿透。

在这里插入图片描述

红色的线条,就是缓存穿透的场景。

当查询的 Key 在缓存和 DB 中都不存在时,就会出现这种情况。

缓存穿透的恶果:

比如有个接口需要查询商品信息。

如果有恶意用户随意使用一个 不存在的商品 ID 发起请求,瞬间并发量很高, DB 会直接挂掉。

缓存穿透方案1:缓存空值

既然缓存 穿透的原因是:因为数据库无数据,缓存中也不会有数据。

当我们从数据库中查询到空值时,可以向缓存中 回写一个空值

在这里插入图片描述

当然,为了避免缓存被长时间占用,需要给这个空值加一个比较短的过期时间,例如 3~5 分钟。

缓存空值的问题:

当大量无效请求穿透过来时,缓存内就会有 大量的空值缓存。

如果缓存空间被占满了,还会因剔除掉一些已经被缓存的用户信息,反而会造成缓存命中率的下降,所以这个方案,需要评估缓存容量。

有什么办法提升 缓存空值的 空间浪费问题呢 ? 可以考虑使用布隆过滤器。

缓存穿透方案2:布隆过滤器

针对这个问题,加一层布隆过滤器。

布隆过滤器的原理是在你存入数据的时候,会通过散列函数将它映射为一个位数组中的K个点,同时把他们置为1。

这样当用户再次来查询A,而A在布隆过滤器值为0,直接返回,就不会产生击穿请求打到DB了。

在这里插入图片描述

显然,使用布隆过滤器之后会有一个问题就是误判。

因为,布隆过滤器本身是一个数组,可能会有多个值落到同一个位置, 理论上来说只要我们的数组长度够长,误判的概率就会越低。

但是,仍然有可能会误判。

不过,这里有转机。

因为 布隆过滤器 是 “ 存在“ 才有 误判, “不 存在“ 没有 误判。

也就是说: 如果 布隆过滤器 判定不存在, 那就是真的不存在

所以, 布隆过滤器 用在这个场景,是可以的。 如果 布隆过滤器 判定不存在, 就去DB中查询,不会再发生缓存 穿透。

布隆过滤器 的问题:

存在数据一致性问题, 解决方案参考下图:

在这里插入图片描述

第三大核心问题:缓存雪崩

某⼀时刻发⽣⼤规模的缓存失效的情况,例如缓存服务宕机、大量key在同一时间过期,这样的后果就是⼤量的请求进来直接打到DB上,db无响应,最后可能导致整个系统的崩溃,称为雪崩。

对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,

但是缓存机器意外发生了:

  • 缓存全盘宕机,缓存挂了,
  • 大量key在同一时间过期

此时 1 秒 5000 个请求全部落数据库,数据库必然扛不住,它会报一下警,然后db无响应,最后导致整个系统的崩溃。

总之,雪崩和击穿、热key的问题不太一样的是,雪崩是指大规模的缓存都过期失效了。

缓存雪崩的两大原因

总结一下,缓存雪崩的两大原因:

  • 缓存全盘宕机,缓存挂了,
  • 大量key在同一时间过期

在这里插入图片描述

缓存雪崩的3大解决方案:

缓存雪崩是三大缓存问题里最严重的一种,我们来看看怎么预防和处理。

  • 提高缓存可用性
  1. 集群部署:通过集群来提升缓存的可用性,可以利用Redis本身的Redis Cluster或者第三方集群方案如Codis等。
  2. 多级缓存:设置多级缓存,设置一级缓存本地 guava 缓存,第一级缓存失效的基础上再访问二级缓存 redis,每一级缓存的失效时间都不同。
  • 过期时间
  1. 设置不同的过期时间:过期时间随机值。为了避免大量的缓存在同一时间过期,可以把不同的 key 过期时间随机生成,避免过期时间太过集中。
  2. 热点数据永不过期。
  • 熔断降级
  1. 限流: 如果redis宕机,可以限流,避免同时刻大量请求打崩DB

  2. 熔断:当缓存服务器宕机或超时响应时,为了防止整个系统出现雪崩,可以使用hystrix 类似的熔断,暂时停止业务服务访问db, 或者其他被依赖的服务,避免 MySQL 被打死。

  3. 降级:当出现大量缓存失效,而且处在高并发高负荷的情况下,在业务系统内部暂时舍弃对一些非核心的接口和数据的请求,而直接返回一个提前准备好的 fallback(退路)错误处理信息。

设置不同 过期时间 的参考实现:

避免缓存设置相近的有效期,为有效期增加随机值(1-5分钟)使失效时间均匀分布。

这样每一个缓存的过期时间的重复率就会降低,很难引发集体失效的事件。

缓存添加随机时间示例:

// 缓存原本的失效时间
int exTime = 10 * 60;
// 随机数生成类
Random random = new Random();
// 缓存设置
jedis.setex(cacheKey, exTime + random.nextInt(1000) , value);

说在最后:有问题找老架构取经‍

Redis 是 面试的核心 知识, 涉及到的核心面试题,主要包括:

阿里面试: 缓存击穿、缓存穿透、缓存雪崩 3大问题,如何彻底解决? (本题)

希音面试:Redis脑裂,如何预防?

美团面试:Redis锁如何续期?Redis锁超时,任务没完怎么办?

京东面试: Redis分布式 锁失效了,怎么办?

阿里面试:如何 彻底解决 Redis热点Key问题?

遇到上面的面试题,可以按照尼恩的深度 梳理,进行 深度回答,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。

在面试之前,建议大家系统化的刷一波 5000页《尼恩Java面试宝典PDF》,里边有大量的大厂真题、面试难题、架构难题。

很多小伙伴刷完后, 吊打面试官, 大厂横着走。

在刷题过程中,如果有啥问题,大家可以来 找 40岁老架构师尼恩交流。

另外,如果没有面试机会,可以找尼恩来改简历、做帮扶。前段时间,刚指导一个27岁 被裁小伙,拿到了一个年薪45W的JD +PDD offer,逆天改命

狠狠卷,实现 “offer自由” 很容易的, 前段时间一个武汉的跟着尼恩卷了2年的小伙伴, 在极度严寒/痛苦被裁的环境下, offer拿到手软, 实现真正的 “offer自由” 。

技术自由的实现路径:

实现你的 架构自由:

吃透8图1模板,人人可以做架构

10Wqps评论中台,如何架构?B站是这么做的!!!

阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了

峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?

100亿级订单怎么调度,来一个大厂的极品方案

2个大厂 100亿级 超大流量 红包 架构方案

… 更多架构文章,正在添加中

实现你的 响应式 自由:

响应式圣经:10W字,实现Spring响应式编程自由

这是老版本 《Flux、Mono、Reactor 实战(史上最全)

实现你的 spring cloud 自由:

Spring cloud Alibaba 学习圣经》 PDF

分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)

一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)

实现你的 linux 自由:

Linux命令大全:2W多字,一次实现Linux自由

实现你的 网络 自由:

TCP协议详解 (史上最全)

网络三张表:ARP表, MAC表, 路由表,实现你的网络自由!!

实现你的 分布式锁 自由:

Redis分布式锁(图解 - 秒懂 - 史上最全)

Zookeeper 分布式锁 - 图解 - 秒懂

实现你的 王者组件 自由:

队列之王: Disruptor 原理、架构、源码 一文穿透

缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)

缓存之王:Caffeine 的使用(史上最全)

Java Agent 探针、字节码增强 ByteBuddy(史上最全)

实现你的 面试题 自由:

4800页《尼恩Java面试宝典 》 40个专题

免费获取11个技术圣经PDF:

posted @ 2024-10-18 10:47  疯狂创客圈  阅读(16)  评论(0编辑  收藏  举报