Redis7——进阶篇(五)

news2025/3/15 3:39:35

 前言:此篇文章系本人学习过程中记录下来的笔记,里面难免会有不少欠缺的地方,诚心期待大家多多给予指教。


基础篇:

  1. Redis(一)
  2. Redis(二)
  3. Redis(三)
  4. Redis(四)
  5. Redis(五)
  6. Redis(六)
  7. Redis(七)
  8. Redis(八)

进阶篇:

  1. Redis(九)
  2. Redis(十)
  3. Redis(十一)
  4. Redis(十二)

接上期内容:上期完成了相关案例的学习。下面学习缓存穿透、预热、雪崩、击穿,话不多说,直接发车。


一、缓存预热

(一)、定义

缓存预热是一种在系统启动阶段或者特定时间点,将一些经常访问或者关键的数据提前加载到缓存中的操作,以减少对数据源(如数据库)的访问次数,从而提高系统的响应速度和性能。避免在用户首次请求时才去加载数据而导致的性能延迟。


(二)、功能

  1. 减少首次请求延迟:当用户首次访问某些数据时,如果没有进行缓存预热,系统需要从数据库等数据源中查询数据,这个过程可能会比较耗时。
  2. 减轻数据库压力:在系统运行初期,如果大量用户同时发起请求,这些请求都直接访问数据库,会给数据库带来巨大的压力,甚至可能导致数据库性能下降或者崩溃。缓存预热可以将部分数据提前加载到缓存中,后续的请求优先从缓存中获取数据,从而减少了对数据库的访问频率,降低了数据库的负载
  3. 提高系统性能和稳定性:由于缓存的读写速度通常比数据库等数据源快很多,缓存预热可以让系统在处理请求时更快地获取数据,从而提高系统的整体性能。同时,减少了对数据库的依赖,降低了因数据库故障或性能问题导致系统不可用的风险,增强了系统的稳定性。

(三)、常用方案

  1.  硬编码(不大推荐):在代码中直接明确地指定需要加载到缓存的数据和逻辑,不通过外部配置或动态计算来改变。在系统启动时,按照预先编写好的代码逻辑将数据加载到缓存中。
  2. @PostConstruct注解: Java 中的一个注解,用于标记一个方法,该方法会在依赖注入完成之后、对象正式投入使用之前被自动调用。
  3. 定时器任务:通过定时任务框架(如 Spring 的@Scheduled注解、Quartz 等),按照预设的时间间隔(如每天凌晨、每小时等)自动执行缓存预热操作。
  4. 数据脚本:使用脚本语言(如 Python、Shell 等)编写脚本,通过脚本连接到缓存系统,将数据加载到缓存中。 

二、缓存雪崩

(一)、名词解释

缓存雪崩是指在某一时刻,缓存中大量的键在同一时间点或者在极短的时间内集中过期失效,或者缓存服务器发生故障导致缓存服务不可用,此时大量原本可以从缓存中获取数据的请求,都直接涌向了数据库等后端数据源,给数据库带来巨大的压力,甚至可能导致数据库不堪重负而崩溃,进而使整个系统出现性能急剧下降、服务不可用等严重问题。


(二)、发生场景

1、硬件方面

缓存服务器的硬件设备(如硬盘、内存、网卡等)出现故障,可能会导致缓存服务无法正常运行,从而发生缓存雪崩现象。


2、业务方面

大量的业务key同时过期,比如在进行缓存预热时,为大量缓存数据设置了相同的过期时间,当这个过期时间到达时,这些缓存数据会同时失效,从而引起缓存雪崩现象。


(三)、预防与解决措施

硬件方面无法把控,主要从业务方面来解决。

业务方面:

  1. 避免大量缓存键同时过期:①、设置随机时间:在设置缓存键的过期时间时,为每个键的过期时间添加一个随机的偏移量,避免它们集中在同一时刻过期。②、设置key用不过期

  2. redis集群实现服务高可用:使用缓存服务器的集群模式,集群模式可以将数据分散存储在多个节点上,当某个节点出现故障时,其他节点仍然可以正常提供服务,保证缓存服务的可用性。

  3. 多缓存结合:采用多级缓存架构,例如同时使用本地缓存(ehcache)+redis缓存。

  4. 服务降级:在应用层对请求进行限流,当请求量超过一定阈值时,直接拒绝部分请求,避免过多的请求直接访问数据库。


三、缓存穿透

(一)、名词解释

缓存穿透是指客户端请求的数据在缓存中不存在,同时在数据库中也不存在,这样每次该请求都会穿透缓存,直接访问数据库。如果有大量这样的无效请求持续涌入,会对数据库造成极大的压力,甚至可能导致数据库不堪重负而崩溃。例如,黑客可能会故意发起大量不存在的键的请求,以消耗数据库资源。


(二)、发生场景

  1. 黑客恶意攻击:攻击者可能会利用系统的漏洞,构造大量不存在的请求,如不存在的用户 ID、商品 ID 等,向系统发起请求。由于这些请求对应的数据在缓存和数据库中都不存在,会导致大量请求直接穿透缓存访问数据库,从而影响系统的正常运行。
  2. 业务数据异常:在业务系统中,可能会出现数据不一致或者数据删除不及时的情况。
  3. 错误的用户输入:如果系统没有对用户输入进行严格的验证,用户可能会输入错误的查询条件,如输入一个不存在的订单号、手机号码等。这些无效请求会直接穿透缓存访问数据库。

(三)、预防与解决措施

1、方案一

缓存空值或默认值,当查询的数据在数据库中不存在时,在缓存中存储一个空值或者默认值,并设置一个较短的过期时间。这样下次相同的请求就可以直接从缓存中获取空值或默认值,而不会再穿透到数据库。


2、方案二

使用布隆过滤器,①、自研布隆过滤器。②、使用Google  Guava 库实现布隆过滤器。


(四)、案例演示

自研简略版布隆过滤器在上一篇已经学习过了,下面将学习Google Guava实现方式。Guava源码地址:GitHub - google/guava: Google core libraries for Java

1、需求说明

模拟使用Guava布隆过滤器拦截掉非法数字,对于合法的数字放行。


2、导入依赖

<!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.2-jre</version>
</dependency>

3、编码实现

public class GuavaBloomFilterDemo {
    public static void main(String[] args) {
        //误判率,它越小误判的个数也就越少(思考,是不是可以设置的无限小,没有误判岂不更好)
        //fpp the desired false positive probability 0.0 < fpp < 1.0,误判率越低,消耗的资源越多,哈希函数也用的越多,误判率也就越低
        // 默认值 0.03
        BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), 1000000, 0.03);
        //1 先往布隆过滤器里面插入100万的样本数据
        for (int i = 1; i <= 1000000; i++) {
            bloomFilter.put(i);
        }

        List<Integer> list1 = Arrays.asList(120000000, 111111, 10, 222222222, 42575, 123457);

        list1.forEach(e -> {
            boolean result = bloomFilter.mightContain(e);
            if (result) {
                // TODO 查redis → 查数据库 → .....
                System.out.println("存在,放行" + e);
            } else {
                // 直接返回结果
                System.out.println("不存在,拦截" + e);
            }
        });

        //故意取10万个不在过滤器里的值,看看有多少个会被认为在过滤器里
        List<Integer> list = new ArrayList<>(1000000);
        for (int i = 1000000 + 1; i <= 1100000; i++) {
            if (bloomFilter.mightContain(i)) {
                list.add(i);
            }
        }
        System.out.println("误判的总数量::{}" + list.size());
    }
}


四、缓存击穿

(一)、名词解释

缓存击穿是指在高并发的场景下,一个非常热点的 key 在缓存中过期失效的瞬间,大量针对该 key 的请求同时涌入。由于此时缓存中没有该 key 对应的数据,这些请求就会全部转向数据库去查询。数据库在短时间内需要处理大量的查询请求,从而承受巨大的压力,可能会导致数据库性能急剧下降,甚至出现崩溃的情况,进而影响整个系统的正常运行。


(二)、发生场景

  1. 热点数据过期:在电商系统中,一款热门商品的信息会被大量用户频繁访问,为了减轻数据库压力,会将该商品信息缓存起来并设置过期时间。当这个过期时间到达,缓存中的数据失效,而此时恰好有大量用户同时发起对该商品信息的请求,就会出现缓存击穿的情况。
  2. 流量突发:一些突发的热点事件会导致瞬间产生大量的请求。比如,某明星突然发布一条微博,引发大量粉丝同时访问其个人主页,而该主页信息在缓存中过期,大量请求就会直接冲向数据库。
  3. 数据预热不正确:比如新闻网站在每天早上进行缓存预热,但遗漏了当天的一条重大热点新闻,当用户大量访问该新闻时,就会出现问题。

(三)、预防与解决措施

1、方案一

使用双检加锁策略。前面已经学习过了,不在阐述。

public String get(String key) {
        String value = redis.get(key);// 查询缓存
        if (value != null) {
            //缓存存在直接返回
            return value;
        } else {
            //缓存不存在则对方法加锁
            //假设请求量很大,缓存过期
            synchronized (this) {
                value = redis.get(key); // 在查一遍redis
                if (value == null) {
                    // 从数据库获取数据
                    value = dao.get(key);
                    // 设置过期时间并回写到缓存
                    redis.setex(key, time, value);
                }
                return value;
            }
        }
    }

2、方案二

随机退避策略。当发现缓存中热点 key 失效时,让各个请求不要立即去访问数据库,而是各自随机等待一段不同的时间后再去尝试获取数据。这样可以避免大量请求在同一时刻集中访问数据库,将请求的时间分散开,减轻数据库在短时间内的压力。

public String randomBackoffMethod(String key) {
        try {
            Jedis jedis = RedisUtils.getJedis();
            String data = jedis.get(key);
            if (data == null) {
                try {
                    // 生成随机退避时间 毫秒
                    int backoffTime = new Random().nextInt(2000);
                    Thread.sleep(backoffTime);
                    // 再次尝试从缓存获取数据,
                    // 但是在高并发场景下,可能线程随机退避时间会一样,
                    // 为了避免造成缓存双写不一致问题,使用双检锁策略来防止
                    String value = jedis.get(key);// 查询缓存
                    if (value != null) {
                        //缓存存在直接返回
                        return value;
                    } else {
                        //缓存不存在则对方法加锁
                        //假设请求量很大,缓存过期
                        synchronized (this) {
                            value = jedis.get(key); // 在查一遍redis
                            if (value == null) {
                                // 从数据库获取数据
                                value = dao.get(key);
                                // 设置过期时间并回写到缓存
                                jedis.setex(key, time, value);
                            }
                            return value;
                        }
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
            jedis.close();
            return data;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

3、方案三

差异失效策略。它的核心思想是避免多个热点 Key 在同一时刻同时失效,从而防止大量请求在瞬间全部涌向数据库,给数据库造成过大压力。

在缓存击穿场景中的实现方式为一个热点key拷贝两份,两份缓存过期时间不一样,将缓存失效的时间分散开来,以此保障系统的稳定性与性能。

/**
     * 差异失效策略查询
     */
    public String differentialFailureSelectMethod(String key) {
        // 同一个热点key的前缀
        String prefixA = "keyA:";
        String prefixB = "keyB:";
        try {
            Jedis jedis = RedisUtils.getJedis();
            String data = jedis.get(prefixA + key);
            if (data == null) {
                System.out.println("=========A缓存已经失效");
                //用户先查询缓存A(上面的代码),如果缓存A查询不到(例如,更新缓存的时候删除了),再查询缓存B
                data = jedis.get(prefixB + key);
                if (data == null) {
                    System.out.println("=========B缓存已经失效");
                    //TODO 查数据库 → 回写redis
                }
                return data;
            }
            System.out.println("查询结果:{}" + data);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return null;
    }

    /**
     * 差异失效策略更新
     */
    public void differentialFailureUpdateMethod(String key) {
        try {
            // 以前的某个热点key
            String oldHotKeyA = "oldHotKey:" + key;
            String oldHotKeyB = "oldHotKey:" + key;

            // 新热点key前缀
            String prefixA = "newKeyA:";
            String prefixB = "newKeyB:";
            //模拟从数据库查数据
            Jedis jedis = RedisUtils.getJedis();
            Object o = dao.get(key);
            //先更新B缓存
            jedis.del(oldHotKeyB);
            jedis.set(prefixB + o.getId());
            jedis.expire(prefixB + o.getId(), 2000);
            //再更新A缓存
            jedis.del(oldHotKeyA);
            jedis.set(prefixA + o.getId());
            jedis.expire(prefixA + o.getId(), 1000);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

五、总结 

一图总结,四个问题以及解决办法:

问题类型核心问题解决方案典型场景
缓存预热数据未提前加载启动加载、定时任务、脚本电商大促前的商品信息加载
缓存雪崩大量缓存失效随机过期、集群、高可用、限流大量key过期、缓存服务器宕机时
缓存穿透无效请求攻击布隆过滤器、缓存空值恶意攻击场景
缓存击穿热点 Key 失效双检锁、差异失效、随机退避秒杀活动中的商品信息访问

ps:努力到底,让持续学习成为贯穿一生的坚守。学习笔记持续更新中。。。。

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

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

相关文章

VBA+FreePic2Pdf 找出没有放入PDF组合的单个PDF工艺文件

设计部门针对某个项目做了一个工艺汇总报告&#xff0c;原先只要几十个工艺文件&#xff0c;组合成一个PDF&#xff0c;但后来要求要多放点PDF进去&#xff0c;但工艺文件都混在一起又不知道哪些是重复的&#xff0c;找上我让我帮忙处理一下&#xff0c;我开始建议让她重新再组…

计网面试准备

正确理解网络数据传输过程 同一路由器的不同接口属于不同局域网&#xff0c;广播只能在同一个局域网

【数据分享】1999—2023年我国地级市社会消费品零售总额和年末金融机构存贷款余额(Shp/Excel格式)

在之前的文章中&#xff0c;我们分享过基于2000-2024年《中国城市统计年鉴》整理的1999-2023年地级市的人口相关数据、染物排放和环境治理相关数据和房地产投资情况和商品房销售面积相关指标数据&#xff08;均可查看之前的文章获悉详情&#xff09;&#xff01; 本次我们分享…

PHP批量去除Bom头的方法

检查的代码&#xff1a; <?php$dir __DIR__; $files new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir));foreach ($files as $file) {if ($file->isFile() && pathinfo($file, PATHINFO_EXTENSION) php) {$content file_get_contents(…

字节攻克关键技术,大模型训练效率提升1.7倍,成本节省40%

近日&#xff0c;字节豆包大模型团队开源针对 MoE 架构的关键优化技术COMET&#xff0c;该技术可将大模型训练效率提升1.7倍&#xff0c;成本节省40%。据悉&#xff0c;该技术已实际应用于字节的万卡集群训练&#xff0c;累计帮助节省了数百万 GPU 小时训练算力。 MoE&#xff…

[Pytorch报错问题解决]AttributeError: ‘nn.Sequential‘ object has no attribute ‘append‘

问题 运行深度学习代码的时候遇到了以下报错问题&#xff1a; Traceback (most recent call last):File "/home/anaconda3/envs/Text2HOI/lib/python3.9/site-packages/torch/autograd/grad_mode.py", line 28, in decorate_contextreturn func(*args, **kwargs)Fi…

基于威胁的安全测试值得关注,RASP将大放异彩

2‍021年7月21日&#xff0c;由中国信息通信研究院&#xff08;CAICT&#xff09;指导、悬镜安全主办、腾讯安全协办的中国首届DevSecOps敏捷安全大会&#xff08;DSO 2021&#xff09;在北京圆满举办。大会以“安全从供应链开始”为主题&#xff0c;寓意安全基础决定“上层建筑…

AGI大模型(2):GPT:Generative Pre-trained Transformer

1 Generative Pre-trained Transformer 1.1 Generative生成式 GPT中的“生成式”指的是该模型能够根据输入自动生成文本内容&#xff0c;而不仅仅是从已有的文本库中检索答案。 具体来说&#xff1a; 生成&#xff08;Generative&#xff09;&#xff1a;GPT是一个生成…

DeepSeek 助力 Vue3 开发:打造丝滑的表格(Table)之添加列宽调整功能,示例Table14_06带搜索功能的固定表头表格

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

MySQL再次基础 向初级工程师迈进

作者&#xff1a;在计算机行业找不到工作的大四失业者 Run run run ! ! ! 1、MySQL概述 1.1数据库相关概念 1.2MySQL数据库 2、SQL 2.1SQL通用语法 SQL语句可以单行或多行书写&#xff0c;以分号结尾。SQL语句可以使用空格/缩进来增强语句的可读性。MySQL数据库的SQL语句不区…

使用 Doris 和 Hudi

作为一种全新的开放式的数据管理架构&#xff0c;湖仓一体&#xff08;Data Lakehouse&#xff09;融合了数据仓库的高性能、实时性以及数据湖的低成本、灵活性等优势&#xff0c;帮助用户更加便捷地满足各种数据处理分析的需求&#xff0c;在企业的大数据体系中已经得到越来越…

城市林业的无声革命:人工智能与古老生态学如何重新设计城市

城市林业的无声革命&#xff1a;人工智能与古老生态学如何重新设计城市 在摩天大楼的阴影下&#xff0c;一场静悄悄的变革正在发生——它融合了硅芯片与古老根系&#xff0c;算法与原住民智慧。 作者&#xff1a;保罗桑杜 作者利用 PicLumen 创建的图像 城市森林不再只是城市…

Linux第七讲:基础IO

Linux第七讲&#xff1a;基础IO 1.什么是文件2.文件操作的复习2.1文件基本操作复习2.2将信息输出到显示器&#xff0c;你有哪种方法2.3stdin、stdout、stderror2.4细节问题讲解 3.系统文件IO3.1open函数使用3.1.1理解标志位3.1.2权限问题3.1.3write和read接口介绍3.1.4谈谈fd以…

力扣热题 100:多维动态规划专题经典题解析

系列文章目录 力扣热题 100&#xff1a;哈希专题三道题详细解析(JAVA) 力扣热题 100&#xff1a;双指针专题四道题详细解析(JAVA) 力扣热题 100&#xff1a;滑动窗口专题两道题详细解析&#xff08;JAVA&#xff09; 力扣热题 100&#xff1a;子串专题三道题详细解析(JAVA) 力…

【Unity】在项目中使用VisualScripting

1. 在packagemanager添加插件 2. 在设置中进行初始化。 Edit > Project Settings > Visual Scripting Initialize Visual Scripting You must select Initialize Visual Scripting the first time you use Visual Scripting in a project. Initialize Visual Scripting …

Pytest自动化测试框架pytest-xdist分布式测试插件

平常我们功能测试用例非常多时&#xff0c;比如有1千条用例&#xff0c;假设每个用例执行需要1分钟&#xff0c;如果单个测试人员执行需要1000分钟才能跑完&#xff1b; 当项目非常紧急时&#xff0c;会需要协调多个测试资源来把任务分成两部分&#xff0c;于是执行时间缩短一…

文件解析漏洞靶场解析全集详解

lls解析漏洞 目录解析 在网站的下面将一个1.asp文件夹&#xff0c;在里面建一个2.txt文件在里面写入<% -now()%>这个显示时间的代码&#xff0c;再将文件名改为2.jpg。 发现2.jpg文件以asp形式执行 畸形文件解析 将2.jpg文件移到网站的下面与1.asp并列&#xff0c;将名…

【一次成功】Win10本地化单机部署k8s v1.31.2版本及可视化看板

【一次成功】Win10本地化单机部署k8s v1.31.2版本及可视化看板 零、安装清单一、安装Docker Desktop软件1.1 安装前<启用或关闭Windows功能> 中的描红的三项1.2 查看软件版本1.3 配置Docker镜像 二、更新装Docker Desktop三、安装 k8s3.1 点击启动安装3.2 查看状态3.3 查…

Vue项目搜索引擎优化(SEO)终极指南:从原理到实战

文章目录 1. SEO基础与Vue项目的挑战1.1 为什么Vue项目需要特殊SEO处理&#xff1f;1.2 搜索引擎爬虫工作原理 2. 服务端渲染&#xff08;SSR&#xff09;解决方案2.1 Nuxt.js框架实战原理代码实现流程图 2.2 自定义SSR实现 3. 静态站点生成&#xff08;SSG&#xff09;技术3.1…