Java --- redis7之缓存预热+雪崩+穿透+击穿

news2024/11/24 22:49:34

目录

一、缓存预热

二、缓存雪崩 

三、缓存穿透 

3.1、解决方案 

 3.1.1、空对象缓存或者缺省值

3.1.2、Goolge布隆过滤器Guava解决缓存穿透 

 四、缓存击穿 

4.1、危害 

 4.2、解决方案

 4.3、模拟百亿补贴活动案例

一、缓存预热

场景:MySQL有N条新记录,redis没有

1、MySQL做数据新增 ,redis利用回写机制,让它逐步实现100条新增记录的同步,部署发布版本的时候,自己人提前做一次redis同步。

2、通过中间件或者程序自行完成。

3、使用白名单

二、缓存雪崩 

场景:

1、redis主机挂掉,redis全盘崩溃,偏硬件运维。

2、redis中有大量key同时过期大面积失效,偏软件开发。 

预防和解决:

1、redis中key设置为永不过期或者过期时间错开

2、redis缓存集群实现高可用:①、主从+哨兵。②、redis集群。③、开启redis持久化机制aof/rdb,尽快恢复缓存集群。

3、多缓存结合预防雪崩:ehcache本地缓存+redis缓存。

4、服务降级:Hystrix或者阿里sentinel限流和降级

5、用钱购买阿里云---》云数据库redis。

三、缓存穿透 

查询一条或N条数据,redis中没有,mysql中也没有,但请求每次都会去查询数据库,导致后台数据库压力暴增,就是缓存穿透。 

3.1、解决方案 

 3.1.1、空对象缓存或者缺省值

 黑客或恶意攻击:

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

key相同攻击系统:

第一次访问到MySQL,空对象缓存后第二次就返回defaultNull缺省值,避免MySQL被攻击,不用到数据库中去走一圈了。

key不同攻击系统:

由于存在空对象缓存和缓存回写,redis中的垃圾key会越写越多(设置key过期时间)。

3.1.2、Goolge布隆过滤器Guava解决缓存穿透 

 白名单过滤器:

1、误判问题,概率小可以接受,不能从布隆过滤器中删除

2、全部合法的key都需要放入Guava版布隆过滤器+redis里面,不然数据就是返回null

 代码实现:

改pom

 <!--google开源guava-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>23.0</version>
        </dependency>

测试案例:

  /**
     * 创建Guava布隆过滤器测试
     */
    @Test
    public void testGuavaAndBloomFilter(){
        //创建Guava布隆过滤器
        BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), 100);
        //判断指定的元素是否存在
        System.out.println(bloomFilter.mightContain(1));
        System.out.println(bloomFilter.mightContain(2));
        System.out.println("加入后");
        //将元素添加进布隆过滤器
        bloomFilter.put(1);
        bloomFilter.put(2);
        System.out.println(bloomFilter.mightContain(1));
        System.out.println(bloomFilter.mightContain(2));
    }

 使用百万数据测试:

@Service
@Slf4j
public class GuavaBloomFilterServiceImpl implements GuavaBloomFilterService {
    public static final Integer  _1W = 10000;
    //定义guava布隆过滤器,初始容量
    public static final Integer  SIZE = 100 * _1W;
    //误判率,误判率越小误判个数越少
    public static double fpp = 0.03;
    //创建guava布隆过滤器
    private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), SIZE,fpp);
    @Override
    public void guavaBloomFilter() {
        //在guava布隆过滤器加入一百万白名单数据
        for (int i = 0; i < SIZE; i++) {
           bloomFilter.put(i);
        }
        //取十万个不在合法范围的数据测试
        ArrayList<Integer> list = new ArrayList<>(10 * _1W);
        //测试
        for (int i = SIZE + 1; i < SIZE+(10 * _1W) ; i++) {
            //判断在guava布隆过滤器是否存在
            if (bloomFilter.mightContain(i)){
                log.info("被误判:{}",i);
                list.add(i);
            }
        }
        log.info("误判总数为:{}",list.size());
    }
}
@RestController
@Api(tags = "Google的guavaBloomFilter")
@Slf4j
public class GuavaBloomFilterController {
    @Autowired
    private GuavaBloomFilterService guavaBloomFilterService;
    @RequestMapping(value = "/guavaBloomFilter",method = RequestMethod.GET)
    @ApiOperation("guava布隆过滤器插入100万样本数据和10万测试数据")
    public void guavaBloomFilter(){
        guavaBloomFilterService.guavaBloomFilter();
    }
}

结论 :100000 / 3033 = 0.03033

源码分析:

 四、缓存击穿 

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

4.1、危害 

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

 4.2、解决方案

 热点key失效的原因:

1、时间到了自然被清除但还是被访问到

2、删除的key,刚好被访问。

方案1:

差异失效时间,对于访问频繁的热点key,不设置过期时间。

方案2:

互斥更新,采用双检加锁策略。

 4.3、模拟百亿补贴活动案例

@Data
@AllArgsConstructor
@NoArgsConstructor
@ApiModel(value = "百亿补贴活动模拟")
public class Product {
    //商品id
    private Long id;
    //商品名
    private String name;
    //商品价格
    private Integer price;
    //商品描述
    private String detail;
}
@Service
@Slf4j
public class BYBTTaskServiceImpl implements BYBTTaskService {
    public static final String BYBT_KEY = "bybt";
    public static final String BYBT_KEY_A = "bybt:a";
    public static final String BYBT_KEY_B = "bybt:b";
    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 模拟MySQL添加商品
     * @return
     */
    public List<Product> getProductFromById(){
        List<Product> list = new ArrayList<>();
        for (int i = 1; i <= 20; i++) {
            Random random = new Random();
            int id = random.nextInt(1000);
            Product product = new Product((long) id, "product" + i, i, "鸽子蛋" + i);
            list.add(product);
        }
        return list;
    }
//    @PostConstruct
    public void initBYBT(){
        log.info("启动定时器模拟百亿补贴活动开始。。。");
        //使用线程模拟定时任务,后台任务定时将MySQL里的活动商品刷新到redis中
        new Thread(()->{
            while (true){
                //从MySQL中查询数据,写入redis
                List<Product> list = this.getProductFromById();
                //删除过期key
                redisTemplate.delete(BYBT_KEY);
                //使用redis的list数据结果存储最新数据
                redisTemplate.opsForList().leftPushAll(BYBT_KEY,list);
                //暂停2分钟模拟,模拟百亿补贴参加活动商品
                try {
                    TimeUnit.MINUTES.sleep(2);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        },"a1").start();
    }
    @PostConstruct
    public void initBYBTAB(){
        log.info("启动AB定时器模拟百亿补贴活动开始。。。" + DateUtil.now());
        //使用线程模拟定时任务,后台任务定时将MySQL里的活动商品刷新到redis中
        new Thread(()->{
            while (true){
                //从MySQL中查询数据,写入redis
                List<Product> list = this.getProductFromById();
                //先更新缓存B,让缓存B过期时间超过缓存A,缓存A突然失效,还有缓存B,以防止缓存击穿
                redisTemplate.delete(BYBT_KEY_B);
                redisTemplate.opsForList().leftPushAll(BYBT_KEY_B,list);
                //设置过期时间
                redisTemplate.expire(BYBT_KEY_B,86410L,TimeUnit.SECONDS);
                //更新缓存A
                redisTemplate.delete(BYBT_KEY_A);
                redisTemplate.opsForList().leftPushAll(BYBT_KEY_A,list);
                //设置过期时间
                redisTemplate.expire(BYBT_KEY_A,86400L,TimeUnit.SECONDS);
                //暂停2分钟模拟,模拟百亿补贴参加活动商品
                try {
                    TimeUnit.MINUTES.sleep(2);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"a1").start();
    }
}
@RestController
@Slf4j
@Api(tags = "百亿补贴活动模拟")
public class BYBTTaskController {
    public static final String BYBT_KEY = "bybt";
    public static final String BYBT_KEY_A = "bybt:a";
    public static final String BYBT_KEY_B = "bybt:b";
    @Autowired
    private RedisTemplate redisTemplate;
    /**
     * 分页查询,查询redis
     * @return
     */
    @RequestMapping(value = "/findPage",method = RequestMethod.GET)
    @ApiOperation("查询商品,每次1页每页5条显示")
    public List<Product> findPage(int page,int size){
        List<Product> list =null;
        long start = (page - 1) * size;
        long end = start + size - 1;
        try {
            //使用redis的list里的lrange查询分页
            list = redisTemplate.opsForList().range(BYBT_KEY,start,end);
            if (CollectionUtils.isEmpty(list)){
                //查询为空就去mysql中查询
                return null;
            }
            log.info("参加活动商品:{}",list);
        } catch (Exception e) {
            //出现异常,一般redis出现事故
            log.error("bybt异常:{}",e);
        }
        return list;
    }
    @RequestMapping(value = "/findPageAB",method = RequestMethod.GET)
    @ApiOperation("双缓存查询商品,每次1页每页5条显示")
    public List<Product> findPageAB(int page,int size){
        List<Product> list =null;
        long start = (page - 1) * size;
        long end = start + size - 1;
        try {
            //先去缓存A中查找
            list = redisTemplate.opsForList().range(BYBT_KEY_A,start,end);
            if (CollectionUtils.isEmpty(list)){
                log.info("缓存A已失效,暂时采用缓存B");
                //若缓存A没有就去缓存B中查找
                list = redisTemplate.opsForList().range(BYBT_KEY_B,start,end);
                if (CollectionUtils.isEmpty(list)){
                    //TODO 去MySQL查找
                    return null;
                }
            }
            log.info("参加活动商品:{}",list);
        } catch (Exception e) {
            //出现异常,一般redis出现事故
            log.error("bybt异常:{}",e);
            e.printStackTrace();
        }
        return list;
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/520022.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Java的班级管理系统的设计与实现(论文+源码)_kaic

摘 要 伴随着信息技术不断的飞速发展&#xff0c;信息技术给现在的生活也在带来翻天覆地的变化。信息时代的到来已经成为一种趋势&#xff0c;人类的发展历史正在进入到一个新的时代。信息技术已经在生活中的各个领域得到普及以及被应用。班级管理在信息技术发展之前一直都是…

金子数量——c++

金子数量 Description 某地区有n条(编号依次为1到n)互不交叉的道路&#xff0c;每条道路上都有m个数字&#xff0c;其中能被8整除的数称为金子数&#xff0c;这个数字表示其重量。 如下表是3条道路&#xff0c;每条道路中有5个数的一种可能情况。 苏海想在n条道路中走一条金子…

[架构之路-195]-《软考-系统分析师》- MVC、MVP、MVVM架构各自的优缺点

目录 一、MVC 1.1 MVC的基本结构 1.2 MVC的工作流程 1.3 MVC的优缺点 二、MVP 2.1 MVP的基本结构 2.2 MVP的工作流程 2.3 MVP的优缺点 三、MVVM 3.1 MVVM的基本结构 3.2 MVVM的工作流程 3.3 MVVM的优缺点 前言&#xff1a; MVC&#xff1a;一个controller对应多个v…

Windows SSH远程连接Linux服务器 - 无公网IP内网穿透

文章目录 前言视频教程1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 转…

学系统集成项目管理工程师(中项)系列02_文件和标准

1. 文件 1.1. 2020 年 4 月&#xff0c;中共中央国务院颁发《关于构建更加完善的要素市场化配置体制的意见》首次将数据作为一种新型的生产要素置入文件 1.1.1. 【21下选01】 1.2. 2021 年印发的 5G 应用扬帆行动计划(2021-2023)的通知到 2023 年&#xff0c;我国 5G 应用发…

数列合并C语言

已知两个不同长度的降序排列的数列&#xff08;假设序列的长度都不超过5&#xff09;&#xff0c;请编程将其合并为一个数列&#xff0c;使合并后的数列仍保持降序排列。 【提示】假设两个降序排列的数列分别保存在数组a和数组b中&#xff0c;用一个循环&#xff0c;从前往后依…

利用 Amazon API Gateway 和 Amazon Lambda 处理 Cloudfront 的内容请求

概述 国内 Amazon Cloudfront 目前不支持 Lambdaedge 功能&#xff0c;不能实现基于 CDN 的 A/B 测试、rewrite、redirect、token 认证和产生 response 等功能&#xff0c;本文介绍如何利用 API Gateway 和 Lambda 实现 Lambdaedge 的功能。下面实验介绍通过 request header 参…

HttpClient5如何进行代理

文章目录 说明原始HttpClient代理HttpClient总结 说明 在这篇文章中会对HttpClient5如何进行代理进行说明&#xff0c;我的HttpClient版本是5.2.1。在进行代理之前请先准备好代理服务器。 原始HttpClient 下面是没有进行代理设置的代码&#xff0c;尝试去访问openai接口 Tes…

GPT-1面试题

简介 1、GPT-1 是什么&#xff1f;它是基于什么模型的&#xff1f; GPT-1 是一种用于自然语言处理的机器学习模型&#xff0c;它是基于 Transformer 架构的。它是 OpenAI 在 2018 年发布的第一个 GPT 模型&#xff0c;有 1.17 亿个参数&#xff0c;比之前的语言模型有了显著的…

PowerShell

2023年5月12日&#xff0c;周五晚上&#xff1a; 今天突然对看到了一篇关于Linux的Shell的文章&#xff0c;引起了我的兴趣&#xff0c;但由于我的电脑没有虚拟机&#xff0c;所以我决定研究研究一下windows的PowerShell&#xff0c;感觉还挺好玩的。 打开了一个新世界。 不…

【最新可用】chatGPT镜像网站国内使用,免费稳定!

新建了一个网站 https://ai.weoknow.com/ 每天给大家更新可用的国内可用chatGPT 2023.5.8新增一个 ChatGPT 国内免翻版 【网站名称】&#xff1a;Chat GPT Ai 【使用环境】&#xff1a;移动端/电脑网页端 ChatGPT是一款功能强大的免费在线聊天机器人&#xff0c;具有人工智能…

Bean的生命周期及演示

文章目录 一、介绍概念组成&#xff1a; 二、实例演示 一、介绍 概念 Bean的生命周期是指一个Bean对象从创建到销毁的整个存在过程。 组成&#xff1a; 1.实例化Bean&#xff08;为Bean分配内存空间&#xff09; 2.属性注入 (Bean注入和装配) 3.Bean的初始化 各种通知&#…

【Midjourney】Midjourney 辅助工具 ② ( 自定义命令工具 | 设置颜色 | 设置材质 | 设置随机种子 | 设置图片链接 )

文章目录 一、Midjourney Prompt Tool 自定义命令工具1、设置颜色参数2、设置材质参数3、设置随机种子参数4、设置图片链接 Midjourney 提示词命令 可以使用 辅助工具 进行生成 , 辅助工具如下 : Midjourney Prompt Tool 自定义命令工具Midjourney Prompt Generator 命令生成器…

C语言实现【三子棋小游戏】

Hello,各位小伙伴们好啊&#xff0c;这篇博客是用C语言实现了三子棋的游戏&#xff0c;感兴趣的友友可以试试玩哦&#xff5e; 咦&#xff0c;当我们以玩家的身份和电脑玩这个游戏的时候&#xff0c;有哪些步骤呢&#xff1f;那么&#xff0c;反过来&#xff0c;我们设计游戏的…

C++智能指针的原理、分类、使用

1. 智能指针介绍 为解决裸指针可能导致的内存泄漏问题。如&#xff1a; a&#xff09;忘记释放内存&#xff1b; b&#xff09;程序提前退出导致资源释放代码未执行到。 就出现了智能指针&#xff0c;能够做到资源的自动释放。 2. 智能指针的原理和简单实现 2.1 智能指针的原…

『python爬虫』19. aiohttp模块应用之下载图片(保姆级图文)

目录 1. aiohttp库安装2. 代码解析3. 实现代码总结 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 欢迎关注 『python爬虫』 专栏&#xff0c;持续更新中 通过爬取下载得到图片 1. aiohttp库安装 pip install aiohttp这个库的作用基本上和request一致&#xff0c;理解…

SpringBoot——RUST风格以及如何快速发送不同方式的请求

RUST风格&#xff1a; 简单来说&#xff0c;RUST就是一种将请求方式融合到路径中的一种请求路径书写风格&#xff0c;注意这里是风格&#xff0c;不是规定&#xff0c;我们也可以不使用他或者不是非常严格的按照他规定的样式来写&#xff0c;但是由于行业中大多数的人在编程的…

Firefox 112 发布:右键单击显示密码、改进标签管理等!

Firefox 发布 112 版本&#xff0c;为我们带来了显示密码的新选择、同时改进了标签管理等。 Mozilla 于 2023 年 4 月 11 日发布了 Firefox 112&#xff0c;新版本包含一些令人兴奋的新功能和改进。该浏览器的最新版本带来了一些功能以增强用户体验和提高性能。 在 Ubuntu 上运…

UNIAPP实战项目笔记69 订单确认时显示为默认地址

UNIAPP实战项目笔记69 订单确认时显示为默认地址 思路 需要用到vuex 默认显示isDefault为1的地址 案例截图 订单结算页面 地址页面 代码 shopcart.vue <template><view class"shop-cart"><template v-if" list.length > 0 "><!-…

【Linux学习笔记】设备驱动模型详解——总线、设备、驱动和类

学习内容 设备驱动模型视频讲解 简介 设备驱动是计算机系统中的重要组成部分&#xff0c;它们允许操作系统与硬件交互。设备驱动模型是一种通用的抽象框架&#xff0c;用于描述操作系统如何管理硬件设备。这里我们将介绍设备驱动模型中的四个关键概念&#xff1a;总线、设备…