当前位置:网站首页>08_SpingBoot 集成Redis
08_SpingBoot 集成Redis
2022-06-26 04:21:00 【书启秋枫】
- 并发:多个线程同时操作某一个(些)资源,带来数据的不确定性、不稳定性、不安全性
- 同步:在某一个时刻,只有一个线程访问资源 解决并发问题,性能低下(程序不能让性能过于低下)
- 锁:唯一 对象监视器
- 缓存穿(刺)透:缓存有(没有)数据,访问了数据库
- 缓存雪崩:在某一个时刻,缓存中大部分 同时失效,而此时恰好有很多线程并发访问,导致数据库无法处理这么多访问而瘫痪
- 评估 缓存数据分时失效 降格处理
- 多线程:不是计算一个数据 而是描述的是一种状态
一、Spring Boot 集成Redis单机模式
1. 案例思路
完善根据学生id查询学生的功能,先从redis缓存中查找,如果找不到,再从数据库中查找,然后放到redis缓存中
2. 实现步骤
首先通过MyBatis逆向工程生成实体bean和数据持久层
A. 在pom.xml文件中添加redis依赖和其它配置
<!-- 加载spring boot redis包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
B. 在Spring Boot核心配置文件application.properties中配置redis连接信息
server.port=9005
server.servlet.context-path=/005-springboot-redis
#数据库的配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
#redis配置
spring.redis.host=192.168.60.130
spring.redis.port=6379
spring.redis.password=123456
C. 启动redis服务
[[email protected] /]# cd /usr/local/redis-4.0.6/src/
[[email protected] src]# ./redis-server ../redis.conf &
D. RedisController 类
@RestController
public class RedisController {
@Autowired
private StudentService studentService;
/*
* http://localhost:9005/005-springboot-redis/springboot/allStudentCount
* */
@GetMapping(value = "/springboot/allStudentCount")
public Object allStudentCount(HttpServletRequest request) {
Long allStudentCount = studentService.queryAllStudentCount();
return "学生总人数:" + allStudentCount;
}
}
E. StudentService 接口
public interface StudentService {
Long queryAllStudentCount();
}
F. 在StudentServiceImpl中注入RedisTemplate并修改根据id获取学生的方法
配置了上面的步骤,Spring Boot将自动配置RedisTemplate,在需要操作redis的类中注入redisTemplate即可。
注意:Spring Boot帮我们注入RedisTemplate类,泛型里面只能写 <String, String>、<Object, Object>或者什么都不写
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
StudentMapper studentMapper;
@Autowired
RedisTemplate redisTemplate;
@Override
public Long queryAllStudentCount() {
//从redis缓存中获取总人数
Long allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");
//判断是否为空
if (allStudentCount == null) {
//从数据库查询
allStudentCount = studentMapper.selectAllStudentCount();
//将值再存放到redis缓存中
redisTemplate.opsForValue().set("allStudentCount", allStudentCount, 20, TimeUnit.SECONDS);
}
return allStudentCount;
}
}
G. Student 实体类
public class Student {
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
H. StudentMapper 接口
@Repository
public interface StudentMapper {
Long selectAllStudentCount();
}
I. StudentMapper.xml 数据库配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.suke.springboot.mapper.StudentMapper">
<resultMap id="BaseResultMap" type="com.suke.springboot.model.Student">
<id column="id" jdbcType="INTEGER" property="id"/>
<result column="name" jdbcType="VARCHAR" property="name"/>
<result column="age" jdbcType="INTEGER" property="age"/>
</resultMap>
<sql id="Base_Column_List">
id
, name, age
</sql>
<select id="selectAllStudentCount" resultType="long">
select count(123)
from tb_student
</select>
</mapper>
J. 启动类Application
在SpringBoot启动类上添加扫描数据持久层的注解并指定扫描包
@SpringBootApplication
@MapperScan("com.suke.springboot.mapper")//扫描数据持久层
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
K. 启动SpringBoot应用,访问测试
L. 打开Redis Desktop Mananger查看Redis中的情况
设置20秒后,缓存消失
M. 在StudentServiceImpl添加一行代码解决乱码
关系型数据库:数据安全的
redis:二进制数据安全的 数据直接序列化 存储到redis中,获取的时候,直接把序列化数据拿出来,反序列化成对象 不会产生乱码的
//设置key键,序列化,与功能无关
redisTemplate.setKeySerializer(new StringRedisSerializer());
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
StudentMapper studentMapper;
@Autowired
RedisTemplate redisTemplate;
@Override
public Long queryAllStudentCount() {
//设置key键,序列化,与功能无关
redisTemplate.setKeySerializer(new StringRedisSerializer());
//从redis缓存中获取总人数
Long allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");
//判断是否为空
if (allStudentCount == null) {
//从数据库查询
allStudentCount = studentMapper.selectAllStudentCount();
//将值再存放到redis缓存中
redisTemplate.opsForValue().set("allStudentCount", allStudentCount, 20, TimeUnit.SECONDS);
}
return allStudentCount;
}
}
二、缓存穿透现象
1. RedisController 类
创建线程池模拟千人并发
@RestController
public class RedisController {
@Autowired
private StudentService studentService;
/*
* http://localhost:9005/005-springboot-redis/springboot/allStudentCount
* */
@GetMapping(value = "/springboot/allStudentCount")
public Object allStudentCount(HttpServletRequest request) {
/*Long allStudentCount = studentService.queryAllStudentCount();
return "学生总人数:" + allStudentCount;*/
//创建线程池
ExecutorService executorService = Executors.newFixedThreadPool(16);
//模拟千人并发
for (int i = 0; i < 1000; i++) {
executorService.submit(new Runnable() {
@Override
public void run() {
studentService.queryAllStudentCount();
}
});
}
return "学生总人数:" + 7;
}
}
2. StudentServiceImpl 类
在控制台输出”查询数据库“和“缓存命中”显示查询结果情况
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
StudentMapper studentMapper;
@Autowired
RedisTemplate redisTemplate;
@Override
public Long queryAllStudentCount() {
//设置key键,序列化,与功能无关
redisTemplate.setKeySerializer(new StringRedisSerializer());
//从redis缓存中获取总人数
Long allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");
//判断是否为空
if (allStudentCount == null) {
System.out.println("----查询数据库-----");
//从数据库查询
allStudentCount = studentMapper.selectAllStudentCount();
//将值再存放到redis缓存中
redisTemplate.opsForValue().set("allStudentCount", allStudentCount, 20, TimeUnit.SECONDS);
} else {
System.out.println("-----缓存命中-------");
}
return allStudentCount;
}
}
3. 启动应用程序,浏览器访问测试
4. 造成的问题
Tip:多个线程都去查询数据库,这种现象就叫做缓存穿透,如果并发比较大,对数据库的压力过大,有可能造成数据库宕机。
三、缓存穿透现象-解决方法
1. 修改StudentServiceImpl中的代码
@Service
public class StudentServiceImpl implements StudentService {
@Autowired
StudentMapper studentMapper;
@Autowired
RedisTemplate redisTemplate;
@Override
public Long queryAllStudentCount() {
//设置key键,序列化,与功能无关
redisTemplate.setKeySerializer(new StringRedisSerializer());
//从redis缓存中获取总人数
Long allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");
//判断学生总人数是否为空
if (allStudentCount == null) {
//设置同步代码块
synchronized (this) {
//再次从redis缓存中获取学生总人数
allStudentCount = (Long) redisTemplate.opsForValue().get("allStudentCount");
//双重检测判断缓存中是否有数据
if (allStudentCount == null) {
System.out.println("----查询数据库-----");
//从数据库查询
allStudentCount = studentMapper.selectAllStudentCount();
//将值再存放到redis缓存中
redisTemplate.opsForValue().set("allStudentCount", allStudentCount, 20, TimeUnit.SECONDS);
} else {
System.out.println("-----缓存命中-------");
}
}
} else {
System.out.println("-----缓存命中-------");
}
return allStudentCount;
}
}
2. 启动应用程序,浏览器访问测试,查看控制台输出
只有第一个线程查询数据库,其它线程查询Redis缓存,这样的解决的小问题就是第一批进来的用户会有一个等待,但是这样的影响可以忽略
3. springboot集成Redis阻止缓存穿透,为什么要做双层验证
- 防止线程获取到cpu执行权限的时候,其他线程已经将数据放到Redis中了,所以再次判断
- 不能将synchronized范围扩大,因为如果Redis缓存中如果有数据,线程不应该同步,否则影响效率
- 解决缓存穿透:2次查询缓存,再一次判断
边栏推荐
- ipvs之ipvs0网卡
- 微软禁止俄用户下载安装Win10/11
- 线程同步之互斥量(互斥锁)
- College C language final exam · multiple choice questions · summary notes of mistakes and difficulties
- mysql自帶的性能測試工具mysqlslap執行壓力測試
- What are the advantages and risks of paper gold investment
- Oracle 数据泵导表
- Unity mobile game performance optimization spectrum CPU time-consuming optimization divided by engine modules
- Introduction Guide to the flutterplugin plug-in in the actual combat of flutter
- SQL related knowledge - DQL
猜你喜欢
In 2022, what professional competitions can college students majoring in automation, electrical engineering and automation participate in?
Construction of art NFT trading platform | NFT mall
35岁程序员炒Luna 千万资产3天归零,网友:和赌博一样
A brain map to summarize the needs analysis (a supplement to the actual situation at work)
力扣 515. 在每个树行中找最大值
OSS CDN alicloud configuration method
Nailing open platform - applet development practice (nailing applet server side)
Threejs专用天空盒素材,五种天空盒素材免费下载
Conditional variables for thread synchronization
Tp6 multi table Association (table a is associated with table B, table B is associated with table C, and table d)
随机推荐
六、项目实战---识别猫和狗
Read / write lock for thread synchronization
mysql自带的性能测试工具mysqlslap执行压力测试
[Nuggets' operation routine disclosure] the routine of being truly Nuggets
Detailed explanation of widget construction process of fluent
Composer version rollback version switching
Redis cache data consistency solution analysis
Getting started with flask
【QT】对话框dialog
Modify the number of Oracle connections
Implementation of seven classes of BlockingQueue interface
Yolov5 improvements: replace the backbone
Construction of art NFT trading platform | NFT mall
asp. Net web page, ASP connects to the database, and uses asp:panel and asp:dropdownlist controls
Knowledge of functions
線程同步之讀寫鎖
Video label forbids downloading. The test is valid. Hide button. The test is valid at three points
[从零开始学习FPGA编程-45]:视野篇 - 集成电路助力数字化时代高质量发展-2-市场预测
钉钉开放平台-小程序开发实战(钉钉小程序服务器端)
What if the serial port fails to open when the SCM uses stc-isp to download software?