文章目录

  • 多级缓存架构
  • 缓存击穿
  • 缓存穿透
  • 缓存雪崩

多级缓存架构

在项目架构中,前端发送请求,服务器会先从缓存中获取数据,如果找到数据则直接返回,如果在缓存中没有找到则会去从数据库中获取,数据库取到后,更新缓存,并返回结果,如果数据库也没有获取到,则返回空值。这一架构设计在访问缓存时,如果设计不当,会发生严重的问题甚至导致数据库挂掉。

缓存击穿

由于大批量缓存在同一时间内失效,可能导致大量请求同时穿透缓存直达数据库,会造成数据库瞬间压力过大甚至挂掉。

解决方案
1、对于这种情况,我们在批量增加缓存时最好将这一批数据的缓存过期时间设置为一个时间段内的不同时间;
2、加互斥锁。

// 伪代码
// 1、设置不同过期时间
String get(String key) {// 从缓存中获取数据String cacheValue = cache.get(key);// 缓存为空if (StringUtils.isBlank(cacheValue)) {// 从存储中获取String storageValue = storage.get(key);cache.set(key, storageValue);//设置一个过期时间(300到600之间的一个随机数)int expireTime = new Random().nextInt(300) + 300;if (storageValue == null) {cache.expire(key, expireTime);}return storageValue;} else {// 缓存非空return cacheValue;}
}
// 2、加互斥锁
String getData(String key) throw InterruptedException {// 从缓存中获取数据String cacheValue = cache.get(key);// 缓存为空if (StringUtils.isBlank(cacheValue)) {// 去获取锁,获取成功则去数据库取数据if (reentrantLock.tryLock()) {// 从数据库中获取String storageValue = get(key);cache.set(key, storageValue);// 解锁reentrantLock.unlock();return storageValue;} else {// 等待100ms再去获取缓存Thread.sleep(100);cacheValue = cache.get(key);}} else {return cacheValue;}
}

缓存穿透

缓存穿透是指查询一个根本不存在的数据,缓存层和存储层都不会命中,通常出于容错的考虑,如果从存储层查不到数据则不写入缓存层。
缓存穿透将导致不存在的数据每次请求都要到存储层去查询, 失去了缓存保护后端存储的意义,给数据库造成一定的压力。
造成缓存穿透的基本原因有两个:

  1. 自身业务代码或者数据出现问题;
  2. 一些恶意攻击、 爬虫等造成大量空命中。

解决方案:
1、缓存空对象;
2、布隆过滤器。

// 伪代码
// 1、缓存空对象
String get(String key) {// 从缓存中获取数据String cacheValue = cache.get(key);// 缓存为空if (StringUtils.isBlank(cacheValue)) {// 从存储中获取String storageValue = storage.get(key);cache.set(key, storageValue);// 如果存储数据为空, 需要设置一个过期时间(300秒)if (storageValue == null) {cache.expire(key, 60 * 5);}return storageValue;} else {// 缓存非空return cacheValue;}
}

布隆过滤器
对于恶意攻击,向服务器请求大量不存在的数据造成的缓存穿透,还可以用布隆过滤器先做一次过滤,对于不存在的数据布隆过滤器一般都能够过滤掉,不让请求再往后端发送。当布隆过滤器说某个值存在时,这个值可能不存在;当它说不存在时,那就肯定不存在。

布隆过滤器就是一个大型的位数组和几个不一样的无偏 hash 函数。所谓无偏就是能够把元素的 hash 值算得比较均匀。
向布隆过滤器中添加 key 时,会使用多个 hash 函数对 key 进行 hash 算得一个整数索引值然后对位数组长度进行取模运算得到一个位置,每个 hash 函数都会算得一个不同的位置。再把位数组的这几个位置都置为 1 就完成了 add 操作。
向布隆过滤器询问 key 是否存在时,跟 add 一样,也会把 hash 的几个位置都算出来,看看位数组中这几个位置是否都为 1,只要有一个位为 0,那么说明布隆过滤器中这个key 不存在。如果都是 1,这并不能说明这个 key 就一定存在,只是极有可能存在,因为这些位被置为 1 可能是因为其它的 key 存在所致。如果这个位数组比较稀疏,这个概率就会很大,如果这个位数组比较拥挤,这个概率就会降低。
这种方法适用于数据命中不高、 数据相对固定、 实时性低(通常是数据集较大) 的应用场景, 代码维护较为复杂, 但是缓存空间占用很少。
可以用redisson实现布隆过滤器,引入依赖:

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.6.5</version>
</dependency>

实例代码:

public class RedissonBloomFilter {public static void main(String[] args) {Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379");//构造RedissonRedissonClient redisson = Redisson.create(config);RBloomFilter<String> bloomFilter = redisson.getBloomFilter("nameList");//初始化布隆过滤器:预计元素为100000000L,误差率为3%,根据这两个参数会计算出底层的bit数组大小bloomFilter.tryInit(100000000L,0.03);//将zhuge插入到布隆过滤器中bloomFilter.add("zhuge");//判断下面号码是否在布隆过滤器中System.out.println(bloomFilter.contains("guojia"));//falseSystem.out.println(bloomFilter.contains("baiqi"));//falseSystem.out.println(bloomFilter.contains("zhuge"));//true}
}

使用布隆过滤器需要把所有数据提前放入布隆过滤器,并且在增加数据时也要往布隆过滤器里放,布隆过滤器缓存过滤伪代码:

//初始化布隆过滤器
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("nameList");
//初始化布隆过滤器:预计元素为100000000L,误差率为3%
bloomFilter.tryInit(100000000L,0.03);//把所有数据存入布隆过滤器
void init(){for (String key: keys) {bloomFilter.put(key);}
}String get(String key) {// 从布隆过滤器这一级缓存判断下key是否存在Boolean exist = bloomFilter.contains(key);if(!exist){return "";}// 从缓存中获取数据String cacheValue = cache.get(key);// 缓存为空if (StringUtils.isBlank(cacheValue)) {// 从存储中获取String storageValue = storage.get(key);cache.set(key, storageValue);// 如果存储数据为空, 需要设置一个过期时间(300秒)if (storageValue == null) {cache.expire(key, 60 * 5);}return storageValue;} else {// 缓存非空return cacheValue;}
}

注意:布隆过滤器不能删除数据,如果要删除得重新初始化数据。

缓存雪崩

缓存雪崩指的是缓存层支撑不住或宕掉后, 大量的流量会打向后端存储层。由于缓存层承载着大量请求, 有效地保护了存储层, 但是如果缓存层由于某些原因不能提供服务(比如超大并发过来,缓存层支撑不住,或者由于缓存设计不好,类似大量请求访问bigkey,导致缓存能支撑的并发急剧下降), 于是大量请求都会打到存储层, 存储层的调用量会暴增, 造成存储层也会级联宕机的情况。

解决方案:
1、保证缓存层服务高可用性,比如使用Redis Sentinel或Redis Cluster。
2、依赖隔离组件为后端限流熔断并降级。比如使用Sentinel或Hystrix限流降级组件。比如服务降级,我们可以针对不同的数据采取不同的处理方式。当业务应用访问的是非核心数据(例如电商商品属性,用户信息等)时,暂时停止从缓存中查询这些数据,而是直接返回预定义的默认降级信息、空值或是错误提示信息;当业务应用访问的是核心数据(例如电商商品库存)时,仍然允许查询缓存,如果缓存缺失,也可以继续通过数据库读取。
3、提前演练。 在项目上线前, 演练缓存层宕掉后, 应用以及后端的负载情况以及可能出现的问题, 在此基础上做一些预案设定。

缓存击穿、缓存穿透、缓存雪崩相关推荐

  1. Redis常见面试题(缓存击穿、穿透、雪崩)

    Redis常见面试题(缓存击穿.穿透.雪崩) 击穿 场景: 一般由于redis中的数据到期,同时并发用户特别多,此时大量请求压到数据库上. 解决思路: 根据redis是单进程单实例的特性,当高流量进入 ...

  2. 使用jedis连接redis-cluster模拟缓存击穿,穿透,雪崩场景

    上一篇演示了通过redis实现ID生成器,本篇模拟缓存击穿,穿透,雪崩的场景. package com.coderman.jedis.clusterdemo.hack;import com.coder ...

  3. 《如何与面试官处朋友》系列-缓存击穿、穿透、雪崩场景原理大调解

    前面我们提到分布式多级缓存架构的全貌,但总感觉少了些什么东西.在这样大的场景下面,如果遇到缓存使用问题那可咋办?但自古英雄出少年,相信此刻你已踏马西去,正走在寻找答案上得夕阳西下.每每面谈Redis大 ...

  4. php怎么解决雪崩或穿透,Redis之缓存击穿、穿透、雪崩、预热,以及如何解决?...

    数据获取的流程,一般是前端请求,后台先从缓存中取数据,缓存取不到则去数据库中取,数据库取到了则返回给前端,然后更新缓存,如果数据库取不到则返回空数据给前端 流程图: 假如缓存的数据没有,后台则会一直请 ...

  5. Redis——缓存击穿、穿透、雪崩

    1.缓存穿透: (1)问题描述:key对应的数据并不存在,每次请求访问key时,缓存中查找不到,请求都会直接访问到数据库中去,请求量超出数据库时,便会导致数据库崩溃.如一个用户id不存在,数据库与缓存 ...

  6. 【Redis系列】缓存击穿、穿透、雪崩解决方案详解

    文章目录 前言 一.击穿 1.介绍 2.产生原因 3.解决方案 二.穿透 1.介绍 2.产生原因 3.解决方案 三.雪崩 1.介绍 2.产生原因 3.解决方案 结尾 前言 众所周知,计算机的瓶颈之一就 ...

  7. Redis缓存击穿,穿透,雪崩等问题

    雪崩(随机过期时间.永不过期). 穿透(表示恶意请求,在系统端判断是否符合规则,比如id<0,布隆过滤器). 击穿(查询加for update,永不过期) redis缓存穿透:查询一个数据库中不 ...

  8. 缓存击穿,穿透,雪崩

    一.缓存击穿 单个热点key,在不停的扛着大并发,在这个key失效的瞬间,持续的大并发请求就会击破缓存,直接请求到数据库 解决方案 使用互斥锁(Mutex Key),只让一个线程构建缓存,其他线程等待 ...

  9. 谈谈redis缓存击穿透和缓存击穿的区别,雪崩效应

    谈谈redis缓存击穿透和缓存击穿的区别,雪崩效应 面试经历 在很长的一段时间里,我以为缓存击穿和缓存穿透是一个东西,直到最近去腾讯面试,面试官问我缓存击穿和穿透的区别:我回答它俩是一样的,面试官马上 ...

  10. java缓存击穿_对缓存击穿的一点思考

    点击上方"Java知音",选择"置顶公众号" 技术文章第一时间送达! 作者:张丰哲 www.jianshu.com/p/93767dac6b56 技术经验交流: ...

最新文章

  1. 自学python可以找到好的工作吗-通过自学python能找到工作吗
  2. 预训练模型参数量越来越大?这里有你需要的BERT推理加速技术指南
  3. 软件协会发布开源软件与商业软件知识产权报告
  4. spark mlib坐标矩阵(Coordinate Matrix)
  5. 2023年南京师范大学资源与环境考研上岸前辈备考经验指导
  6. catia三维轴承_常用滚动轴承手册与三维图库(CATIA版)
  7. 计算机的此电脑管理出错了,win10重置此电脑出现问题怎么处理_win10重置初始化失败解决方法...
  8. java算法编程题:某年某月某日是这一年的第几天
  9. 负数二进制转换十进制
  10. 安卓使用WebView清除缓存
  11. TcaplusDB君 · 行业新闻汇编(12月12号)
  12. Uniapp之API promise化
  13. ENSP网络综合实验
  14. 算法笔记_203:第四届蓝桥杯软件类决赛真题(C语言B组)
  15. 制造企业如何从0到1进行信息化建设?
  16. 嵌入式必备技能---git与github
  17. 大数据之Linux基础认识
  18. 一个模仿布卡那样的划动手势看在线漫画的简单应用DEMO
  19. 给ROM包内置ROOT权限,刷机以后立马拥有ROOT权限
  20. 《Android开发艺术探索》

热门文章

  1. 2022-2028年中国地铁广告行业研究及前瞻分析报告
  2. Redis 预防缓存穿透“神器” — 布隆过滤器
  3. HTTP 协议入门 — (TCP/IP协议族、通信传输流、URI 与 URL 的区别、Cookie 状态管理、HTTP 支持的方法、状态码类别、HTTP 首部字段)
  4. 解决pip安装报错:is not a supported wheel on this platform
  5. 在装有Ubuntu16.04的VMware虚拟机下安装OpenCV3.2.0
  6. 机房收费系统总结【4】-报错码
  7. Python 标准库之 xml.etree.ElementTree xml解析
  8. 完全理解Python迭代对象、迭代器、生成器
  9. 压缩人工智能的数据值
  10. CUDA运行时 Runtime(四)