Redis缓存击穿问题以及解决方案

news2024/9/29 7:19:15

Redis缓存击穿问题以及解决方案

  • 前言
  • 一、什么是Redis缓存击穿
  • 二、解决方案
    • 1.使用锁来解决
      • 使用锁的流程:
      • 核心思路:
      • 思路流程图:
      • 操作的锁的代码:
      • 业务的实现:
    • 2.逻辑过期来解决
      • 思路分析:
      • 解决流程:
      • 业务实现:


前言

跟随黑马视频虎哥学习redis:

这是我认为b站上最好的redis教程,各方面讲解透彻,知识点覆盖比较全。
黑马redis视频链接:B站黑马redis教学视频
本文参考黑马redis课程笔记


一、什么是Redis缓存击穿

缓存击穿问题也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击。

逻辑分析:
假设线程1在查询缓存之后,本来应该去查询数据库,然后把这个数据重新加载到缓存的,此时只要线程1走完这个逻辑,其他线程就都能从缓存中加载这些数据了,但是假设在线程1没有走完的时候,后续的线程2,线程3,线程4同时过来访问当前这个方法, 那么这些线程都不能从缓存中查询到数据,那么他们就会同一时刻来访问查询缓存,都没查到,接着同一时间去访问数据库,同时的去执行数据库代码,对数据库访问压力过大

在这里插入图片描述

二、解决方案

1.使用锁来解决

因为锁能实现互斥性。假设线程过来,只能一个人一个人的来访问数据库,从而避免对于数据库访问压力过大,但这也会影响查询的性能,因为此时会让查询的性能从并行变成了串行,我们可以采用tryLock方法 + double check来解决这样的问题。

使用锁的流程:

假设现在线程1过来访问,他查询缓存没有命中,但是此时他获得到了锁的资源,那么线程1就会一个人去执行逻辑,假设现在线程2过来,线程2在执行过程中,并没有获得到锁,那么线程2就可以进行到休眠,直到线程1把锁释放后,线程2获得到锁,然后再来执行逻辑,此时就能够从缓存中拿到数据了。
在这里插入图片描述

核心思路:

相较于原来从缓存中查询不到数据后直接查询数据库而言,现在的方案是 进行查询之后,如果从缓存没有查询到数据,则进行互斥锁的获取,获取互斥锁后,判断是否获得到了锁,如果没有获得到,则休眠,过一会再进行尝试,直到获取到锁为止,才能进行查询。

如果获取到了锁的线程,再去进行查询,查询后将数据写入redis,再释放锁,返回数据,利用互斥锁就能保证只有一个线程去执行操作数据库的逻辑,防止缓存击穿

思路流程图:

在这里插入图片描述

操作的锁的代码:

核心思路就是利用redis的setnx方法来表示获取锁,该方法含义是redis中如果没有这个key,则插入成功,返回1,在stringRedisTemplate中返回true, 如果有这个key则插入失败,则返回0,在stringRedisTemplate返回false,我们可以通过true,或者是false,来表示是否有线程成功插入key,成功插入的key的线程我们认为他就是获得到锁的线程。

private boolean tryLock(String key) {
	//使用redis的set nx进行分布式锁的获取
    Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    return BooleanUtil.isTrue(flag);
}

private void unlock(String key) {
    stringRedisTemplate.delete(key);
}

业务的实现:

// 实现缓存重构
        //获取互斥锁
        String lockKey = "lock:shop:" + id;
        Shop shop = null;
        try {
            boolean isLock = tryLock(lockKey);
            //判断否获取成功
            if(!isLock){
                //失败,则休眠重试
                Thread.sleep(50);
                return queryWithMutex(id);
            }
            //成功,根据id查询数据库
             shop = getById(id);
            //不存在,返回错误
            if(shop == null){
                 //将空值写入redis
                stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
                //返回错误信息
                return null;
            }
            //写入redis
            stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_NULL_TTL,TimeUnit.MINUTES);

        }catch (Exception e){
            throw new RuntimeException(e);
        }
        finally {
            //释放互斥锁
            unlock(lockKey);
        }

2.逻辑过期来解决

思路分析:

当用户开始查询redis时,判断是否命中,如果没有命中则直接返回空数据,不查询数据库,而一旦命中后,将value取出,判断value中的过期时间是否满足,如果没有过期,则直接返回redis中的数据,如果过期,则在开启独立线程后直接返回之前的数据,独立线程去重构数据,重构完成后释放互斥锁。

解决流程:

在这里插入图片描述

业务实现:


//定义一个线程池
private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);
public Shop queryWithLogicalExpire( Long id ) {
    String key = CACHE_SHOP_KEY + id;
    // 1.从redis查询商铺缓存
    String json = stringRedisTemplate.opsForValue().get(key);
    // 2.判断是否存在
    if (StrUtil.isBlank(json)) {
        // 3.存在,直接返回
        return null;
    }
    // 4.命中,需要先把json反序列化为对象
    RedisData redisData = JSONUtil.toBean(json, RedisData.class);
    Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);
    LocalDateTime expireTime = redisData.getExpireTime();
    // 5.判断是否过期
    if(expireTime.isAfter(LocalDateTime.now())) {
        // 5.1.未过期,直接返回店铺信息
        return shop;
    }
    // 5.2.已过期,需要缓存重建
    // 6.缓存重建
    // 6.1.获取互斥锁
    String lockKey = LOCK_SHOP_KEY + id;
    boolean isLock = tryLock(lockKey);
    // 6.2.判断是否获取锁成功
    if (isLock){
    	//开启独立线程去解决:
        CACHE_REBUILD_EXECUTOR.submit( ()->{

            try{
                //重建缓存
                this.saveShop2Redis(id,20L);
            }catch (Exception e){
                throw new RuntimeException(e);
            }finally {
                unlock(lockKey);
            }
        });
    }
    // 6.4.返回过期的商铺信息
    return shop;
}

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

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

相关文章

YOLOv5改进系列(17)——更换IoU之MPDIoU(ELSEVIER 2023|超越WIoU、EIoU等|实测涨点)

【YOLOv5改进系列】前期回顾: YOLOv5改进系列(0)——重要性能指标与训练结果评价及分析 YOLOv5改进系列(1)——添加SE注意力机制

【数据结构】_5.栈

目录 1. 概念 2. 栈的使用 2.1 方法 2.2 示例 3. 栈的模拟实现 4. 栈的应用场景 4.1 题目1:不可能的出栈序列 4.2 题目2:逆序打印单链表 4.3 题目3:逆波兰表达式求值 4.4 题目4:括号匹配 4.5 题目5:栈的压入…

如何构建高效的接口自动化测试框架

在选择接口测试自动化框架时,需要根据团队的技术栈和项目需求来综合考虑。对于测试团队来说,使用Python相关的测试框架更为便捷。无论选择哪种框架,重要的是确保 框架功能完备,易于维护和扩展,提高测试效率和准确性。今…

最适合新手的SpringBoot+SSM项目《苍穹外卖》实战—(五)员工管理

黑马程序员最新Java项目实战《苍穹外卖》,最适合新手的SpringBootSSM的企业级Java项目实战。 新增员工 设计 DTO 类 我们需要根据新增员工接口设计对应的 DTO 类去接收前端传递的参数,前端传递参数列表如下: 注意: 当前端提交的…

C++ 成员初始化列表和new运算符的应用

输出一个学生的学号&#xff0c;姓名&#xff0c;性别和出生年月日 class Date { public:Date(int y, int m, int d) :m_year(y), m_month(m), m_day(d) {}void Print(){cout << m_year << "/" << m_month << "/" << m_day…

第四章 No.1树状数组的原理与使用

文章目录 应用问题原理树状数组练习题241. 楼兰图腾242. 一个简单的整数问题243. 一个简单的整数问题2244. 谜一样的牛 线段树的反面&#xff1a;树状数组原理复杂&#xff0c;实现简单 应用问题 支持两个操作&#xff1a;快速求前缀和任意地修改某个数&#xff0c;时间复杂度…

520 · 一致性哈希 II

链接&#xff1a;LintCode 炼码 - ChatGPT&#xff01;更高效的学习体验&#xff01; 题解&#xff1a; class Solution{private:int n;const int mVirtualNodeCount;map<int, int> mVirtualNodeToMachineIdMap;set<int> mVirtualNodeSet;public:Solution(int n…

SpringBoot的三层架构以及IOCDI

目录 一、IOC&DI入门 二、三层架构 数据库访问层 业务逻辑层 控制层 一、IOC&DI入门 在软件开发中&#xff0c;IOC&#xff08;Inversion of Control&#xff09;和DI&#xff08;Dependency Injection&#xff09;是密切相关的概念。 IOC&#xff08;控制反转&a…

CAN学习笔记2:CAN简介

CAN 1 概述 CAN(Controller Area Network),是 ISO 国际标准化的串行通信协议,目的是适应汽车“减少线束的数量”、“通过多个网络进行大量数据的高速传输”的需求。 低速 CAN(ISO11519)通信速率 10~125Kbps&#xff0c;总线长度可达 1000米. 高速 CAN(ISO11898)通信速率 125~…

cnn卷积神经网络(基础)

convolutional neural networks 特征提取&#xff08;卷积、下采样&#xff09;->分类器 &#xff08;全连接&#xff09; 卷积过程 依次进行数乘 &#xff08;每个相同位置上的数字相乘再加和&#xff09; 左右数乘矩阵channel数量要一样&#xff0c;输出得到一个通道 卷…

吴师傅教你几招极速清理C盘,高能操作绝不让你失望!

电脑使用久了&#xff0c;C盘堆积的垃圾过多&#xff1b;每天上网会给电脑带来很多临时文件&#xff0c;这些垃圾文件不清理掉时间久了就会影响到电脑的运行速度&#xff1b;也会导致C盘变红&#xff0c;空间不足。那么&#xff0c;电脑C盘满了如何清理呢&#xff1f;教你几招极…

SpringBoot 如何进行 统一异常处理

在Spring Boot中&#xff0c;可以通过自定义异常处理器来实现统一异常处理。异常处理器能够捕获应用程序中抛出的各种异常&#xff0c;并提供相应的错误处理和响应。 Spring Boot提供了ControllerAdvice注解&#xff0c;它可以将一个类标记为全局异常处理器。全局异常处理器能…

NICE-SLAM: Neural Implicit Scalable Encoding for SLAM论文阅读

论文信息 标题&#xff1a;NICE-SLAM: Neural Implicit Scalable Encoding for SLAM 作者&#xff1a;Zihan Zhu&#xff0c; Songyou Peng&#xff0c;Viktor Larsson — Zhejiang University 来源&#xff1a;CVPR 代码&#xff1a;https://pengsongyou.github.io/nice-slam…

ARM单片机中断处理过程解析

前言 中断&#xff0c;在单片机开发中再常见不过了。当然对于中断的原理和执行流程都了然于胸&#xff0c;那么对于ARM单片机中断的具体处理行为&#xff0c;你真的搞清楚了吗&#xff1f; 今天来简单聊一聊&#xff0c;ARM单片机中断处理过程中的具体行为是什么样的&#xf…

spring5源码篇(13)——spring mvc无xml整合tomcat与父子容器的启动

spring-framework 版本&#xff1a;v5.3.19 文章目录 整合步骤实现原理ServletContainerInitializer与WebApplicationInitializer父容器的启动子容器的启动 相关面试题 整合步骤 试想这么一个场景。只用 spring mvc&#xff08;确切来说是spring-framework&#xff09;&#x…

Windows环境下安装及部署Nginx

一、安装Nginx教程 1、官网下载地址&#xff1a;https://nginx.org/en/download.html 2、下载教程&#xff1a;选择Stable version版本下载到本地 3、下载完成后&#xff0c;解压放入本地非中文的文件夹中&#xff1a; 4、启动nginx&#xff1a;双击nginx.exe&#xff0c;若双击…

Vue 3:玩一下web前端技术(五)

前言 本章内容为VUE语法的简单学习与相关语法讨论。 上一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;四&#xff09;_Lion King的博客-CSDN博客 下一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;六&#xff09;_L…

算法与数据结构(四)--排序算法

一.冒泡排序 原理图&#xff1a; 实现代码&#xff1a; /* 冒泡排序或者是沉底排序 *//* int arr[]: 排序目标数组,这里元素类型以整型为例; int len: 元素个数 */ void bubbleSort (elemType arr[], int len) {//为什么外循环小于len-1次&#xff1f;//考虑临界情况&#xf…

自动驾驶感知系统-全球卫星定位系统

卫星定位系统 车辆定位是让无人驾驶汽车获取自身确切位置的技术&#xff0c;在自动驾驶技术中定位担负着相当重要的职责。车辆自身定位信息获取的方式多样&#xff0c;涉及多种传感器类型与相关技术。自动驾驶汽车能够持续安全可靠运行的一个关键前提是车辆的定位系统必须实时…

为什么你的独立站有流量没转化?如何做诊断检查?

新店的创业初期&#xff0c;即使网站有流量&#xff0c;但是销售额为零的情况也常有发生。如果你确定流量是高质量的&#xff0c;寻找阻止潜在客户购买的具体因素可能会感到困难重重。 从“立即购买”按钮的色彩选择这样的细节&#xff0c;到构建品牌故事这样的大计划&#xf…