分布式缓存redis
·
分布式缓存redis
1.分布式缓存概述
1.1.什么是分布式缓存
就是用户查询数据的时候,我们将数据从数据库中同步一份到内存中,客户端直接从内存中查询数据,减少了和数据库的交互次数,提高查询性能(内存读写很快),减轻数据库的压力,用户的体验感也会好

1.2.那些数据适合缓存
-
经常查询的热点数据(热点数据,就是访问查询多的数据)
-
不经常变的数据(数据变化会导致缓存中的数据跟着变,如果比较频繁,性能开销比较大)
1.3.缓存执行流程
-
每次查询,先看缓存是否有数据 , 如果有:直接返回
-
如果缓存没有数据,去数据库查询数据
-
把数据同步一份到缓存
-
返回数据
【注意】数据库数据被修改,缓存要清空,或者重置

1.3.为什么要分布式缓存
-
在集群环境中,每个应用都有一个本地缓存,当缓存发生修改会造成缓存不同步问题
-
本地缓存本身要占用应用的内存空间

1.4.分布式缓存方案
使用Redis作为共享缓存 ,解决缓存不同步问题,Redis是独立的服务,缓存不用占用应用本身的内存空间

2.分布式缓存实战
比较流行的分布式缓存方案有:redis,mongdb等。我们选择Redis
2.1.集成Redis
SpringBoot提供了整合Redis的方案,我们使用spring-boot-starter-data-redis包就可以很方便的操作Redis了
1.导入依赖
<!--整合Redis , 底层可以用jedis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
2.配置Redis
spring:
redis:
database: 0
host: 127.0.0.1
port: 6379
password: 123456
jedis:
pool:
max-wait: 2000ms
min-idle: 2
max-idle: 8
3.Redis序列化配置
我们通常以JSON格式将数据存储到Redis中,这种格式是所有编程语言通用的,所以我们可以把Redis的序列化方式配置为JSON ,这样的话我们就可以不用自己去转JSON了
//缓存的配置
@Configuration
public class RedisConfig {
@Resource
private RedisConnectionFactory factory;
//使用JSON进行序列化
@Bean
public RedisTemplate<Object, Object> redisTemplate() {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(factory);
//JSON格式序列化
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer();
//key的序列化
redisTemplate.setKeySerializer(genericJackson2JsonRedisSerializer);
//value的序列化
redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer);
//hash结构key的虚拟化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//hash结构value的虚拟化
redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer);
return redisTemplate;
}
}
2.2.Redis测试
@Autowired
private RedisTemplate<Object, Object> redisTemplate;
...省略...
redisTemplate.opsForValue().set("key", "value");
项目测试:
package com.hy.ymcc.service.impl;
import com.hy.ymcc.domain.CourseType;
import com.hy.ymcc.mapper.CourseTypeMapper;
import com.hy.ymcc.service.ICourseTypeService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.hy.ymcc.vo.CourseCategoryVO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
/**
* <p>
* 课程目录 服务实现类
* </p>
*
* @author whale
* @since 2025-10-23
*/
@Service
public class CourseTypeServiceImpl extends ServiceImpl<CourseTypeMapper, CourseType> implements ICourseTypeService {
@Autowired
private CourseTypeMapper courseTypeMapper;
@Autowired
private RedisTemplate<Object,Object> redisTemplate;
@Override
public List<CourseCategoryVO> getCourseTypeTree() {
String key = "courseTypeTree";
//先看缓存中有没有
Object o = redisTemplate.opsForValue().get(key);
if(o!=null){
//有直接返回
return (List<CourseCategoryVO>)o;
}
// 1. 查所有分类
List<CourseType> allCategories = courseTypeMapper.selectAll();
// 2. 按“父ID(pid)”分组,方便快速找子节点
Map<Long, List<CourseType>> pidMap = allCategories.stream()
.collect(Collectors.groupingBy(CourseType::getPid));
// 3. 找所有“一级分类”(pid=0的节点),并递归找子节点
List<CourseCategoryVO> rootNodes = new ArrayList<>();
for (CourseType category : allCategories) {
if (category.getPid() == 0) { // 一级分类的pid是0
CourseCategoryVO vo = convertToVO(category);
rootNodes.add(vo);
findChildren(vo, pidMap); // 递归找子节点
}
}
//缓存中没有 从数据中拿到的数据 添加到缓存里
redisTemplate.opsForValue().set(key,rootNodes,5, TimeUnit.MINUTES);
return rootNodes;
}
// 递归找子节点
private void findChildren(CourseCategoryVO parentVO, Map<Long, List<CourseType>> pidMap) {
List<CourseType> children = pidMap.get(parentVO.getId()); // 找“父ID等于当前节点ID”的子节点
if (children != null && !children.isEmpty()) {
for (CourseType child : children) {
CourseCategoryVO childVO = convertToVO(child);
parentVO.getChildren().add(childVO); // 把子节点加入当前节点的children列表
findChildren(childVO, pidMap); // 继续找孙子节点
}
}
}
// 把数据库实体转成前端VO
private CourseCategoryVO convertToVO(CourseType category) {
CourseCategoryVO vo = new CourseCategoryVO();
vo.setId(category.getId());
vo.setName(category.getName());
return vo;
}
}

更多推荐



所有评论(0)