引言:
对于一些经常访问,但又不经常变更的数据,如果没有做缓存处理,那么每次访问都是在查询数据库,效率低下。
集成缓存后,访问的数据来源的查询过程:
1.先在缓存中查找数据,如果查出数据,直接返回前端;
2.如果缓存中没有数据,则去数据库查找,查出来之后再添加保存到缓存,再返回前端;
集成缓存后,数据的添加、修改、删除等操作,都需要将对应缓存数据清除,保证下一次查询的数据是最新的。
1.模块集成redis缓存
使用Redis作为共享缓存 ,解决缓存不同步问题,Redis是独立的服务,缓存不用占用应用本身的内存空间
1.1 模块导入redis依赖
使用spring提供的redisTemplate
<!-- 缓存redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 需要用到的解析json对象及字符串等操作的依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
1.2 配置application.yml配置文件的redis信息
连接redis的信息设置:
spring:
redis:
host: localhost
port: 6379
timeout: 5000 #连接超时 毫秒
jedis:
pool:
maxActive: 30
maxIdle: 30
minIdle: 10
maxWait: -1 #连接池最大等行时间 -1没有限制
password: 123456
1.3 自定义redis操作的常量类
/**
* 定义常量类(用接口是因为默认会被public static final修饰)
* 专门用来存储系统中需要用到缓存的key的名称
*/
public interface RedisContants {
String REDIS_KEY_COURSE_TYPE_TREE = "redis_key_course_type_tree";
}
1.4 自定义redis操作的工具类()
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component //给spring管理
public class RedisService {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 向Redis中存储 key-value键值对数据,value的类型为object
* @param key
* @param value
*/
public void setStringKeyAndValue(String key, Object value) {
stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(value));
}
/**
* 保存key-value键值对到 redis,存储的value是字符串类型
* @param key
* @param value
*/
public void setStringKeyAndValue(String key, String value) {
stringRedisTemplate.opsForValue().set(key, value);
}
/**
* 存储的数据,增加设置 有效时间
* @param key
* @param value
* @param minute
*/
public void setStringKeyAndValue(String key, Object value, long minute) {
stringRedisTemplate.opsForValue().set(key, JSONObject.toJSONString(value), minute, TimeUnit.MINUTES);
}
public void setStringKeyAndValue(String key, String value, long minute) {
stringRedisTemplate.opsForValue().set(key, value, minute, TimeUnit.MINUTES);
}
/**
* 功能说明 取出对象并转换为对象类型
*
* @param key
* @param clazz
* @return T
*/
public <T> T getKeyObjectValue(String key, Class<T> clazz) {
T t = null;
String s = stringRedisTemplate.opsForValue().get(key);
//判断是否为空,避免后面的判断处理报空指针
if (StringUtils.isBlank(s)) {
return null;
}
//判断传入类型是否为字符串类型
if (StringUtils.equalsIgnoreCase(clazz.getName(), "java.lang.string")) {
t = (T) s; //定义泛型的语法要求,要转
} else if (StringUtils.isNotBlank(s)) {
//如果不是字符串类型,且有值,查出的字符串(前面定义存入的时候一定转换为字符串),转换为对应的类型
t = JSONObject.parseObject(s, clazz);
}
return t;
}
/**
* 删除缓存中对应的key-value
* @param key
*/
public void delete(String key) {
stringRedisTemplate.delete(key);
}
}
1.5 实际业务代码中应用
@Autowired
private RedisService redisService;
@Override
public AjaxResult getTreeData() {
List<CourseType> courseTypes = null;
//查询的时候先去缓存中取数据,如果没有取到,就去DB中查询,查询出来后,先存入缓存,再返回数据
String keyObjectValue = redisService.getKeyObjectValue(RedisContants.REDIS_KEY_COURSE_TYPE_TREE, String.class);
if (StringUtils.isBlank(keyObjectValue)) {
//空的,就去查数据库,再存到缓存中
courseTypes = baseMapper.selectForTreeData();
//将集合利用json转换为字符串,存到redis
redisService.setStringKeyAndValue(RedisContants.REDIS_KEY_COURSE_TYPE_TREE,
JSONArray.toJSONString(courseTypes), 30);
} else { //直接再缓存中取到值,直接返回即可
courseTypes = JSONObject.parseArray(keyObjectValue, CourseType.class);
}
return AjaxResult.me().setResultObj(courseTypes);
}
1.6 当该表数据被修改、新增、删除时,要删除对应的缓存,保证下次查出数据最新
@Override
public boolean updateById(CourseType entity) {
super.updateById(entity);
//清除缓存中对应的数据,再次查询的时候会从数据库查询,然后再存到缓存中
redisService.delete(RedisContants.REDIS_KEY_COURSE_TYPE_TREE);
return true;
}
2.SpringCache实现缓存
主要使用到3个注解:
@EnableCaching – 在启动类上开启缓存
@Cacheable – 触发缓存写入
@CacheEvict – 触发缓存清楚
2.1 启动类上开启SpringCache缓存
@SpringBootApplication
@EnableEurekaClient //开启注册
@EnableCaching //开启缓存
public class CourseServerApp1060 {
public static void main(String[] args) {
SpringApplication.run(CourseServerApp1060.class);
}
}
2.2 直接在业务代码中使用
2.2.1 场景一:key为字符串 -------- " 'xxxx ’ "
//将返回的数据保存到缓存中,key为xxx,k-v保存在缓存名为cacheNames的地方
//在使用时,key的值,特别注意:如果是一个字符串,在双引号里,要多添加一个单 引号,否则会当成变量(引号内部可以存变量)
@Cacheable(cacheNames = RedisContants.SPRING_CACHE_NAME_COURSE_TYPE_LIST,key = "'course_type_list'")
@Override
public List<CourseType> list() {
List<CourseType> list = super.list();
return list;
}
2.2.2 场景二:key为变量 -------- " xxxx "
/**
* 分页查询时,可以用spring cache缓存每页的数据,提升查询效率,加了缓存,在变更该表数据的时候,就需要清楚缓存数据
* @param query
* @return
*/
//key值可以使用变量,来做每一页的区分
@Cacheable(cacheNames = RedisContants.SPRING_CACHE_NAME_COURSE_TYPE_LIST,key = "#query.page+'_'+#query.rows")
@Override
public PageList<CourseType> pageList(CourseTypeQuery query) {
Page<CourseType> page = new Page<CourseType>(query.getPage(), query.getRows());
page = super.page(page);
return new PageList<CourseType>( page.getTotal(),page.getRecords());
}
2.3 表数据被修改、新增、删除时清除缓存
//清楚对应name缓存中的所有的key-value
@CacheEvict(cacheNames = RedisContants.SPRING_CACHE_NAME_COURSE_TYPE_LIST,allEntries = true)
//清除对应name缓存中对应key的值
//@CacheEvict(cacheNames = RedisContants.SPRING_CACHE_NAME_COURSE_TYPE_LIST,key = "'course_type_list'")
@Override
public boolean removeById(Serializable id) {
super.removeById(id);
redisService.delete(RedisContants.REDIS_KEY_COURSE_TYPE_TREE);
return true;
}