【SpringBoot 2学习笔记】《十二》SpringBoot2缓存之旅Redis探秘

12.1 前提

Redis安装:https://blog.csdn.net/gavinbj/article/details/104098730

代码基于SpringCache的代码:https://blog.csdn.net/gavinbj/article/details/104310547

DB访问代码自动生成参考:https://blog.csdn.net/gavinbj/article/details/104024890

12.2 Redis简介

Redis是一个高性能的key-value缓存存储数据库。完全开源免费的,遵守BSD协议。Redis产品有以下三个特点:

  • Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。
  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。
  • Redis支持数据的备份,即master-slave模式的数据备份。
  • 性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子行:Redis的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过MULTI和EXEC指令包起来。
  • 其他特性:Redis还支持 publish/subscribe, 通知, key 过期等等特性。

12.3 配置文件(POM和Properties)

pom.xml中添加关于Redis的依赖。

	<dependency>
	    <groupId>org.springframework.bootgroupId>
	    <artifactId>spring-boot-starter-data-redisartifactId>
	dependency>

配置文件添加Redis配置项目:application.properties

# Redis 配置项目
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器连接密码(默认为空)
spring.redis.password=HaoJT1980
# Redis服务器地址
spring.redis.host=119.3.155.121
# Redis服务器连接端口
spring.redis.port=6379
# 链接超时时间 单位 ms(毫秒)
spring.redis.timeout=3000

#连接池中最大空闲链接,默认值是8
spring.redis.jedis.pool.max-idle=20
#连接池中最小空闲链接,默认是0
spring.redis.jedis.pool.min-idle=10
## 如果赋值为-1,则表示不限制
spring.redis.jedis.pool.max-active=200
#等待连接的最长时间
spring.redis.jedis.pool.max-wait=1000

12.4 应用代码

Controller层和前面一样不在赘述,这里只给出改变的业务逻辑层代码。

package com.gavinbj.confmng.service.impl;

import java.util.List;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import com.gavinbj.confmng.persistence.entity.UserInfo;
import com.gavinbj.confmng.persistence.entity.UserInfoExample;
import com.gavinbj.confmng.persistence.mapper.UserInfoMapper;
import com.gavinbj.confmng.service.UserInfoService;

/**
 * 用户信息
 * 
 * @author gavinbj
 *
 */
@Service
public class UserInfoServiceRedisImpl implements UserInfoService {

	@Autowired 
	private UserInfoMapper userInfoMapper;
	
	@Autowired
    private RedisTemplate<Object, Object> redisTemplate;
	
	/**
	 * 根据用户ID进行主键检索
	 */
	@Override
	public UserInfo getUserByPK(String userId) {
		UserInfo user = (UserInfo)redisTemplate.opsForValue().get(userId);
		if(user == null) {
			user = this.userInfoMapper.selectByPrimaryKey(userId);
		}
		if (user != null) {
			redisTemplate.opsForValue().set(user.getUserId(), user, 100L, TimeUnit.SECONDS);			
		}
		return user;
	}
	
	/**
	 * 保存用户信息
	 * 
	 */
	@Override
	public UserInfo saveUserInfo(UserInfo user) {
		UserInfo userExist = this.userInfoMapper.selectByPrimaryKey(user.getUserId());
		if(userExist == null) {
			// 该用户ID不存在可以插入用户
			this.userInfoMapper.insertSelective(user);
			redisTemplate.opsForValue().set(user.getUserId(), user, 100L, TimeUnit.SECONDS);
		}else {
			throw new SysException(EnumCodeMsg.USER_EXIST, "用户信息");
		}
		return user;
	}
	
	
	/**
	 * 删除用户信息
	 * 
	 */
	@Override
	public void delUserInfo(String userId) {
	
		this.userInfoMapper.deleteByPrimaryKey(userId);
        // 同步删除Redis中的该用户信息
		redisTemplate.delete(userId);
	}

}

12.5 验证缓存

1、验证保存数据到Redis

验证工具:Postman (UserInfoServiceImpl.saveUserInfo)

POST
http://localhost:9003/gavin/api/users
JSON Body:
{
	"userId" : "lijing",
	"userName" : "李静",
	"introduce" : "美女主播",
	"mobilephone" : "13948474647",
	"email": "[email protected]"
}

执行结果:

{
    "status": 0,
    "code": 1003,
    "msg": "处理成功!",
    "data": {
        "userId": "lijing",
        "userName": "李静",
        "introduce": "美女主播",
        "mobilephone": "13948474647",
        "email": "[email protected]",
        "birthday": null,
        "gender": null
    }
}

控制台输出:

2020-02-15 10:10:29.403  INFO 18812 --- [nio-9003-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/gavin]  : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-02-15 10:10:30.130 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.selectByPrimaryKey           : ==>  Preparing: select user_id, user_name, introduce, mobilephone, email, birthday, gender from user_info where user_id = ? 
2020-02-15 10:10:30.138 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.selectByPrimaryKey           : ==> Parameters: lijing(String)
2020-02-15 10:10:30.295 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.selectByPrimaryKey           : <==      Total: 0
2020-02-15 10:10:30.319 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.insertSelective              : ==>  Preparing: insert into user_info ( user_id, user_name, introduce, mobilephone, email ) values ( ?, ?, ?, ?, ? ) 
2020-02-15 10:10:30.352 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.insertSelective              : ==> Parameters: lijing(String), 李静(String), 美女主播(String), 13948474647(String), [email protected](String)
2020-02-15 10:10:30.457 DEBUG 18812 --- [nio-9003-exec-1] c.g.c.p.m.U.insertSelective              : <==    Updates: 1

此时在数据库中已经保存了该数据,接下来我们用检索数据的方式,验证数据是直接从缓存取得相应数据。因为我们开启了SQL执行日志的打印,所以,如果检索时,有没有打印输出检索SQL使我们检验是否走缓存的依据。

GET
http://localhost:9003/gavin/api/users/lijing

执行结果:

{
    "status": 0,
    "code": 1003,
    "msg": "处理成功!",
    "data": {
        "userId": "lijing",
        "userName": "李静",
        "introduce": "美女主播",
        "mobilephone": "13948474647",
        "email": "[email protected]",
        "birthday": "",
        "gender": ""
    }
}

此时看控制台,没有输出新的检索SQL,可以验证本次数据检索直接从Redis数据区中取得,在上次请求中除了将用户信息保存到数据库外,Redis数据库中也缓存了相应的用户数据。

2、验证清除缓存

基于第一步中,我们在向数据库保存用户信息时,同时将该数据保存到了Redis数据库中。现在,我们调用删除用户信息方法(该方法中通过使用redisTemplate.delete来删除指定关键字的缓存内容),我们看看是否同时将缓存的用户信息删除掉。验证步骤如下:

  • 调用用户删除方法

  • 调用用户信息查询

如果缓存删除后,在调用用户信息查询,应该去数据库检索,控制台能够输出相应的检索语句。

DELETE
http://localhost:9003/gavin/api/users/lijing

执行结果

{
    "status": 0,
    "code": 1003,
    "msg": "处理成功!",
    "data": "OK"
}

控制台信息:

2020-02-15 10:15:03.273 DEBUG 18812 --- [nio-9003-exec-6] c.g.c.p.m.U.deleteByPrimaryKey           : ==>  Preparing: delete from user_info where user_id = ? 
2020-02-15 10:15:03.276 DEBUG 18812 --- [nio-9003-exec-6] c.g.c.p.m.U.deleteByPrimaryKey           : ==> Parameters: lijing(String)
2020-02-15 10:15:03.449 DEBUG 18812 --- [nio-9003-exec-6] c.g.c.p.m.U.deleteByPrimaryKey           : <==    Updates: 1

从上面可以看出,已经从数据库删除了数据并打印了删除语句。

接下来执行检索用的接口

GET
http://localhost:9003/gavin/api/users/lijing

执行结果

{
    "status": 1,
    "code": 5003,
    "msg": "用户信息检索结果为空",
    "data": ""
}

我们看控制台输出:

2020-02-15 10:15:31.510 DEBUG 18812 --- [nio-9003-exec-7] c.g.c.p.m.U.selectByPrimaryKey           : ==>  Preparing: select user_id, user_name, introduce, mobilephone, email, birthday, gender from user_info where user_id = ? 
2020-02-15 10:15:31.512 DEBUG 18812 --- [nio-9003-exec-7] c.g.c.p.m.U.selectByPrimaryKey           : ==> Parameters: lijing(String)
2020-02-15 10:15:31.559 DEBUG 18812 --- [nio-9003-exec-7] c.g.c.p.m.U.selectByPrimaryKey           : <==      Total: 0

可见执行检索时,没有从缓存中区的数据,接下里直接检索数据库,数据库检索结果也没有。所以返回用户信息为空。刚才删除Redis缓存也是正确的。

3、验证检索功能同时可以存储缓存和查询缓存

通过如上两步,现在缓存中没有缓存的用户信息,我们此时执行两次检索方法,检索一个数据库中存在的用户。第一次,因为缓存中没有该用户,所以会输出检索用SQL。第二次在执行该用户的检索,此时应该直接使用缓存中的检索结果,不去数据库重新检索,控制台中不输出SQL。

GET
http://localhost:9003/gavin/api/users/zhangsanfeng

连续执行两次结果:

{
    "status": 0,
    "code": 1003,
    "msg": "处理成功!",
    "data": {
        "userId": "zhangsanfeng",
        "userName": "张三丰",
        "introduce": "一代宗师",
        "mobilephone": "13948474647",
        "email": "[email protected]",
        "birthday": "",
        "gender": ""
    }
}

控制台输出:

2020-02-15 10:18:49.607 DEBUG 18812 --- [nio-9003-exec-8] c.g.c.p.m.U.selectByPrimaryKey           : ==>  Preparing: select user_id, user_name, introduce, mobilephone, email, birthday, gender from user_info where user_id = ? 
2020-02-15 10:18:49.608 DEBUG 18812 --- [nio-9003-exec-8] c.g.c.p.m.U.selectByPrimaryKey           : ==> Parameters: zhangsanfeng(String)
2020-02-15 10:18:49.655 DEBUG 18812 --- [nio-9003-exec-8] c.g.c.p.m.U.selectByPrimaryKey           : <==      Total: 1

从上可以看出第一次检索zhangsanfeng时,直接检索了数据库。第二次检索使用了缓存,没有打印出SQL日志。

以上是基本的缓存使用的例子,旨在让大家了解入门,实际项目中使用缓存的场景比较复杂,这里不做分析,各位根据实际项目进行适当的研究学习。

你可能感兴趣的:(SpringBoot2)