使用Redis做缓存的小案例

news2024/11/26 12:19:02

        如果不了解Redis,可以查看本人博客:Redis入门

        Redis基于内存,因此查询速度快,常常可以用来作为缓存使用,缓存就是我们在内存中开辟一段区域来存储我们查询比较频繁的数据,这样,我们在下一次查询的时候,会直接去内存中查询,如果命中(查询到),就直接返回,否则就去数据库等在磁盘位置存储的数据进行查询,去磁盘硬盘等位置查询数据就比较慢了。因此,用好缓存对于我们的用户体验也是很重要的。

        本文基于springboot+mybatisplus+Redis进行实现。

        首先创建一个springboot项目:

        不勾选依赖:

        导入依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        配置application.yaml

spring:
  redis:
    host: localhost
    port: 6379
    password: 123456
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/hongyan_sso?characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai&nullCatalogMeansCurrent=true&allowPublicKeyRetrieval=true

        编写实体类:

package com.qcby.springbootdata.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

@TableName("t_user")
@Data
public class User {
    private Long id;
    private String userName;
    private String password;
    private String nickName;
}

        编写mapper:

package com.qcby.springbootdata.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qcby.springbootdata.pojo.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.tomcat.websocket.BackgroundProcess;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

        编写controller:

package com.qcby.springbootdata.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.qcby.springbootdata.mapper.UserMapper;
import com.qcby.springbootdata.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Arrays;
import java.util.List;

@RestController
@Slf4j
public class UserController {

    @Resource
    private UserMapper userMapper;
    @Resource
    private RedisTemplate<String,String> redisTemplate;

    @GetMapping("/get-user-by-nick")
    public String getUserByNick(Long  id){
        String key = buildKey(String.valueOf(id));
        if(Boolean.TRUE.equals(redisTemplate.hasKey(key))){
            log.info("命中缓存,id:{}",id);
            return redisTemplate.boundValueOps(key).get();
        }
        User userPO = userMapper.selectById(id);
        if(userPO == null){
            redisTemplate.boundValueOps(key).set("暂无此人");
            return "暂无此人";
        }
        redisTemplate.boundValueOps(key).set(userPO.toString());
        return userPO.toString();
    }

    /**
     * 更新用户
     * 一般情况下,修改后删除就行了
     * 但是并发比较高,而且对数据比较敏感,比如说金钱,需要双删
     *
     * @param userId   用户id
     * @param nickName 尼克名字
     * @return {@link String}
     * @throws InterruptedException 中断异常
     */
    @GetMapping("/update-user-by-nick")
    public String updateUser(Long userId,String nickName) throws InterruptedException {
        User userPO = new User();
        userPO.setId(userId);
        userPO.setNickName(nickName);
        //这一步删除是为了保证,当sql指令发送给mysql以后,查询出来的值还是旧的
        redisTemplate.delete(buildKey(userId.toString()));
        userMapper.updateById(userPO);
        //这一步是为保证在sql指定发送给mysql的时候,来了查询,如果说这个时候的查寻还是修改之前的,导致缓存不更新
        redisTemplate.delete(buildKey(userId.toString()));
        return "22";
    }


    /**
     * 缓存预热
     */
    @PostConstruct
    public void initCache(){
        log.info("=========进行缓存预热=======================");
        List<Long> id  = Arrays.asList(101110L,101112L,101116L,101118L);
        List<User> userPOS = userMapper.selectList(new LambdaQueryWrapper<User>().in(User::getId, id));
        userPOS.stream().forEach(item->{redisTemplate.boundValueOps(buildKey(item.getId().toString())).set(item.toString());});

    }

    private String buildKey(String Id){
        return "user:Id_"+Id;
    }
}

        我们看一下controller中的思路:

        ①.@GetMapping("/get-user-by-nick")
            public String getUserByNick(Long  id)

        这个方法首先根据给出的查询参数构造Redis的key,我们查询Redis或者存Redis数据通常会构造一个key,按照这个key来存储值。

        首先从Redis数据库中查询,是否存在该查询结果的数据,如果存有,就直接拿取返回即可,否则走下一步。

        然后从数据库中查询需要的数据,首先判断这个数据是否存在,如果存在就存入Redis该数据值,否则说明不存在,直接存储“无此数据”即可。

        这个函数的大体思路就是,有数据就拿,没数据就存一个下次方便拿。

        ②.@GetMapping("/update-user-by-nick")
            public String updateUser(Long userId,String nickName)

        这个函数就是用来更新数据库数据,并且做到数据库与缓存之间保持同步。主要是考虑并发情况下,保证缓存数据和数据库数据的最终一致性。更新前删除缓存中的数据,保证尽可能的让其他进程或线程感知到数据库变化,进而在缓存中没有命中,进数据库中查询数据,此时,数据可能已经被更新了,或者还没有更新。

        第二次删除就是为了保证数据更新以后,删除缓存中数据,让其他进程或线程在缓存中无命中,进而进入数据库读取最新的数据,保证了最终一致性。

        ③.@PostConstruct
            public void initCache()

        这个方法主要是做缓存预热,在查询数据之前,预先将访问频繁的数据存入缓存,进而提高缓存命中,提高查询效率。

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

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

相关文章

服装行业实现数字化转型,只需上线这个系统!

近年来&#xff0c;内卷、降本增效等词频频冲上热搜&#xff0c;随着经济的周期性下行&#xff0c;服装企业的竞争愈发激烈&#xff0c;小单快返时代的到来&#xff0c;加速了服装行业的重新洗牌。在这样的大环境下&#xff0c;服装企业数字化转型几乎成了取得确定性增长的唯一…

QT网络编程之获取本机网络信息

一.概述 查询一个主机的MAC地址或者IP地址是网络应用中常用到的功能&#xff0c;Qt提供了QHostInfo和QNetworkInterface 类可以用于此类信息的查询 1.QHostInfo 类&#xff08;显示和查找本地的信息&#xff09; 2.QNetworkInterface 类&#xff08;获得应用程序上所在主机的…

Sublime Text3 C/C++一键调试运行代码

minGW的系统环境配置&#xff1a; 使用的C/C编译器是minGW&#xff0c;点此进入官网链接&#xff0c;下载后需要在线安装&#xff0c;安装后需要将安装目录下的bin目录所在路径加入path环境变量。本菜鸡的电脑里安装了CodeBlocks&#xff0c;在CodeBlocks的安装目录下有MinGW&…

婴儿洗衣机十大排名名牌:十款超高价值婴儿洗衣机综合整理

我们都知道宝宝的皮肤是超级娇嫩的&#xff0c;宝宝的衣物也一样&#xff0c;宝宝的衣物大部分都是纯棉的。如果将宝宝的衣物和大人衣服一起扔进大型洗衣机混洗&#xff0c;更可能出现细菌交叉感染&#xff0c;对小宝宝来说百害而无一利&#xff0c;会让小宝宝肌肤过敏、红肿、…

json字符串的数据提取

json的数据提取 学习目标 掌握 json相关的方法(load loads dump dumps)了解 jsonpath的使用(提取 json中的数据) 2 复习什么是json JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式&#xff0c;它使得人们很容易的进行阅读和编写。同时也方便了机器进行解析和…

蓝牙耳机哪个品牌质量比较好?真实用户实测推荐!

​一款舒适的蓝牙耳机能让你长时间沉浸在音乐之中&#xff0c;享受无拘无束的听音体验。但对于许多朋友来说&#xff0c;选择合适的蓝牙耳机可能是个难题。别担心&#xff0c;我来为你推荐几款既舒适又音质出色的蓝牙耳机&#xff0c;让你的选择变得更加简单。 一、蓝牙耳机这样…

AI浸入社交领域,泛娱乐APP如何抓住新风口?

2023年是大模型技术蓬勃发展的一年&#xff0c;自ChatGPT以惊艳姿态亮相以来&#xff0c;同年年底多模态大模型技术在国内及全球范围内的全面爆发&#xff0c;即模型能够理解并生成包括文本、图像、视频、音频等多种类型的内容。例如&#xff0c;基于大模型的文本到图像生成工具…

【SpringBoot3.x教程04】SpringBoot如何自定义starter

前言&#xff1a;什么是Starter POMs Starter POMs是预配置的依赖集合&#xff0c;旨在提供一种快速的方式来引入和管理Spring及相关技术栈的依赖。每个Starter POM都是针对特定的Spring模块或技术场景设计的。使用Starter POM&#xff0c;开发者只需要添加一个依赖项&#xff…

mysql - 多表访问

多表访问 创建两个表 mysql> SELECT * FROM t1; ------------ | m1 | n1 | ------------ | 1 | a | | 2 | b | | 3 | c | ------------ 3 rows in set (0.00 sec)mysql> SELECT * FROM t2; ------------ | m2 | n2 | ------------ | 2 | …

2023年天府杯全国大学生数学建模竞赛A题震源属性识别模型构建与震级预测解题全过程文档及程序

2023年天府杯全国大学生数学建模竞赛 A题 震源属性识别模型构建与震级预测 原题再现&#xff1a; 地震是一种较为复杂的地壳运动现象&#xff0c;全世界每年发生的地震灾害事故不计其数。旨在减少地震灾害的地震预警预报技术需要在日常地震监测中有效识别出天然地震事件&…

PyTorch学习笔记之激活函数篇(三)

文章目录 3、ReLU3.1 公式3.2 对应的图像3.3 对应的图像的代码3.4 优点与不足3.5 torch.relu()函数 3、ReLU 3.1 公式 ReLU函数的公式&#xff1a; f ( x ) { x , x > 0 0 , x < 0 f(x) \begin{cases} x&,x>0 \\ 0&,x<0 \end{cases} f(x){x0​,x>…

鸿蒙4.0ArkUI快速入门(一)应用模型

ArkUI篇 应用模型Stage模型FA模型模型对比 应用模型 应用模型是HarmonyOS为开发者提供的应用程序所需能力的抽象提炼&#xff0c;它提供了应用程序必备的组件和运行机制。 HarmonyOS先后提供了两种应用模型&#xff1a; FA&#xff08;Feature Ability&#xff09;模型&…

HarmonyOS4.0—自定义渐变导航栏开发教程

前言 今天要分享的是一个自定义渐变导航栏&#xff0c;本项目基于鸿蒙4.0。 先看效果&#xff1a; 这种导航栏在开发中也比较常见&#xff0c;特点是导航栏背景色从透明到不透明的渐变&#xff0c;以及导航栏标题和按钮颜色的变化。 系统的导航栏无法满足要求&#xff0c;我们…

【Linux】项目部署CPU彪高如何定位

1.查看所有CPU占比 使用top指令获取彪高进程的PID 2.输出进程的信息 ps H -eo pid,tid,%cpu | grep 1313 3.查看线程的信息 jstack tid nid都是十六进制的 4.进制转换 将 tid的十进制转为十六进制 找到nid 可以定位到具体位置 5.关闭程序 ps -ef | grep java kill -9 jav…

LeetCode 面试经典150题 380.O(1)时间插入、删除和获取随机元素

题目&#xff1a; 实现RandomizedSet 类&#xff1a; RandomizedSet() 初始化 RandomizedSet 对象bool insert(int val) 当元素 val 不存在时&#xff0c;向集合中插入该项&#xff0c;并返回 true &#xff1b;否则&#xff0c;返回 false 。bool remove(int val) 当元素 va…

Lightroom Classic 2024 for mac 中文激活:强大的图像后期处理软件

对于追求极致画面效果的摄影师来说&#xff0c;Lightroom Classic 2024无疑是Mac平台上的一款必备软件。它凭借其强大的功能和出色的性能&#xff0c;赢得了众多摄影师的青睐。 软件下载&#xff1a;Lightroom Classic 2024 for mac 中文激活版下载 在Lightroom Classic 2024中…

软件测评中心:进行科技成果鉴定测试的注意事项和好处简析

软件产品科技成果鉴定是有效评价科技成果质量和水平的方法之一&#xff0c;也是鼓励科技成果通过市场竞争等方式得到有效的评价和认可&#xff0c;可以推动科技成果的进步和转化。 一、进行科技成果鉴定测试时的注意事项&#xff1a;   1、应由具备一定资质和能力的专业机构…

处理器方法的参数

处理器方法的参数&#xff1a; 处理器方法可以包含以下四类参数&#xff0c;这些参数会在系统调用时由系统自动赋值&#xff0c;即程序员可在方法内直接使用: HttpServletRequestHttpServletResponseHttpSession请求中所携带的请求参数 控制器方法&#xff1a; 前面三个参数怎…

【LeetCode: 173. 二叉搜索树迭代器 + dfs + 二叉搜索树】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

常用小知识点总结

1. pc可以跑通&#xff0c;但是安卓编译死循环&#xff0c;可能是函数声明了返回类型&#xff0c;但是没有真正返回 2. ubuntu下根据关键词杀死所有相关进程。ps -ef | grep code | grep -v grep | cut -c 10-16 | xargs kill -s 9 top和ps基本作用都是显示系统进程状况&…