Redis数据缓存

news2025/1/31 8:25:09

缓存

一 缓存基础

1 缓存的概念和作用

缓存就是数据交换的缓冲区(称作Cache),是存贮数据的临时地方,一般读写性能较高

2 缓存的使用

之前没有使用缓存是的模型

3 项目说明

当我们查询商家信息的时候,直接从mysql中获取的。现在我们将原来的项目改造。改造地方在ShopController,我们按照流程图去做,添加redis缓存,业务都是在service中实现的。

# 具体实现流程
1 redis中查询商户缓存
2 判断是否存在
3 存在直接返回
4 不存在根据id去数据库查询
5 数据库也不存在,返回错误
6 存在则写入redis中
7 返回

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>

1 修改controller
/**
    * 根据id查询商铺信息
     * @param id 商铺id
     * @return 商铺详情数据
*/
@GetMapping("/{id}")
public Result queryShopById(@PathVariable("id") Long id) {
    return shopService.queryById(id);
    //        return Result.ok(shopService.getById(id));
}
2 修改service
@Resource
private RedisTemplate<String,Object> redisTemplate;
​
@Override
public Result queryById(Long id) {
    //1 redis中查询商户缓存
    String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
    //2 判断是否存在
    if(StrUtil.isNotBlank(shopJson)){
        //3存在直接返回
        Shop shop = JSONUtil.toBean(shopJson, Shop.class);
        return Result.ok(shop);
    }
    //4 不存在根据id去数据库查询
    Shop shop = this.getById(id);
    //5 数据库也不存在,返回错误
    if(shop==null){
        return Result.fail("店铺不存在");
    }
    //6 存在则写入redis中
    redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop));
    //7 返回
    return Result.ok(shop);
}
3 改造首页

@Autowired
private RedisTemplate<String, Object> redisTemplate;
​
@Override
public Result queryTypeList() {
​
    List<Object> list = redisTemplate.opsForList().range("cache.list",0,-1);
    if(list!=null && list.size()!=0){
        return Result.ok(list);
    }
    List<ShopType> sort = this.query().orderByAsc("sort").list();
    if(sort==null||sort.size()==0){
        return Result.fail("列表不存在");
    }
    sort.forEach(s->redisTemplate.opsForList().rightPush("cache.list",s));
    return Result.ok(sort);
}

二 数据一致性

1 思路

  • 查询数据的时候,如果缓存未命中,则查询数据库,将数据写入缓存设置超时时间

  • 修改数据时,先修改数据库,在删除缓存。

  • 延时双删策略

2 代码实现

  • 修改更新方法,添加超时时间

 @Override
    public Result queryById(Long id) {
        //1 redis中查询商户缓存
        String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
        //2 判断是否存在
        if(StrUtil.isNotBlank(shopJson)){
            //3存在直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        //4 不存在根据id去数据库查询
        Shop shop = this.getById(id);
        //5 数据库也不存在,返回错误
        if(shop==null){
            return Result.fail("店铺不存在");
        }
        //6 存在则写入redis中
       redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
        //7 返回
        return Result.ok(shop);
    }

  • 修改ShopController

  
@PutMapping
    public Result updateShop(@RequestBody Shop shop) {
        // 写入数据库
​
        //shopService.updateById(shop);
        //return Result.ok();
        return  shopService.update(shop);
    }
  • 修改service代码 延时双删策略

 @Override
    public Result update(Shop shop) {
​
        Long id = shop.getId();
        if(id==null){
            return Result.fail("店铺id不存在");
        }
         // 删除缓存
        redisTemplate.delete("cache.shop:" + id);
        // 更新数据库
        updateById(shop);
        Thread.sleep(800);
        // 删除缓存
        redisTemplate.delete("cache.shop:" + id);
        return Result.ok();
    }

3 修改完代码以后,将所有的缓存删除,执行查询操作,多了超时

4 用postman执行修改方法: localhost:8081/shop

{
  "area":"大关",
  "sold":3035,
  "address":"金华路锦昌文华苑29号",
  "name":"102茶餐厅",
  "x":120.149192,
  "y":30.316078,
  "typeId":1,
  "id":1
}

执行完成以后,数据库的数据发生改变,查看redis的数据已经删除了。

三 缓存常见问题

1 缓存穿透:

客户端请求的数据,在数据库和redis中都不存在,这样缓存永远都不会生效,请求最终都到了数据库上。

解决方案:

  • 当在数据库查询的结果也不存在的时候,可以返回null值给redis,并且设置TTL

  • 布隆过滤器

布隆过滤器是一种数据结构,底层是位数组,通过将集合中的元素多次hash得到的结果保存到布隆过滤器中。主要作用就是可以快速判断一个元素是否在集合里面,但是因为算法的原因,也有一定概率的错误。

开发的时候我们一般选择空值值方式。

  • 代码方式实现

根据id查询的时候,如果信息不存在,则要将空值写入redis,并设置空值过期时间

@Override
    public Result queryById(Long id) {
        //1 redis中查询商户缓存
        String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
        //2 判断是否存在
        if(StrUtil.isNotBlank(shopJson)){
            //3存在直接返回
            Shop shop = JSONUtil.toBean(shopJson, Shop.class);
            return Result.ok(shop);
        }
        if(shopJson!=null){
            return Result.fail("店铺不存在");
        }
        //4 不存在根据id去数据库查询
        Shop shop = this.getById(id);
        //5 数据库也不存在,返回错误
        if(shop==null){
            // 空值写入redis中
            redisTemplate.opsForValue().set("cache.shop:" + id,"",3, TimeUnit.MINUTES);
            return Result.fail("店铺不存在");
        }
        //6 存在则写入redis中
       redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
        //7 返回
        return Result.ok(shop);
    }

2 缓存击穿:

也叫热点key问题,一个被高并发访问且业务复杂的key突然失效了,无数的请求瞬间给数据库带来的巨大冲击

解决方案: 互斥锁 逻辑过期

互斥锁思路:

查询缓存的时候,未命中需要获取锁 代码ShopServiceImpl

@Override
public Result queryById(Long id) {
    //1 redis中查询商户缓存
    String shopJson = (String)redisTemplate.opsForValue().get("cache.shop:" + id);
    //2 判断是否存在
    if(StrUtil.isNotBlank(shopJson)){
        //3存在直接返回
        Shop shop = JSONUtil.toBean(shopJson, Shop.class);
        return Result.ok(shop);
    }
    if(shopJson!=null){
        return Result.fail("店铺不存在");
    }
    Shop shop = null;
    String lockKey = "lock.id:" + id;
    try {
        //代码到这里说明没有命中缓存,那么就可以获取锁了
        boolean isLock = tryLock(lockKey);
        // 如果没有拿到锁,则等待一会,递归执行代码
        if(!isLock){
            Thread.sleep(100);
            queryById(id);
        }
        //获取锁成功
        //4 不存在根据id去数据库查询
        shop = this.getById(id);
        //5 数据库也不存在,返回错误
        if(shop==null){
            // 空值写入redis中
            redisTemplate.opsForValue().set("cache.shop:" + id,"",3, TimeUnit.MINUTES);
            return Result.fail("店铺不存在");
        }
        //6 存在则写入redis中
        redisTemplate.opsForValue().set("cache.shop:" + id,JSONUtil.toJsonStr(shop),30, TimeUnit.MINUTES);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        unlock(lockKey);
    }
    //7 返回
    return Result.ok(shop);
}

    // 获取锁
private boolean tryLock(String key){
    Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);
    return BooleanUtil.isTrue(flag);
}
//释放锁
private void unlock(String key){
    redisTemplate.delete(key);
}

3 缓存雪崩

同一时间段内,大量的缓存key失效或者redis宕机,到时大量的请求到达数据库,带来巨大的压力。

解决方案

  • 给key设置随机的TTL

  • 集群方案防止宕机不可用

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

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

相关文章

FFmpeg 的使用与Docker安装流媒体服务器

本文阐述的均为命令行的使用方式&#xff0c;并不牵扯FFmpeg 的 C音视频开发内容&#xff0c;补充一句&#xff0c;C的资料真的少&#xff0c;能把C学好的人&#xff0c;我真的是觉得巨佬。 我主要是使用FFmpeg 推流方面的知识&#xff0c;案例大都是靠近这方面。 一、FFmpeg…

寻找最富裕的小家庭 - 华为OD统一考试

OD统一考试(C卷) 分值: 100分 题解: Java / Python / C++ 题目描述 在一棵树中,每个节点代表一个家庭成员,节点的数字表示其个人的财富值,一个节点及其直接相连的子节点被定义为一个小家庭现给你一棵树,请计算出最富裕的小家庭的财富和。 输入描述 第一行为一个数N,…

系列十、Spring Security登录接口添加验证码

一、Spring Security登录接口添加验证码 1.1、概述 一般企业开发中&#xff0c;登录时都会有一个验证码&#xff0c;基于Spring Security的登录接口默认是没有验证码的&#xff1f;那么如何把验证码功能集成到Spring Security的登录接口呢&#xff1f;请看下文&#xff01; 1.…

SpringBoot视图渲染技术:整合Freemarker,常见指令和数据类型

目录 1.Freemarker 1.1.什么是Freemarker 1.2.Freemarker模板组成部分 1.3.优点 2.SpringBoot整合Freemarker 2.1.配置 2.2.数据类型 2.2.1.字符串 2.2.2.数值 2.2.3.布尔值 2.2.4.日期 2.3.常见指令 2.3.1.处理不存在的值 2.3.2.assign 2.3.3.if/elseif/else …

物联网中的通信技术

阅读引言&#xff1a; 本文主要大致为大家带来物联网中的常见的通信方式的知识梳理。 目录 一、概述 二、无线通信技术 1.物联网电子标签 RFID 1.1 RFID 概念 1.2 RFID 系统组成 2.WI-FI技术 3.UWB技术 4.ZigBee技术 5.NFC技术 6.蓝牙技术 7.EnOcean技术 一、概述 物…

ssm基于VUE.js的在线教育系统论文

摘 要 随着学习压力越来越大&#xff0c;课外参加补习班的学生越来越多。现在大多数学生采用请家教、自学、报名补习班的方式进行课外的额外学习。请家教费用昂贵&#xff0c;自学效率低&#xff0c;碰到自己不会的知识不能及时得到解达&#xff0c;报名补习班需要时间、地点的…

VBA技术资料MF106:检查单元格是否在表对象中

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…

Java中的包机制、final和super关键字

一、包机制 关于java语言当中的包机制&#xff1a; 1.包又被称为package,java中引入package这种语法机制主要是为了方便程序的管理。 不同功能的类被分门别类放到不同的软件包当中&#xff0c;查找比较方便&#xff0c;管理比较方便&#xff0c;易维护。 2.怎么定义package呢…

第 4 课 创建工作空间与功能包

文章目录 第 4 课 创建工作空间与功能包1.工作环境的创建2.ROS功能包的创建 第 4 课 创建工作空间与功能包 消息和服务的创建、发布器和订阅器的编写、服务端和客户端的编写都是基于Ros功能包进行操作的&#xff0c;因此在进行上述操作前&#xff0c;需要先创建工作空间及功能包…

基于Springboot的网上点餐系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的网上点餐系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构&am…

【Java学习】Java环境变量——配置jdk

本文我主要是介绍jdk的下载方式和在Windows系统下安装配置jdk11&#xff08;压缩包格式&#xff09;&#xff0c;其他格式的jdk以及Linux操作系统上的jdk安装我后续视情况进行更新… JDK的下载 大家可以去官网Java|Oracle下载对应的资源&#xff0c;也可以下载文末我上传的jd…

8 - MySQL数据读写分离|MySQL多实例

MySQL数据读写分离&#xff5c;MySQL多实例 MySQL数据读写分离数据读写分离如何实现数据的读写分离提供数据读写分离服务的软件&#xff08;中间件&#xff09;maxscale 软件提供的读写分离服务的工作过程配置数据读写分离结构 提供数据存储服务 MySQL多实例 MySQL数据读写分离…

最新国内可用GPT4、Midjourney绘画、DALL-E3文生图模型教程

一、前言 ChatGPT3.5、GPT4.0、GPT语音对话、Midjourney绘画&#xff0c;文档对话总结DALL-E3文生图&#xff0c;相信对大家应该不感到陌生吧&#xff1f;简单来说&#xff0c;GPT-4技术比之前的GPT-3.5相对来说更加智能&#xff0c;会根据用户的要求生成多种内容甚至也可以和…

Zabbix监控系统及部署

目录 前言 一个完整的项目 业务架构 运维架构 优秀监控软件的好处 1.zabbix概述 zabbix是什么 zabbix监控原理 Zabbix6.0新特性 1.Zabbix server高可用防止硬件故障或计划维护期的停机 2.Zabbix6.0 LTS新增Kubernetes监控功能&#xff0c;可以在Kubernetes系统从多个…

选择服务商搭建WiFi贴码小程序,有哪些注意事项呢?

随着移动互联网的快速发展&#xff0c;共享WiFi已经成为人们生活中不可或缺的一部分。在各种公共场所&#xff0c;如咖啡厅、餐厅、酒店、商场等&#xff0c;提供免费WiFi已经成为一种趋势。而WiFi贴码小程序的出现&#xff0c;为商家和用户提供了一个便捷的连接方式。那么&…

多语言历史报纸广告事件抽取(ACL2023)

1、写作动机&#xff1a; 首先&#xff0c;获取大规模的、有注释的历史数据集是困难的&#xff0c;因为只有领域专家才能可靠地为它们打标签。其次&#xff0c;大多数现成的NLP模型是在现代语言文本上训练的&#xff0c;这使得它们在应用于历史语料库时效果显著降低。这对于研…

2024年《一个项目征服Java中高级体系》博客计划

终于下决心来写一套大型的Java 笔记&#xff0c;不为别的&#xff0c;就是为了强迫自己将整个Java体系梳理清楚&#xff0c;让自己成为内功扎实的Java高级架构师。牛已经吹出来了&#xff0c;不做对不起网友&#xff01; 经过一个多月的持续规划&#xff0c;现在终于定好了整体…

调用Clarifai API提取图像特征

官方文档放前面&#xff1a;Images | Clarifai Guide 功能很多&#xff0c;有各种不同的模型&#xff0c;可以提取不同的图像特征&#xff0c;比如图像摘要、图像实体等。。。 具体的调用过程&#xff1a; 1.进入官方网站Full Stack AI Developer Platform: Production Compu…

FPGA 移位运算与乘法

题目&#xff1a; 已知d为一个8位数&#xff0c;请在每个时钟周期分别输出该数乘1/3/7/8,并输出一个信号通知此时刻输入的d有效&#xff08;d给出的信号的上升沿表示写入有效&#xff09; 由题意可知&#xff1a; 复位信号高有效&#xff0c;低复位&#xff1b;在inpu_grant上升…

关于编程的一些小小记录

这里记录一些关于编程的小技巧吧&#xff0c;算是个记录 1&#xff0c;vs同时有多个cpp文件怎么办&#xff1f; 我们只想运行第一个cpp文件&#xff0c;那么怎么做呢&#xff1f; 其实很简单&#xff0c;单击你不想让之运行的文件&#xff0c;点击最下面的属性 最后设置为这样…