之前用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版本
下载Redis版本

打开一个 cmd 窗口 使用cd命令切换目录到 C:\tools\redis 运行 redis-server.exe redis.windows.conf 。
如果想方便的话,可以把 redis 的路径加到系统的环境变量里,这样就省得再输路径了,后面的那个 redis.windows.conf 可以省略,如果省略,会启用默认的。输入之后,会显示如下界面:

启动成功后
启动成功后

启动成功后不要关闭命令窗口,不然redis服务也会关闭。如果不想每次使用都一直开着这个命令窗口,可以将redis服务添加到windows的服务中:

1
2
3
4
5
安装命令:
redis-server.exe --service-install redis.windows.conf --loglevel verbose
卸载命令:
redis-server --service-uninstall

安装成功后,可以在windows的服务管理中对redis进行管理,就不用每次都打开命令窗口来启动redis服务了,如下图:

在windows中管理redis服务
在windows中管理redis服务

获取之前项目

环境我就直接在之前的整合框架上进行搭建,之前项目下载地址:https://git.oschina.net/z77z/springboot_mybatisplus
注意:之前搭建这个框架的时候我为了获取基础数据,在启动springboot的时候也启动了爬虫程序,如果不想每次启动都启动爬虫可以注释掉启动类中的run方法。

添加Redis依赖到pom.xml中

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
</dependency>

在application.properties中添加配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# REDIS (RedisProperties)
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=localhost
# 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=0

我们需要做的配置到这里就已经完成了,Spring Boot会在侦测到存在Redis的依赖并且Redis的配置是可用的情况下,使用RedisCacheManager初始化CacheManager。也就是说要使用缓存的话,SpringBoot就会选择Redis来作为缓存的容器。

编写一个简单的redis读写测试列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 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读写测试结果
redis读写测试结果

编写缓存测试列

不使用缓存:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* 获取数据,并且做缓存处理
* @author z77z
*
*/
@Component
public class RedisCache {
@Autowired
BeautifulPicturesService beautifulPicturesService;
//@Cacheable(value = "BeautifulPictures")
public BeautifulPictures getBeautifulPicturesList(String id) {
return beautifulPicturesService.selectById(id);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
/**
* 测试类
* @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 操作的时候我们需要对可能影响到的缓存进行更新或者清除。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 获取数据,并且做缓存处理
* @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);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* 测试类
* @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:作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存

主要参数说明:

  1. value:缓存的名称,在 spring 配置文件中定义,必须指定至少一个,例如:@Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”}。
  2. key:缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合,例如:@Cacheable(value=”testcache”,key=”#userName”)。
  3. condition:缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存,例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2。
  • @CachePut:作用是主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,和 @Cacheable 不同的是,它每次都会触发真实方法的调用

主要参数说明:

  1. valuekeycondition参数配置和@Cacheable一样。
  • @CacheEvict:作用是主要针对方法配置,能够根据一定的条件对缓存进行清空

主要参数说明:

  1. valuekeycondition参数配置和@Cacheable一样。
  2. allEntries:是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存,例如:@CachEvict(value=”testcache”,allEntries=true)。
  3. beforeInvocation:是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存,例如@CachEvict(value=”testcache”,beforeInvocation=true)。

另外说下: @cache(“something”);这个相当于save()操作,@cachePut相当于Update()操作,只要他标示的方法被调用,那么都会缓存起来,而@cache则是先看下有没已经缓存了,然后再选择是否执行方法。@CacheEvict相当于Delete()操作。用来清除缓存用的。

荆轲~刺秦王~~