之前用SpringBoot+MyBatisPlus+SpringMVC整合搭建了一个基础web开发框架,使用这三个框架搭建出来项目结构非常的清爽,没有过多的配置文件,各个模块之间有清晰的联系,非常适合敏捷开发。
最近学习了Redis这个基于内存的,Key-Value数据形式的高性能数据库,感觉学习了入门之后很简单,没有体会到它具体能干嘛,我就想着使用Redis这个数据库来整合之前搭建的框架,利用Spring中的缓存机制,将查询的信息缓存到Redis中。
安装Redis
Window 下安装 下载地址:https://github.com/MSOpenTech/redis/releases。 Redis 支持32 位和64 位。这个需要根据你系统平台的实际情况选择,这里我们下载 Redis-x64-xxx.zip压缩包到 C盘的tools目录中,解压后,将文件夹重新命名为 redis。
下载Redis版本
打开一个 cmd 窗口 使用cd命令切换目录到 C:\tools\redis 运行 redis-server.exe redis.windows.conf 。
如果想方便的话,可以把 redis 的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个 redis.windows.conf 可以省略,如果省略,会启用默认的。输入之后,会显示如下界面:
启动成功后
启动成功后不要关闭命令窗口,不然redis服务也会关闭。如果不想每次使用都一直开着这个命令窗口,可以将redis服务添加到windows的服务中:
安装命令:
redis-server.exe --service-install redis.windows.conf --loglevel verbose
卸载命令:
redis-server --service-uninstall
安装成功后,可以在windows的服务管理中对redis进行管理,就不用每次都打开命令窗口来启动redis服务了,如下图:
在windows中管理redis服务
获取之前项目
环境我就直接在之前的整合框架上进行搭建,springboot_mybatisplus
注意:之前搭建这个框架的时候我为了获取基础数据,在启动springboot的时候也启动了爬虫程序,如果不想每次启动都启动爬虫可以注释掉启动类中的run方法。
添加Redis依赖到pom.xml中
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>
在application.properties中添加配置
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=5000
我们需要做的配置到这里就已经完成了,Spring Boot会在侦测到存在Redis的依赖并且Redis的配置是可用的情况下,使用RedisCacheManager初始化CacheManager。也就是说要使用缓存的话,SpringBoot就会选择Redis来作为缓存的容器。
编写一个简单的redis读写测试列
/**
* redis读写测试
* @author z77z
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class RedisCacheTest {
@Autowired
StringRedisTemplate stringRedisTemplate;
@Test
public void redisTest() throws Exception {
//保存字符串
stringRedisTemplate.opsForValue().set("aaa", "111");
//读取字符串
String aaa = stringRedisTemplate.opsForValue().get("aaa");
System.out.println(aaa);
}
}
打印结果:
redis读写测试结果
编写缓存测试列
不使用缓存:
/**
* 获取数据,并且做缓存处理
* @author z77z
*
*/
@Component
public class RedisCache {
@Autowired
BeautifulPicturesService beautifulPicturesService;
//@Cacheable(value = "BeautifulPictures")
public BeautifulPictures getBeautifulPicturesList(String id) {
return beautifulPicturesService.selectById(id);
}
}
/**
* 测试类
* @author z77z
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class RedisCacheTest {
@Autowired
BeautifulPicturesService beautifulPicturesService;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
RedisCache redisCache;
@Test
public void redisTest() throws Exception {
//保存字符串
stringRedisTemplate.opsForValue().set("aaa", "111");
//读取字符串
String aaa = stringRedisTemplate.opsForValue().get("aaa");
System.out.println(aaa);
}
@Test
public void CacheTest() {
BeautifulPictures beautifulPicture = redisCache.getBeautifulPicturesList("1011");
System.out.println("第一次查询结果:");
System.out.println(beautifulPicture);
BeautifulPictures beautifulPicture1 = redisCache.getBeautifulPicturesList("1011");
System.out.println("第二次查询结果:");
System.out.println(beautifulPicture1);
}
}
执行结果:
可以从日志中看出两次查询都是执行了sql,也就是执行了
getBeautifulPicturesList
这个方法
使用缓存:
在getBeautifulPicturesList方法的上面添加@Cacheable(value = “BeautifulPictures”)注解,当请求这个方法时会先判断缓存中是否存在,存在就在缓存中获取,不会执行这个方法。不存在就正常执行这个方法获取返回值并且存如缓存中。添加注解后执行结果如下:
添加缓存注释后
从日志中可以看出,第一次查询的时候执行的sql,而第二次查询的时候没有执行sql,说明是从缓存中获取的数据。
缓存数据一致性保证
CRUD (Create 创建,Retrieve 读取,Update 更新,Delete 删除) 操作中,除了 R具备幂等性,其他三个发生的时候都可能会造成缓存结果和数据库不一致。为了保证缓存数据的一致性,在进行 CUD 操作的时候我们需要对可能影响到的缓存进行更新或者清除。如下:
/**
* 获取数据,并且做缓存处理
* @author z77z
*
*/
@Component
public class RedisCache {
@Autowired
BeautifulPicturesService beautifulPicturesService;
//查询
@Cacheable(value = "beautifulPictures")
public BeautifulPictures getBeautifulPicturesList(String id) {
return beautifulPicturesService.selectById(id);
}
//修改
@CachePut(value = "beautifulPictures")
public void updateBeautifulPicture(String id) {
BeautifulPictures beautifulPictures = new BeautifulPictures();
beautifulPictures.setTitle("Title被我修改了一下,哈哈");
beautifulPictures.setId(id);
beautifulPicturesService.updateById(beautifulPictures);
}
}
/**
* 测试类
* @author z77z
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class RedisCacheTest {
@Autowired
BeautifulPicturesService beautifulPicturesService;
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
RedisCache redisCache;
@Test
public void redisTest() throws Exception {
//保存字符串
stringRedisTemplate.opsForValue().set("aaa", "111");
//读取字符串
String aaa = stringRedisTemplate.opsForValue().get("aaa");
System.out.println(aaa);
}
@Test
public void CacheTest() {
String id = "1";
BeautifulPictures beautifulPicture = redisCache.getBeautifulPicturesList(id);
System.out.println("第一次查询结果:");
System.out.println(beautifulPicture);
BeautifulPictures beautifulPicture1 = redisCache.getBeautifulPicturesList(id);
System.out.println("第二次查询结果:");
System.out.println(beautifulPicture1);
redisCache.updateBeautifulPicture(id);
BeautifulPictures beautifulPicture2 = redisCache.getBeautifulPicturesList(id);
System.out.println("第三次查询结果:");
System.out.println(beautifulPicture2);
}
}
保持缓存一致性测试结果:
保持缓存的一致性测试
在会导致数据发生改变的方法上添加
@CachePut(value = "beautifulPictures")
注解,添加后会更新缓存中的值,并且每次都会正常执行方法内容。
SpringBoot缓存注解详解
- @Cacheable:作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
主要参数说明:
value
:缓存的名称,在 spring 配置文件中定义,必须指定至少一个,例如:@Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”}。key
:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合,例如:@Cacheable(value=”testcache”,key=”#userName”)。condition
:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存,例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2。
- @CachePut:作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用
主要参数说明:
value
,key
和condition
参数配置和@Cacheable一样。
- @CacheEvict:作用是主要针对方法配置,能够根据一定的条件对缓存进行清空
主要参数说明:
value
,key
和condition
参数配置和@Cacheable一样。allEntries
:是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存,例如:@CachEvict(value=”testcache”,allEntries=true)。beforeInvocation
:是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存,例如@CachEvict(value=”testcache”,beforeInvocation=true)。
另外说下: @cache(“something”);这个相当于save()操作,@cachePut相当于Update()操作,只要他标示的方法被调用,那么都会缓存起来,而@cache则是先看下有没已经缓存了,然后再选择是否执行方法。@CacheEvict相当于Delete()操作。用来清除缓存用的。