十三、缓存雪崩+缓存击穿+缓存穿透

十三、缓存雪崩+缓存击穿+缓存穿透

  • 十三、缓存雪崩+缓存击穿+缓存穿透
    • 1、缓存雪崩
      • 1、什么情况会发生雪崩
      • 2、雪崩解决方案
    • 2、缓存穿透
      • 1、是什么
      • 2、解决方案
        • 方案1:空对象缓存或者缺省值
        • 方案2:Google布隆过滤器Guava解决缓存穿透
        • 方案3:Redis布隆过滤器解决缓存穿透
      • 3、安装
        • 1、采用docker安装RedisBloom,推荐
        • 2、编译安装
    • 3、缓存击穿
      • 1、是什么
      • 2、解决方案
    • 4、实际案例
      • 更多内容:

1、缓存雪崩

1、什么情况会发生雪崩

  • redis主机挂了,Redis 全盘崩溃
  • 比如缓存中有大量数据同时过期

2、雪崩解决方案

  • 构建多级缓存架构:nginx缓存 + redis缓存 +其他缓存(ehcache等)
  • redis缓存集群实现高可用 (主从+哨兵,Redis Cluster)
  • ehcache本地缓存 + Hystrix或者阿里sentinel限流&降级
  • 开启Redis持久化机制aof/rdb,尽快恢复缓存集群
  • 使用锁或队列
    • 用加锁或者队列的方式保证来保证不会有大量的线程对数据库一次性进行读写,从而避免失效时大量的并发请求落到底层存储系统上。不适用高并发情况
  • 将缓存失效时间分散开
    • 比如我们可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就会降低,就很难引发集体失效的事件。

2、缓存穿透

1、是什么

​ 请求去查询一条记录,先redis后mysql发现都查询不到该条记录,但是请求每次都会打到数据库上面去,导致后台数据库压力暴增,这种现象我们称为缓存穿透,这个redis变成了一个摆设。。。。。。

​ key对应的数据在数据源并不存在,每次针对此key的请求从缓存获取不到,请求都会压到数据源,从而可能压垮数据源。比如用一个不存在的用户id获取用户信息,不论缓存还是数据库都没有,若黑客利用此漏洞进行攻击可能压垮数据库。

简单说就是本来无一物,既不在Redis缓存中,也不在数据库中

2、解决方案

方案1:空对象缓存或者缺省值

  • 黑客或者恶意攻击

黑客会对你的系统进行攻击,拿一个不存在的id 去查询数据,会产生大量的请求到数据库去查询。可能会导致你的数据库由于压力过大而宕掉

id相同打你系统:第一次打到mysql,空对象缓存后第二次就返回null了,避免mysql被攻击,不用再到数据库中去走一圈了

id不同打你系统:由于存在空对象缓存和缓存回写(看自己业务不限死),redis中的无关紧要的key也会越写越多(记得设置redis过期时间)

方案2:Google布隆过滤器Guava解决缓存穿透

Guava 中布隆过滤器的实现算是比较权威的,所以实际项目中我们不需要手动实现一个布隆过滤器

Guava’s BloomFilter 源码剖析 https://github.com/google/guava/blob/master/guava/src/com/google/common/hash/BloomFilter.java

@Testpublic void bloomFilter(){// 创建布隆过滤器对象BloomFilter<Integer> filter = BloomFilter.create(Funnels.integerFunnel(), 100);
// 判断指定元素是否存在System.out.println(filter.mightContain(1));System.out.println(filter.mightContain(2));
// 将元素添加进布隆过滤器filter.put(1);filter.put(2);System.out.println(filter.mightContain(1));System.out.println(filter.mightContain(2));}
public static final int _1W = 10000;//布隆过滤器里预计要插入多少数据public static int size = 100 * _1W;//误判率,它越小误判的个数也就越少(思考,是不是可以设置的无限小,没有误判岂不更好)public static double fpp = 0.03;// 构建布隆过滤器private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), size,fpp);public static void main(String[] args){//1 先往布隆过滤器里面插入100万的样本数据for (int i = 0; i < size; i++) {bloomFilter.put(i);}//故意取10万个不在过滤器里的值,看看有多少个会被认为在过滤器里List<Integer> list = new ArrayList<>(10 * _1W);for (int i = size+1; i < size + 100000; i++) {if (bloomFilter.mightContain(i)) {System.out.println(i+"\t"+"被误判了.");list.add(i);}}System.out.println("误判的数量:" + list.size());}

现在总共有10万数据是不存在的,误判了3033次,

原始样本:100W

不存在数据:101W—110W


方案3:Redis布隆过滤器解决缓存穿透

Guava缺点说明:

Guava 提供的布隆过滤器的实现还是很不错的 (想要详细了解的可以看一下它的源码实现),但是它有一个重大的缺陷就是只能单机使用 ,而现在互联网一般都是分布式的场景。

为了解决这个问题,我们就需要用到 Redis 中的布隆过滤器了

案例:白名单过滤器

  • 白名单架构说明

  • 误判问题,但是概率小可以接受,不能从布隆过滤器删除
  • 全部合法的key都需要放入过滤器+redis里面,不然数据就是返回null
 public static final int _1W = 10000;//布隆过滤器里预计要插入多少数据public static int size = 100 * _1W;//误判率,它越小误判的个数也就越少public static double fpp = 0.03;static RedissonClient redissonClient = null;static RBloomFilter rBloomFilter = null;static{Config config = new Config();config.useSingleServer().setAddress("redis://192.168.111.147:6379").setDatabase(0);//构造redissonredissonClient = Redisson.create(config);//通过redisson构造rBloomFilterrBloomFilter = redissonClient.getBloomFilter("phoneListBloomFilter",new StringCodec());rBloomFilter.tryInit(size,fpp);// 1测试  布隆过滤器有+redis有rBloomFilter.add("10086");redissonClient.getBucket("10086",new StringCodec()).set("chinamobile10086");// 2测试  布隆过滤器有+redis无//rBloomFilter.add("10087");//3 测试 ,都没有}public static void main(String[] args){String phoneListById = getPhoneListById("10087");System.out.println("------查询出来的结果: "+phoneListById);//暂停几秒钟线程try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }redissonClient.shutdown();}private static String getPhoneListById(String IDNumber){String result = null;if (IDNumber == null) {return null;}//1 先去布隆过滤器里面查询if (rBloomFilter.contains(IDNumber)) {//2 布隆过滤器里有,再去redis里面查询RBucket<String> rBucket = redissonClient.getBucket(IDNumber, new StringCodec());result = rBucket.get();if(result != null){return "i come from redis: "+result;}else{result = getPhoneListByMySQL(IDNumber);if (result == null) {return null;}// 重新将数据更新回redisredissonClient.getBucket(IDNumber,new StringCodec()).set(result);}return "i come from mysql: "+result;}return result;}private static String getPhoneListByMySQL(String IDNumber){return "chinamobile"+IDNumber;}

黑名单使用:

3、安装

1、采用docker安装RedisBloom,推荐

Redis 在 4.0 之后有了插件功能(Module),可以使用外部的扩展功能,
可以使用 RedisBloom 作为 Redis 布隆过滤器插件。
docker run -p 6379:6379 --name=redis6379bloom -d redislabs/rebloom
docker exec -it redis6379bloom /bin/bash redis-clibf.reserve filter 0.01 100
bf.add filter v11
bf.exists filter v11
bf.exists filter v12

2、编译安装

# 下载 编译 安装Rebloom插件
wget https://github.com/RedisLabsModules/rebloom/archive/v2.2.2.tar.gz
# 解压
tar -zxvf v2.2.2.tar.gz
cd RedisBloom-2.2.2
# 若是第一次使用 需要安装gcc++环境
make
# redis服启动添加对应参数 这样写还是挺麻烦的
# rebloom_module="/usr/local/rebloom/rebloom.so"
# daemon --user ${REDIS_USER-redis} "$exec $REDIS_CONFIG --loadmodule # $rebloom_module --daemonize yes --pidfile $pidfile"
# 记录当前位置
pwd
# 进入reids目录 配置在redis.conf中 更加方便
vim redis.conf
# :/loadmodule redisbloom.so是刚才具体的pwd位置 cv一下
loadmodule /xxx/redis/redis-5.0.8/RedisBloom-2.2.2/redisbloom.so
# 保存退出
wq
# 重新启动redis-server 我是在redis中 操作的 若不在请写出具体位置 不然会报错
redis-server redis.conf
# 连接容器中的 redis 服务 若是无密码 redis-cli即可
redis-cli -a 密码
# 进入可以使用BF.ADD命令算成功

3、缓存击穿

1、是什么

大量的请求同时查询一个 key 时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去

简单说就是热点key突然失效了,暴打mysql

危害:会造成某一时刻数据库请求量过大,压力剧增。

2、解决方案

  1. 缓存击穿 - 热点key失效 - 互斥更新、随机退避、差异失效时间
  2. 对于访问频繁的热点key,干脆就不设置过期时间
  3. 互斥独占锁防止击穿

4、实际案例

高并发的淘宝聚划算案例落地


@Autowiredprivate RedisTemplate redisTemplate;@PostConstructpublic void initJHS(){log.info("启动定时器淘宝聚划算功能模拟.........."+DateUtil.now());new Thread(() -> {//模拟定时器,定时把数据库的特价商品,刷新到redis中while (true){//模拟从数据库读取100件特价商品,用于加载到聚划算的页面中List<Product> list=this.products();//采用redis list数据结构的lpush来实现存储this.redisTemplate.delete(Constants.JHS_KEY);//lpush命令this.redisTemplate.opsForList().leftPushAll(Constants.JHS_KEY,list);//间隔一分钟 执行一遍try { TimeUnit.MINUTES.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }log.info("runJhs定时刷新..............");}},"t1").start();}/*** 模拟从数据库读取100件特价商品,用于加载到聚划算的页面中*/public List<Product> products() {List<Product> list=new ArrayList<>();for (int i = 1; i <=20; i++) {Random rand = new Random();int id= rand.nextInt(10000);Product obj=new Product((long) id,"product"+i,i,"detail");list.add(obj);}return list;}@Autowiredprivate RedisTemplate redisTemplate;/*** 分页查询:在高并发的情况下,只能走redis查询,走db的话必定会把db打垮* http://localhost:5555/swagger-ui.html#/jhs-product-controller/findUsingGET*/@RequestMapping(value = "/pruduct/find",method = RequestMethod.GET)@ApiOperation("按照分页和每页显示容量,点击查看")public List<Product> find(int page, int size) {List<Product> list=null;long start = (page - 1) * size;long end = start + size - 1;try {//采用redis list数据结构的lrange命令实现分页查询list = this.redisTemplate.opsForList().range(Constants.JHS_KEY, start, end);if (CollectionUtils.isEmpty(list)) {//TODO 走DB查询}log.info("查询结果:{}", list);} catch (Exception ex) {//这里的异常,一般是redis瘫痪 ,或 redis网络timeoutlog.error("exception:", ex);//TODO 走DB查询}return list;}


下一篇:Redis分布式锁

更多内容:

更多内容大家可以关注一下个人博客网,https://blog.xueqimiao.com/,内容更丰富喔。

回复Redis可以获取完整md文档喔,谢谢关注。

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

  1. 缓存雪崩、击穿、穿透解决方案

    用户的数据一般都是存储于数据库,数据库的数据是落在磁盘上的,磁盘的读写速度可以说是计算机里最慢的硬件了. 当用户的请求,都访问数据库的话,请求数量一上来,数据库很容易就奔溃的了,所以为了避免用户直接访 ...

  2. 老司机带你玩转面试(2):Redis 过期策略以及缓存雪崩、击穿、穿透

    前文回顾 建议前一篇文章没看过的同学先看下前面的文章: 「老司机带你玩转面试(1):缓存中间件 Redis 基础知识以及数据持久化」 过期策略 Redis 的过期策略都有哪些? 在聊这个问题之前,一定 ...

  3. 如何解决Redis缓存雪崩、击穿与穿透

    Redis最常用使用的场景就是作为业务系统的缓存,既然是作为缓存,那么就不免会碰到缓存常见的问题,即雪崩.击穿与穿透,什么是缓存雪崩.击穿与穿透以及如何解决这几个问题呢?今天我们一起来探讨一下! 一. ...

  4. Redis 缓存雪崩、击穿、穿透

    Redis 缓存雪崩.击穿.穿透 文章目录 Redis 缓存雪崩.击穿.穿透 一.Redis基础 Redis基本数据类型.操作 二.面试相关问题 1.小伙子您好,看你简历上写了你项目里面用到了Redi ...

  5. 面试填坑之Redis无底洞(一、Redis缓存雪崩、击穿、穿透)

    Redis缓存雪崩.击穿.穿透 学习自大佬:https://blog.csdn.net/qq_35190492/article/details/102889333 https://www.cnblog ...

  6. redis缓存雪崩、击穿、穿透

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言 一.redis缓存雪崩 二.redis缓存击穿 三.redis缓存穿透 前言 主要是介绍一下redis缓存雪崩.击穿. ...

  7. Redis中的缓存雪崩、击穿、穿透的原因以及解决办法

    缓存雪崩.击穿.穿透一旦发生,会导致大量的请求积压到数据库层.如果请求的并发量很大,就会导致数据库宕机或是故障,这就是很严重的生产事故了. 俗话说,知己知彼,百战不殆.了解了问题的成因,我们就能够在应 ...

  8. 关于缓存异常:缓存雪崩、击穿、穿透的解决方案

    关于缓存雪崩.击穿.穿透的解决方案 前言 缓存雪崩 缓存雪崩的原因 解决方案 缓存击穿 解决方案 缓存穿透 解决方案 布隆过滤器 布隆过滤器原理 布隆过滤器如何使用 在Java中使用布隆过滤器 前言 ...

  9. 如何解决缓存雪崩、击穿、穿透难题?

    缓存雪崩.缓存击穿和缓存穿透这三个问题是我们在使用redis做缓存的时候要面临的,一旦发生这三个问题,就会导致大量的请求都积压到了数据库层,有可能导致数据库宕机,进入导致整个系统不可用. 下边,具体看 ...

  10. Redis核心技术与实战-学习笔记(二十六):缓存雪崩、击穿、穿透

    一.缓存雪崩 缓存雪崩:大量应用请求无法在Redis缓存中进行处理,应用请求频繁访问数据库,导致数据库压力激增. 产生原因: 缓存中有大量数据同时过期,导致大量请求无法得到处理 数据保存在缓存中,并设 ...

最新文章

  1. 【CentOS】设置服务开机自动启动
  2. Cocos2d:使用 CCCamera 做滚动效果 (Four Ways of Scrolling with Cocos2D)
  3. 鸿蒙系统代码开源不担忧友商,华为鸿蒙系统正式开源!打脸“安卓套皮论”,友商手机也可采用!...
  4. Python文件夹与文件的操作
  5. vim 使用中的一些错误[omnifunc未设置错误]
  6. python队列怎么用_如何在Python中使用多处理队列? - python
  7. this.getstate_Java线程类Thread.State getState()方法(带示例)
  8. 刷题总结——湫湫系列故事——设计风景线(hdu4514 并差集判环+树的直径)
  9. SQL Server 2008 各种DateTime的取值范围
  10. Oracle18C RPM安装介绍
  11. ICML 2019收录774篇论文:谷歌153篇,清华北大26篇
  12. 微软推出Windows Sandbox:可安全运行任何应用的一次性VM\n
  13. 【协同任务】基于matlab蚁群算法多无人机攻击调度【含Matlab源码 034期】
  14. leetcode 125 valid-palindrome
  15. MDT+ADK网络部署操作系统
  16. movs 数据传送指令_Introduction to CSAPP(十二):数据传送指令与 C 语言赋值
  17. Angular 入门教程系列:33:移动端统计图表F2
  18. 2018天梯赛第一次训练题解和ac代码
  19. Android应用市场转移【2021-03-22】
  20. 简单的抢地盘js小游戏实战篇(多人在线对战)

热门文章

  1. 如何高效使用搜索引擎 - 善用高级搜索
  2. win10应用软件乱码一种解法----环境配置那些事儿
  3. 一个罕见的Http403错误分析
  4. 最简真分数 1199
  5. TTS技术在语音识别中的应用:提升语音交互的准确性
  6. php案例 记录当前用户的访问次数
  7. 我的毕设之基于WEB的征婚网站的设计与实现
  8. 菜鸟工具在线Java_Java 测验五(函数)
  9. 热乎的面经——小试牛刀
  10. 小白学做网云小程序(2)