使用Redis做缓存的小案例

news/2024/4/27 14:58:18

        如果不了解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: localhostport: 6379password: 123456datasource:type: com.zaxxer.hikari.HikariDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverusername: rootpassword: rooturl: 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 {@Resourceprivate UserMapper userMapper;@Resourceprivate 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";}/*** 缓存预热*/@PostConstructpublic 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.cpky.cn/p/10936.html

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

相关文章

基于python+vue智慧社区家政服务系统的设计与实现flask-django-nodejs

论文主要是对智慧社区家政服务系统进行了介绍&#xff0c;包括研究的现状&#xff0c;还有涉及的开发背景&#xff0c;然后还对系统的设计目标进行了论述&#xff0c;还有系统的需求&#xff0c;以及整个的设计方案&#xff0c;对系统的设计以及实现&#xff0c;也都论述的比较…

公司内部局域网怎么适用飞书?

随着数字化办公的普及&#xff0c;企业对于内部沟通和文件传输的需求日益增长。飞书作为一款集成了即时通讯、云文档、日程管理、视频会议等多种功能的智能协作平台&#xff0c;已经成为许多企业提高工作效率的首选工具。本文将详细介绍如何在公司内部局域网中应用飞书&#xf…

在 Linux/Ubuntu/Debian 上安装 SQL Server 2019

Microsoft 为 Linux 发行版&#xff08;包括 Ubuntu&#xff09;提供 SQL Server。 以下是有关如何执行此操作的基本指南&#xff1a; 注册 Microsoft Ubuntu 存储库并添加公共存储库 GPG 密钥&#xff1a; sudo wget -qO- https://packages.microsoft.com/keys/microsoft.as…

【GO全栈掌握入门】

GO语言全栈学习咯 ~ 1. GO 语言简介2.语言特性3.哪些公司使用GO语言&#xff1f;3. 安装GO开发环境4. 学习说明&#xff1a;5. GO结构篇5.1 工作空间5.2 导入包5.3 组织结构5.4 依赖管理 6. GO骨肉篇7.GO工具篇 1. GO 语言简介 起源于2007年&#xff0c;GO语言之年轻如你所见&…

【LabVIEW FPGA入门】使用FPGA实现串行同步接口(SSI)

SSI&#xff08;串行同步接口&#xff09;是连接绝对位置传感器和控制器的广泛应用的串行接口。SSI利用控制器发出一个时钟脉冲序列&#xff0c;初始化传感器的门限输出。 传感器不断更新位置数据&#xff0c;并传送到移位寄存器中。在每一个时钟脉冲序列之间&#xff…

Blender小:图片转mesh,并展UV

其实就这么一个插件&#xff1a;Image2Mesh https://github.com/kedepot/i2m-std blender加载完图片之后点这个就行 到了这一步还不行如果直接放到unity中使用的话&#xff0c;添加材质和贴图之后每一个小块都是独立的区域来展示贴图&#xff0c;所以需要展UV