Redis-更新策略,缓存穿透,缓存雪崩,缓存击穿

Redis-更新策略,缓存穿透,缓存雪崩,缓存击穿

1.缓存更新 策略

  1. 淘汰策略
  2. 超时剔除
  3. 主动更新
    1. 更新策略:先修改数据库还是先删除缓存 结论:先修改数据库,因为缓存的操作比较快,容易产生数据不一致
    2. 更新缓存还是删除缓存?

2.缓存穿透

客户端请求的数据在缓存和数据库中都不存在,这些请求会访问到数据库

解决方式

  1. 缓存空值:额外内存空间; 短期造成数据不一致
  2. 布隆过滤器,把数据转换成二进制的情况存储,即使在布隆过滤其中存在,实际上也可能不存在,因此有一定的风险
  3. 增加id复杂度,主动预防缓存穿透情况
  4. 增强用户的权限

3.缓存雪崩

是指在同一时间大量的缓存失效或redis服务器宕机,导致大量的请求同时访问数据库。

解决方式:

  1. 设置缓存key随机的TTL
  2. 增加redis服务高可用
  3. 大量的请求限流
  4. 多级缓存,nginx,jvm,浏览器等
  5. 设置热点数据不过期

4.缓存击穿

缓存击穿也叫热点Key问题,一个被高并发访问并且缓存业务重建复杂的key失效了,导致大量的key访问数据库带来冲击。

解决方式:

  1. 互斥锁

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 逻辑过期

    在这里插入图片描述

在这里插入图片描述

  1. 热点数永不过期

  2. 限流熔断

缓存击穿-互斥锁代码实现:
 /** 缓存数据KEY */
    private final String CACHE_SHOP_KEY = "CACHE_SHOP_KEY:";
    /** 缓存互斥锁KEY */
    private final String CACHE_SHOP_LOCK_KEY = "CACHE_SHOP_LOCK_KEY:";

    @Autowired
    private StringRedisTemplate stringRedisTemplate;




/**
     * redis缓存
     * 缓存击穿-互斥锁版本
     * @param bookId
     */
    private BooksVo tryCacheMutex(Long bookId) {
        // RedisKey
        String cacheKey = CACHE_SHOP_KEY + bookId;
        // 1.从Redis查询商铺缓存
        // 获取缓存数据
        String contentBook = stringRedisTemplate.opsForValue().get(cacheKey);
        // 2.判断缓存是否命中
        if (StringUtils.isNotBlank(contentBook)){
            // 3.1缓存命中 直接返回结果
            return JSONUtil.toBean(contentBook, BooksVo.class);
        }
        BooksVo booksVo = null;
        try {
            // 3.2缓存未命中,尝试获取互斥锁
            if (BooleanUtil.isFalse(tryLock(bookId))) {
                // 4.1获取互斥锁失败,尝试重试
                Thread.sleep(50);
                return tryCacheMutex(bookId);
            }
            // 4.2 获取互斥锁成功
            // 4.3 再次检测缓存是存在 doubleCheck
            contentBook = stringRedisTemplate.opsForValue().get(cacheKey);
            if (StringUtils.isNotBlank(contentBook)){
                return JSONUtil.toBean(contentBook, BooksVo.class);
            }
            // 4.4 查询数据库,缓存重建
            booksVo = this.queryById(bookId);
            // 模拟缓存重建延迟
            Thread.sleep(200);
            String jsonBook = JSONUtil.toJsonStr(booksVo);
            stringRedisTemplate.opsForValue().set(cacheKey,jsonBook);
        } catch (InterruptedException e) {
            throw new RuntimeException();
        }finally {
            // 5.释放锁
            stringRedisTemplate.delete(CACHE_SHOP_LOCK_KEY + bookId);
        }

        return booksVo;
    }

    /**
     * 获取互斥锁
     */
    public boolean tryLock(Long bookId){
        // redis: set xxx value nx ex 10 添加锁nx是互斥,ex设置过期时间
        Boolean aBoolean = stringRedisTemplate.opsForValue()
            .setIfAbsent(CACHE_SHOP_LOCK_KEY + bookId, "1", 10, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(aBoolean);
    }

后期更新逻辑过期的实现方式

========20240209

更新一波工具类完成互斥锁操作

@Slf4j
@Component
@AllArgsConstructor
public class PikerRedisUtils {

    private final StringRedisTemplate stringRedisTemplate;


    /**
     * 存储String值,设置TTL
     */
    private void set(String key, Object value, Long time, TimeUnit unit){
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, unit);
    }

    /**
     * 存储逻辑过期时间
     */
    private void setExpiration(String key, Object value, Long time, TimeUnit unit){
        // 封装过期时间
        RedisData redisData = new RedisData();
        redisData.setData(JSONUtil.toJsonStr(value));
        redisData.setExpireSecond(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(redisData));
    }

    /**
     * <R, ID> 用于指定此方法所处理的泛型类型:
     *   - R:代表方法返回的结果类型,即从缓存或数据库中获取的具体业务对象类型。
     *   - ID:标识要查询的特定资源的唯一标识符类型,通常为某种主键或唯一键。

     * 方法功能:
     *   实现缓存击穿防护机制,结合互斥锁(Mutex)避免大量并发请求直接穿透缓存到达数据库,
     *   造成数据库压力过大。适用于高并发场景下对同一资源(由 `ID` 标识)的频繁访问。

     * 参数说明:
     *   - `key`:用于构造缓存键的基础部分,与 `id` 结合形成完整的缓存键。
     *   - `id`:指定要查询资源的唯一标识符,用于从缓存或数据库中定位具体数据。
     *   - `tClass`:`Class<R>` 类型参数,表示期望从缓存或数据库中获取数据的类类型,用于反序列化 JSON 数据。
     *   - `time`:表示在尝试获取互斥锁时愿意等待的最大时间量。
     *   - `unit`:时间单位,与 `time` 结合确定等待锁的实际时长。
     *   - `lockKey`:互斥锁的键名,在 Redis 中用于实现分布式锁。
     *   - `dbFallback`:`Function<ID, R>` 类型参数,提供一个回调函数,当缓存未命中时,用于查询数据库并返回 `R` 类型数据。

     * 方法逻辑:
     *   1. 构建基于 `key` 和 `id` 的 Redis 缓存键。
     *   2. 尝试从 Redis 中获取缓存数据。
     3. 若缓存命中,则直接反序列化并返回缓存中的数据。
     4. 若缓存未命中:
     a. 尝试获取互斥锁,若获取失败则短暂休眠后递归调用自身重试。
     b. 若成功获取互斥锁,进行双重检查以确认缓存是否在等待锁期间已被其他线程填充。
     c. 若缓存仍为空,则调用 `dbFallback` 函数查询数据库,获取 `R` 类型数据。
     d. 将数据库查询结果转化为 JSON 存储至 Redis,完成缓存重建。
     e. 最后释放互斥锁,确保资源的正确释放。

     * 返回值:
     *   返回与给定 `id` 对应的 `R` 类型数据,该数据来源于缓存(优先)或数据库查询(缓存未命中时)。
     *
     */
    public <R,ID> R getMutex(String key, ID id, Class<R> tClass, Long time,
                             TimeUnit unit, String lockKey, Function<ID, R> dbFallback){
        // RedisKey
        String cacheKey = key + id;
        // 1.从Redis查询商铺缓存
        // 获取缓存数据
        String contentBook = stringRedisTemplate.opsForValue().get(cacheKey);
        // 2.判断缓存是否命中
        if (StringUtils.isNotBlank(contentBook)){
            // 3.1缓存命中 直接返回结果
            return JSONUtil.toBean(contentBook, tClass);
        }
        R r = null;
        try {
            // 3.2缓存未命中,尝试获取互斥锁
            if (BooleanUtil.isFalse(tryLock(lockKey, time ,unit))) {
                // 4.1获取互斥锁失败,尝试重试
                Thread.sleep(50);
                return getMutex(key, id, tClass, time, unit, lockKey, dbFallback);
            }
            // 4.2 获取互斥锁成功
            // 4.3 再次检测缓存是存在 doubleCheck
            contentBook = stringRedisTemplate.opsForValue().get(cacheKey);
            if (StringUtils.isNotBlank(contentBook)){
                return JSONUtil.toBean(contentBook, tClass);
            }
            // 4.4 查询数据库,缓存重建
            r = dbFallback.apply(id);
            // 模拟缓存重建延迟
            // Thread.sleep(200);
            String jsonBook = JSONUtil.toJsonStr(r);
            stringRedisTemplate.opsForValue().set(cacheKey,jsonBook);
        } catch (InterruptedException e) {
            throw new RuntimeException();
        }finally {
            // 5.释放锁
            stringRedisTemplate.delete(lockKey);
        }

        return r;
    }
    
    
    /**
     * 获取互斥锁
     */
    public boolean tryLock(String lockKey,Long time,  TimeUnit unit){
        // redis: set xxx value nx ex 10 添加锁nx是互斥,ex设置过期时间
        Boolean aBoolean = stringRedisTemplate.opsForValue()
            .setIfAbsent(lockKey, "1", time, unit);
        return BooleanUtil.isTrue(aBoolean);
    }

调用

@Override
    public BooksVo selectById(Long bookId) {
        // 缓存击穿-工具类-互斥锁
        return pikerRedisUtils.getMutex(CACHE_SHOP_KEY,bookId,BooksVo.class, 10L,
            TimeUnit.SECONDS, CACHE_SHOP_LOCK_KEY+bookId,item -> baseMapper.selectVoById(bookId));

        // 缓存击穿-互斥锁
        // return tryCacheMutex(bookId);

        // 缓存击穿-逻辑过期时间
        // return tryCacheMutex2(bookId);

    }

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

强化学习-Reinforcement learning | RL

目录 什么是强化学习? 强化学习的应用场景 强化学习的主流算法 强化学习是机器学习的一种学习方式,它跟监督学习、无监督学习是对应的。本文将详细介绍强化学习的基本概念、应用场景和主流的强化学习算法及分类。 什么是强化学习? 强化学习并不是某一种特定的算法,而是…

2001-2022年上市公司异常审计费用指标包含原始数据 参考顶刊文献含构造过程Stata代码

01、数据介绍 异常审计费用则是指实际审计费用超过或低于正常审计费用的部分&#xff0c;该部分审计费用受不可观测因素的影响&#xff0c;可能来源于审计师所付出的额外努力或者审计师与被审计单位间的特殊关系&#xff0c;也可能产生于被审计单位在审计买方市场中的优势地位…

(学习日记)2024.04.17:UCOSIII第四十五节:中断管理

写在前面&#xff1a; 由于时间的不足与学习的碎片化&#xff0c;写博客变得有些奢侈。 但是对于记录学习&#xff08;忘了以后能快速复习&#xff09;的渴望一天天变得强烈。 既然如此 不如以天为单位&#xff0c;以时间为顺序&#xff0c;仅仅将博客当做一个知识学习的目录&a…

【操作系统专题】详解操作系统 | 操作系统的目标和功能 | 操作系统如何工作

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1.操作系统的目标和功能2…

【菜狗学前端】原生Ajax笔记(包含原生ajax的get/post传参方式、返回数据等)

这回图片少&#xff0c;给手动替换了~祝看得愉快&#xff0c;学的顺畅&#xff01;哈哈 一 原生ajax经典四步 (一) 原生ajax经典四步 第一步&#xff1a;创建网络请求的AJAX对象&#xff08;使用XMLHttpRequest&#xff09; JavaScript let xhr new XMLHttpRequest() 第二…

为什么你的LDO输出不稳定?

原文来自微信公众号&#xff1a;工程师看海&#xff0c;与我联系&#xff1a;chunhou0820 看海原创视频教程&#xff1a;《运放秘籍》 大家好&#xff0c;我是工程师看海。 前一阵朋友和我说当初用某型号LDO时&#xff0c;发现输出异常&#xff0c;仔细阅读datasheet后&#x…

Clip下游任务解读

相关代码链接见文末 1.DALL-1 (1)VQGAN https://arxiv.org/pdf/2012.09841.pdf VQGAN(Vector Quantized Generative Adversarial Networks)是一种基于向量化量化的生成对抗网络。这种技术首先将图像转换为一系列向量,每个向量代表图像中的一小块区域(或称为“patch”)。…

在Mac上更好的运行Windows,推荐这几款Mac虚拟机 mac运行windows虚拟机性能

想要在Mac OS上更好的运行Windows系统吗&#xff1f;推荐你使用mac虚拟机。虚拟机通过生成现有操作系统的全新虚拟镜像&#xff0c;它具有真实windows系统完全一样的功能&#xff0c;进入虚拟系统后&#xff0c;所有操作都是在这个全新的独立的虚拟系统里面进行&#xff0c;可以…

Linux的文件操作中的静态库的制作

Linux操作系统支持的函数库分为&#xff1a; 静态库&#xff0c;libxxx.a&#xff0c;在编译时就将库编译进可执行程序中。 优点&#xff1a;程序的运行环境中不需要外部的函数库。 缺点&#xff1a;可执行程序大 &#xff08;因为需要 编译&#xff09; 动态库&#xff0c…

自动化测试Junit

1.什么是Junit JUint是Java编程语言的单元测试框架&#xff0c;用于编写和运行可重复的自动化测试。 JUnit 促进了“先测试后编码”TDD的理念&#xff0c;强调建立测试数据的一段代码&#xff0c;可以先测试&#xff0c;然后再应用。这个方法就好比“测试一点&#xff0c;编码一…

Qt QProcess详解

1.简介 QProcess提供了在 Qt 应用程序中启动外部程序的方法。通过QProcess&#xff0c;你可以启动一个进程&#xff0c;与它通信&#xff08;发送输入和读取输出&#xff09;&#xff0c;检查它的状态&#xff0c;以及等待它完成。这个类在执行系统命令、运行其他程序或脚本时…

Leetcode 394. 字符串解码

心路历程&#xff1a; 这道题看到括号直接想到栈&#xff0c;五分钟新题直接秒了&#xff0c;一开始以为需要两个栈分别存储数字和非数字&#xff0c;后来发现一个栈就够了&#xff0c;思路如图&#xff1a; 这道题考察的应该是队栈这两种数据结构的转换&#xff0c;因为每次…

LangChain - 文档加载

文章目录 一、关于 检索二、文档加载器入门指南 三、CSV1、使用每个文档一行的 CSV 数据加载 CSVLoader2、自定义 csv 解析和加载 &#xff08;csv_args3、指定用于 标识文档来源的 列&#xff08;source_column 四、文件目录 file_directory1、加载文件目录数据&#xff08;Di…

缺少vcruntime140_1.dll

windows安装mysql的时候错误提示&#xff1a; 64位下载安装&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1u_ALo0JMc-Y2an22l1Y1EA 提取码&#xff1a;ve10 32位下载安装&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/16XTt642Tj-Oc-WvbgQK-Ww 提取码…

学校4-11天梯赛选拔赛

目录 L1-5 6翻了 题目 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; 思路 AC代码 L1-1 嫑废话上代码 题目 输入格式&#xff1a; 输出格式&#xff1a; 输入样例&#xff1a; 输出样例&#xff1a; AC代码 L1-8 刮刮彩…

MySQL_00001_00000

数据准备 员工表&#xff1a;emp Oracle: create table emp ( empno number(4) not null, ename varchar2(10), job varchar2(9), mgr number(4), hiredate date, sal number(7, 2), comm number(7, 2), deptno number(2) ); insert into em…

bugku-web-decrypt

这里的提示解密后没有什么意义 这里下载文件包 得到一个index.php文件 得到代码 <?php function encrypt($data,$key) {$key md5(ISCC);$x 0;$len strlen($data);$klen strlen($key);for ($i0; $i < $len; $i) { if ($x $klen){$x 0;}$char . $key[$x];$x1;}for…

计算机炸了,电子信息也是劝退专业?

还不是因为这个版本&#xff0c;计算机专业受到了制裁&#xff0c;导致这些偏计算机类的专业也受到了牵连 我本科的时候是一所双一流院校的计科专业&#xff0c;我们学校的电子信息专业堪称苦逼&#xff0c;我们计科学的东西&#xff0c;他们都要学&#xff0c;他们学的一些东…

什么是享元模式,有哪些具体应用

一、定义 享元模式是一种通过尽可能多地共享数据来最小化内存使用和对象数量&#xff0c;从而提高性能的设计模式。在享元模式中&#xff0c;如果需要相同数据的多个对象&#xff0c;则共享这些对象而不是创建新的对象&#xff0c;从而提高系统的效率。 其实有很多应用场景&am…

基于顺序表实现通讯录

上篇我们讲了顺序表是什么&#xff0c;和如何实现顺序表。这篇文章我们将基于顺序表来实现通讯录。 文章目录 前言一、基于顺序表是如何实现的二、通讯录的头文件和实现文件三、通讯录的实现3.1 定义通讯录结构3.2 初始化通讯录3.3 销毁通讯录3.4 通讯录添加数据3.5 查找联系人…