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

文摘   科技   2024-10-17 21:51   湖北  
FSAC未来超级架构师

架构师总动员
实现架构转型,再无中年危机


尼恩说在前面

在40岁老架构师 尼恩的100+读者交流群 中,最近有小伙伴拿到了一线互联网企业如得物、阿里、滴滴、极兔、有赞、希音、百度、网易、美团的面试资格,遇到很多很重要的面试题:
  • 什么是缓存击穿、缓存穿透、缓存雪崩?
  • 缓存击穿、缓存穿透、缓存雪崩 碰到过吗?如何 彻底解决?
最近有小伙伴在面试 阿里,又遇到了相关的面试题。小伙伴懵了,因为没有遇到过,所以支支吾吾的说了几句,面试官不满意,面试挂了。
所以,尼恩给大家做一下系统化、体系化的梳理,使得大家内力猛增,可以充分展示一下大家雄厚的 “技术肌肉”,让面试官爱到 “不能自已、口水直流”,然后实现”offer直提”。
当然,这道面试题,以及参考答案,也会收入咱们的 《尼恩Java面试宝典PDF》V171版本,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
最新《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请关注本公众号【技术自由圈】获取,回复:领电子书
这里,把这个答案共享出来,帮大家内力猛增,让面试官爱到 “不能自已、口水直流”。

本文目录

- 尼恩说在前面:

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

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

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

 - 缓存击穿的原因

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

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

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

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

 - 缓存穿透的原因:

 - 缓存穿透的恶果:

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

    -缓存空值的问题:

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

 - 布隆过滤器 的问题:

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

 - 缓存雪崩的两大原因

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

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

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


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自由” 。


被裁之后, 空窗1年/空窗2年, 如何  起死回生  ? 


案例1:42岁被裁2年,天快塌了,急救1个月,拿到开发经理offer,起死回生


案例2:35岁被裁6个月, 职业绝望,转架构急救上岸,DDD和3高项目太重要了

案例3:失业15个月,学习40天拿offer, 绝境翻盘,如何实现?


 被裁之后,100W 年薪 到手, 如何 人生逆袭? 


100W案例,100W年薪的底层逻辑是什么? 如何实现年薪百万? 如何远离  中年危机?

100W案例240岁小伙被裁6个月,猛卷3月拿100W年薪 ,秘诀:首席架构/总架构

环境太糟,如何升 P8级,年入100W?

如何  逆天改命,包含AI、大数据、golang、Java  等      


职业救助站

实现职业转型,极速上岸


关注职业救助站公众号,获取每天职业干货
助您实现职业转型、职业升级、极速上岸
---------------------------------

技术自由圈

实现架构转型,再无中年危机


关注技术自由圈公众号,获取每天技术千货
一起成为牛逼的未来超级架构师

几十篇架构笔记、5000页面试宝典、20个技术圣经
请加尼恩个人微信 免费拿走

暗号,请在 公众号后台 发送消息:领电子书

如有收获,请点击底部的"在看"和"",谢谢

技术自由圈
疯狂创客圈(技术自由架构圈):一个 技术狂人、技术大神、高性能 发烧友 圈子。圈内一大波顶级高手、架构师、发烧友已经实现技术自由;另外一大波卷王,正在狠狠卷,奔向技术自由
 最新文章