1. 什么是Redis缓存,为什么要用Redis缓存
Redis 是一种内存高速cache,如果使用redis缓存,那经常被访问的内容会被缓存在内存中,需要使用的时候直接从内存调取,不知道比硬盘调取快了多少倍,并且支持复杂的数据结构,应用于许多高并发的场景中。
Redis 是一个高性能的 key-value 数据库。
2. Redis使用之前先安装
- 先从官网下载,然后解压,找一个目录放好
官网:https://github.com/tporadowski/redis/releases - 管理员身份运行命令行
cd C:\Redis-x64-5.0.10
redis-server.exe redis.windows.conf
出现上图表示Redis安装成功
- 设置环境变量
- 开启Redis服务
cd C:\Redis-x64-5.0.10
redis-server --service-install redis.windows-service.conf --loglevel verbose
检查服务是否存在:
启动Redis服务后再登录看是否成功
redis-server --service-start
cd C:\Users\QYY
redis-cli
- 命令行测试
这里是键值对的形式,“name”是key,“qyy”是value
C:\Users\QYY>redis-cli
127.0.0.1:6379> set name qyy
OK
127.0.0.1:6379> get name
"qyy"
127.0.0.1:6379>
补:可以用flushdb
命令删除所有key
更多操作参考:https://www.jianshu.com/p/d06b94c33bbb
- 设置密码
找到redis的配置文件—redis.windows.conf文件,然后修改里面的requirepass,这个本来是注释起来了的,将注释去掉,并将后面对应的字段设置成自己想要的密码,保存退出。重启redis服务,即可。
3. SpringBoot如何使用
- pom.xml引入jar包
<!--redis依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
- 项目启动类增加注解@EnableCaching,开启缓存功能
- application.properties中配置Redis连接信息
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000
- 新建Redis缓存配置类RedisConfig
package com.jit.springbootshop.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Duration;
/**
* @program: springbootshop
* @description: Redis缓存配置类
* @author: qyy
* @create: 2021-07-20 15:32
**/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
@Value("${spring.redis.timeout}")
private int timeout;
//自定义缓存key生成策略
// @Bean
// public KeyGenerator keyGenerator() {
// return new KeyGenerator(){
// @Override
// public Object generate(Object target, java.lang.reflect.Method method, Object... params) {
// StringBuffer sb = new StringBuffer();
// sb.append(target.getClass().getName());
// sb.append(method.getName());
// for(Object obj:params){
// sb.append(obj.toString());
// }
// return sb.toString();
// }
// };
// }
//缓存管理器
//spring boot的版本是2x以下的用这个
// @Bean
// public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
// RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
// //设置缓存过期时间
// cacheManager.setDefaultExpiration(10000);
// return cacheManager;
// }
// 管理缓存
//spring boot的版本是2x以上的用这个
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl( Duration.ofHours(1)); // 设置缓存有效期一小时
return RedisCacheManager
.builder( RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
.cacheDefaults(RedisCacheConfiguration.defaultCacheConfig(Thread.currentThread().getContextClassLoader())).build();
}
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){
StringRedisTemplate template = new StringRedisTemplate(factory);
setSerializer(template);//设置序列化工具
template.afterPropertiesSet();
return template;
}
private void setSerializer(StringRedisTemplate template){
@SuppressWarnings({ "rawtypes", "unchecked" })
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
template.setValueSerializer(jackson2JsonRedisSerializer);
}
}
参看链接:https://www.jianshu.com/p/5b98a61afd28
- 修改实体类(实现Serializable接口)
public class User implements Serializable
- mapper类加注解
@Mapper
@CacheConfig(cacheNames = "users")
public interface RedisTestMapper {
/**
* @param normalUserId
* @return com.jit.springbootshop.pojo.User
* @author qyy
* @describe: 根据用户id查询用户实体
* @date 2021/7/21 15:53
*/
@Cacheable
public User findUserById(int normalUserId);
}
@CacheConfig
:主要用于配置该类中会用到的一些共用的缓存配置。@CacheConfig(cacheNames = “users”):配置了该数据访问对象中返回的内容将存储于名为users的缓存对象中,我们也可以不使用该注解,直接通过@Cacheable自己配置缓存集的名字来定义。@Cacheable
:配置了findUserById函数的返回值将被加入缓存。同时在查询时,会先从缓存中获取,若不存在才再发起对数据库的访问。该注解主要有下面几个参数:
- value、cacheNames:两个等同的参数(cacheNames为Spring 4新增,作为value的别名),用于指定缓存存储的集合名。由于Spring 4中新增了@CacheConfig,因此在Spring 3中原本必须有的value属性,也成为非必需项了
- key:缓存对象存储在Map集合中的key值,非必需,缺省按照函数的所有参数组合作为key值,若自己配置需使用SpEL表达式,比如:@Cacheable(key = “#p0”):使用函数第一个参数作为缓存的key值,更多关于SpEL表达式的详细内容可参考官方文档
- condition:缓存对象的条件,非必需,也需使用SpEL表达式,只有满足表达式条件的内容才会被缓存,比如:@Cacheable(key = “#p0”, condition = “#p0.length() < 3”),表示只有当第一个参数的长度小于3的时候才会被缓存,若做此配置上面的AAA用户就不会被缓存,读者可自行实验尝试。
- unless:另外一个缓存条件参数,非必需,需使用SpEL表达式。它不同于condition参数的地方在于它的判断时机,该条件是在函数被调用之后才做判断的,所以它可以通过对result进行判断。
- keyGenerator:用于指定key生成器,非必需。若需要指定一个自定义的key生成器,我们需要去实现org.springframework.cache.interceptor.KeyGenerator接口,并使用该参数来指定。需要注意的是:该参数与key是互斥的
- cacheManager:用于指定使用哪个缓存管理器,非必需。只有当有多个时才需要使用
- cacheResolver:用于指定使用那个缓存解析器,非必需。需通过org.springframework.cache.interceptor.CacheResolver接口来实现自己的缓存解析器,并用该参数指定。
除了这里用到的两个注解之外,还有下面几个核心注解:
-
@CachePut
:配置于函数上,能够根据参数定义条件来进行缓存,它与@Cacheable不同的是,它每次都会真是调用函数,所以主要用于数据新增和修改操作上。它的参数与@Cacheable类似,具体功能可参考上面对@Cacheable参数的解析 -
@CacheEvict
:配置于函数上,通常用在删除方法上,用来从缓存中移除相应数据。除了同@Cacheable一样的参数之外,它还有下面两个参数:
allEntries:非必需,默认为false。当为true时,会移除所有数据
beforeInvocation:非必需,默认为false,会在调用方法之后移除数据。当为true时,会在调用方法之前移除数据。
参考链接:
- 测试类测试
@Autowired
RedisTestMapper redisTestMapper;
@Test
public void redisMapperTest(){
User user=redisTestMapper.findUserById(2);
System.out.println("1:"+user);
User user1=redisTestMapper.findUserById(2);
System.out.println("2:"+user1);
}
第一次执行时,从数据库读取;第二次执行时从redis读取。所以读取数据库的操作提示只出现了一次。
- 注解的方式管理缓存能够实现一些简单的操作,如果情况比较复杂的话,还是需要手动设置。
参考链接:StringRedisTemplate常用操作 比如关于增删改图书章节的缓存:(serviceImpl类内容)
查看图书章节内容时:
@Override
public BookChapter searchBookChapterById(int id) {
//手动增加缓存
redisTemplate.setKeySerializer(new StringRedisSerializer());
ValueOperations ops = redisTemplate.opsForValue();
BookChapter bookChapter = (BookChapter) ops.get("bookChapter_" + id);
if (bookChapter == null) {
bookChapter = adminBookMapper.searchBookChapterById(id);
ops.set("bookChapter_" + id, bookChapter);
}
return bookChapter;
}
删除图书章节时:
@Override
public int deleteChapterById(int chapterId) {
redisTemplate.setKeySerializer(new StringRedisSerializer());
//根据章节id查找到该章节所属于的图书id,和图书章节序号
BookChapter bookChapter = adminBookMapper.searchBookChapterById(chapterId);
int bookId = bookChapter.getBookId();
int chapterNumber = bookChapter.getChapterNumber();
int changeNum = -1;
//根据图书id,改变图书章节序号(-1)
int lines = adminBookMapper.updateChapterNumberById(changeNum, bookId, chapterNumber);
//缓存更新
Set<String> keys = redisTemplate.keys("bookChapter_*");
for (String key : keys) {
System.out.println("key="+key);
if (redisTemplate.hasKey(key)) {
ValueOperations ops = redisTemplate.opsForValue();
BookChapter temp = (BookChapter) ops.get(key);
if (temp.getBookId() == bookId && temp.getChapterNumber() > chapterNumber) {
int num = temp.getChapterNumber();
temp.setChapterNumber(num-1);
ops.set(key, temp);
}
}
}
//删除该章节
lines = adminBookMapper.deleteBookChapterById(chapterId);
if (lines > 0) {
System.out.println("删除成功!");
//手动修改缓存
if (redisTemplate.hasKey("bookChapter_" + chapterId)) {
redisTemplate.delete("bookChapter_" + chapterId);
}
}
return lines;
}
修改章节内容时:
@Override
public int updateChapterInfoById(BookChapter bookChapter) {
//先检查章节名是否重复
BookChapter bookChapter1 = adminBookMapper.searchBookChapterByChapterName(bookChapter.getChapterName(), bookChapter.getBookId());
if (bookChapter1 != null && bookChapter1.getId() != bookChapter.getId()) {
System.out.println("章节名重复!");
return -1;
}
//在章节名没有重复时修改
int lines = adminBookMapper.updateChapterInfoById(bookChapter);
if (lines > 0) {
System.out.println("修改成功!");
//手动修改缓存
redisTemplate.setKeySerializer(new StringRedisSerializer());
ValueOperations ops = redisTemplate.opsForValue();
if (redisTemplate.hasKey("bookChapter_" + bookChapter.getId())) {
ops.set("bookChapter_" + bookChapter.getId(), bookChapter);
}
}
return lines;
}
中间插入章节时:
@Override
public int insertToUpChapter(BookChapter bookChapter) {
redisTemplate.setKeySerializer(new StringRedisSerializer());
//先检查章节名是否重复
BookChapter bookChapter1 = adminBookMapper.searchBookChapterByChapterName(bookChapter.getChapterName(), bookChapter.getBookId());
if (bookChapter1 != null) {
System.out.println("章节名重复!");
return -1;
}
//根据图书id,改变图书章节序号(+1)
int changeNum = 1;
int bookId = bookChapter.getBookId();
int chapterNumber = bookChapter.getChapterNumber() - 1;
int lines = adminBookMapper.updateChapterNumberById(changeNum, bookId, chapterNumber);
//缓存更新
Set<String> keys = redisTemplate.keys("bookChapter_*");
for (String key : keys) {
System.out.println("key="+key);
if (redisTemplate.hasKey(key)) {
ValueOperations ops = redisTemplate.opsForValue();
BookChapter temp = (BookChapter) ops.get(key);
if (temp.getBookId() == bookId && temp.getChapterNumber() > chapterNumber) {
int num = temp.getChapterNumber();
temp.setChapterNumber(num+1);
ops.set(key, temp);
}
}
}
lines = adminBookMapper.insertBookChapter(bookChapter);
return bookChapter.getId();
}
还可以设置缓存过期时间:
参考链接: