📢 大家好,我是 【战神刘玉栋】,有10多年的研发经验,致力于前后端技术栈的知识沉淀和传播。 💗
🌻 CSDN入驻不久,希望大家多多支持,后续会继续提升文章质量,绝不滥竽充数,欢迎多多交流。👍
写在前面的话
本系列博文已连载到第11篇,上一篇文章介绍了整合MyBatis-Plus
,简化数据交互。
本篇文章将在此基础上,整合Redis
缓存机制,进一步提升项目能力。
关联文章:
《程序猿入职必会(1) · 搭建拥有数据交互的 SpringBoot 》
《程序猿入职必会(2) · 搭建具备前端展示效果的 Vue》
《程序猿入职必会(3) · SpringBoot 各层功能完善 》
《程序猿入职必会(4) · Vue 完成 CURD 案例 》
《程序猿入职必会(5) · CURD 页面细节规范 》
《程序猿入职必会(6) · 返回结果统一封装》
《程序猿入职必会(7) · 前端请求工具封装》
《程序猿入职必会(8) · 整合 Knife4j 接口文档》
《程序猿入职必会(9) · 用代码生成器快速开发》
《程序猿入职必会(10) · 整合 Redis(基础篇)》
Redis 缓存机制
技术简介
Tips:Redis 想必大家都不陌生了,长篇大论的介绍理论意义不大,这边仅简单说明一二。
Redis 是一个开源的高性能键值存储数据库,广泛用于缓存、消息队列、实时数据分析等场景。它支持多种数据结构,如字符串、哈希、列表、集合和有序集合等,并提供丰富的操作命令。
Redis 应用场景:数据缓存、分布式锁、消息队列、排行榜等。
整合步骤
引言:基于之前的 SpringBoot3 后端项目,并且已经完成了各层的CURD操作,之前内容可以参考 《程序猿入职必会(1) · 搭建拥有数据交互的 SpringBoot 》和《程序猿入职必会(3) · SpringBoot 各层功能完善 》。
**Step1、引入依赖 **
<!-- 整合 Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
这里直接引入spring-boot-starter-data-redis
依赖即可,版本号SpringBoot
内置维护了。
这里好在 SpringBoot2 和 SpringBoot3 关于 Redis 的依赖没有变化,少踩一些坑。
Step2、修改配置
在 application.yml 中配置 Redis 连接信息。
这里注意一下,在 SB3.0 版本之前,属性是 spring.redis,但是在 SB3.0 版本之后,变为 spring.data.redis。
spring:
data:
redis:
database: 5
host: xx.xx.xx.xx
port: xx
password: xx
timeout: 60s
lettuce:
pool:
# 连接池最大连接数
max-active: 200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
# 连接池中的最大空闲连接
max-idle: 10
# 连接池中的最小空闲连接
min-idle: 0
Step3、创建Redis配置类(可选)
这边主要是设置RedisTemplate的序列化规则,让缓存值可以更直观一些,这步跳过也可以。
关于 Redis 的几种序列化策略,可以参考之前的博文:《知识点扫盲 · Redis 序列化器》
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
GenericFastJsonRedisSerializer serializer = new GenericFastJsonRedisSerializer();
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8);
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(StringRedisSerializer.UTF_8);
redisTemplate.setHashValueSerializer(serializer);
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
Step4、编写 RedisService(可选)
这步也是可选的,可以直接在具体逻辑里面注入 RedisTemplate,但通常会封装一下相关逻辑。
下方也是简单示例,实战中不会如此简单。
@Service
public class RedisService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
/**
* 设置数据
*/
public void setValue(String key, Object value) {
redisTemplate.opsForValue().set(key, value);
}
/**
* 获取数据
*/
public Object getValue(String key) {
return redisTemplate.opsForValue().get(key);
}
/**
* 设置带过期时间的数据
*/
public void setValueWithExpiration(String key, Object value, long timeout, TimeUnit unit) {
redisTemplate.opsForValue().set(key, value, timeout, unit);
}
/**
* 删除数据
*/
public void deleteValue(String key) {
redisTemplate.delete(key);
}
}
Step5、修改业务并测试
直接在之前根据ID获取教师详情
的接口上进行整改,加上缓存逻辑。
即先从缓存获取,缓存不存在则从数据库获取,数据库获取到值再设置回缓存,然后返回。
这也是缓存利用的常规套路。
private static final String CACHE_TEA_PREV = "TEA:";
@Autowired
private RedisService redisService;
@Autowired
private ZyTeacherInfoService zyTeacherInfoService;
@GetMapping("/{id}")
public ZyTeacherInfo get(@Parameter(description = "教师ID", required = true, in = ParameterIn.PATH) @PathVariable String id) {
String key = CACHE_TEA_PREV + id;
ZyTeacherInfo zyTeacherInfo = (ZyTeacherInfo) redisService.getValue(key);
if (zyTeacherInfo == null) {
zyTeacherInfo = zyTeacherInfoService.getById(id);
if (zyTeacherInfo != null) {
redisService.setValue(CACHE_TEA_PREV + id, zyTeacherInfo);
}
}
return zyTeacherInfo;
}
启动程序,用 Knife4j 测试一下效果,一些正常。
现在先不细究过程,可以观察一下缓存工具,确实数据进来了,搞定收工。
过程复盘
上述步骤已经实现了 Redis 整合的目的,你可以开始用 Redis 了。
Redis 最常用的是数据缓存,可以减少访问数据库的次数,降低接口耗时。
但它还有诸多场景,本篇文章篇幅有限,先不一一展开,后续再见。
那本篇文章整合一下就结束了吗?那还没有。
接下来对结合上述整合步骤,分享一些实战和拓展知识。
用法扩展
【缓存使用封装】
先看看,上述缓存的改造版本,先缓存、再数据库
,这已经算常规模式了,有啥问题?
public ZyTeacherInfo get(@Parameter(description = "教师ID", required = true, in = ParameterIn.PATH) @PathVariable String id) {
String key = CACHE_TEA_PREV + id;
ZyTeacherInfo zyTeacherInfo = (ZyTeacherInfo) redisService.getValue(key);
if (zyTeacherInfo == null) {
zyTeacherInfo = zyTeacherInfoService.getById(id);
if (zyTeacherInfo != null) {
redisService.setValue(CACHE_TEA_PREV + id, zyTeacherInfo);
}
}
return zyTeacherInfo;
}
试想一下,如果还有其他业务逻辑,那这种写法又要来一次,或者说其他业务 Service 也要获取教师信息,那这部分逻辑也还要重复编码一次。
有重复的代码出现,所以我们才需要封装。
思路分享一下:将这部分代码抽到缓存Service中,利用泛型传递设置相关业务类。
这样可以实现大大的复用,代码里优雅不少。
Tips:逻辑较简单,暂不贴实现了,后续有需要再补充。
【注解方式】
除了前面的封装公用 Service,还是注入 RedisTemplate,都算是编程式使用(也可以封装RedisUtils)。
还有一种注解式用法,那就是 Spring-Redis 的 @Cacheable 注解方式。
这就有点类似 Spring 事务的,编程式事务和声明式事务。
@Cacheable 注解是 Spring Framework 中用于缓存的一个重要注解。它可以帮助我们在方法调用时缓存结果,从而提高应用程序的性能。下面是关于如何使用 @Cacheable 注解进行增、删、改、查的示例。
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Cacheable("users")
public User getUserById(Long id) {
return new User(id, "User" + id);
}
@CachePut(value = "users", key = "#user.id")
public User createUser(User user) {
// 保存用户到数据库
return user;
}
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
// 更新用户到数据库
return user;
}
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
// 从数据库中删除用户
}
}
【注意事项】
1、缓存使用的时候,要先定义合规的Key,不能乱起八糟;
2、查询接口通常是按查缓存-查数据库-设置缓存
这个模式,既然有并设置缓存,那也要考虑删除缓存的场景,通常CURD的后台管理系统,维护业务字典表的时候,增删改接口加上删除缓存的注解。
3、存 Redis 的时候要考虑一下 Key的要有效,不同业务设置不一样的。
总结陈词
此篇文章介绍了Redis
的基础整合过程,效果还不错,仅供学习参考。
Redis 值得我们学习的功能和场景还很多,这篇仅为基础篇,后续会专栏展开。
💗 后续会逐步分享企业实际开发中的实战经验,有需要交流的可以联系博主。