什么是缓存(Cache)
1.定义:就是计算机内存中的一段数据
2.特点:读写快,断电即失
3.缓存解决的问题:
- 提高网站吞吐量和运行效率
- 减轻数据库访问压力
- 注意使用缓存时一定是数据库中数据极少发生修改,更多用于查询这种情况 比如收货地址等
4.本地缓存和分布式缓存区别:
- 本地缓存:存在应用服务器内存中的数据称为本地缓存
- 分布式缓存:存储在当前应用服务器内存之外的数据称为分布式缓存
- 集群:将同一种服务的多个节点放在一起共同对系统提供服务的过程称之为集群
- 分布式:有多个不同服务集群共同对系统提供服务,就称为分布式系统
利用mybatis中自身本地缓存结合redis实现分布式缓存
- mybatis中应用级缓存(二级缓存) SqlSessionFactory级别缓存 所有会话共享
- mybatis二级缓存也就是本地缓存,在xml文件中用标签开启,在第一次查询完数据库后会把数据加入到缓存中,之后查询相同的数据会先走缓存
- mybatis结合redis解决了缓存穿透的问题:如果查询了数据库中不存在的值,会在缓存中存储一个null值返回,之后无论查多少次都会返回这个null值,而不用再次经过数据库
- 查看Cache标签缓存实现:mybatis底层默认使用的是org.apache.ibatis.cache.impl.PerpetualCache实现
- 自定义RedisCache实现
- 1.通过mybatis默认Cache源码可知,可以使用自定义Cache类,通过实现Cache接口,并对里面的方法进行实现
- 2.使用RedisCache实现 《cache type=“xxxx.RedisCache” /》 (全路径名)
工具类
package com.baizhi.cache;
import com.baizhi.util.ApplicationContextUtils;
import org.apache.ibatis.cache.Cache;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
public class RedisCache implements Cache {
//当前放入缓存的mapper的namespace
private final String id;
//必须存在构造方法
public RedisCache(String id) {
System.out.println(id);
this.id = id;
}
//返回cache唯一标识
@Override
public String getId() {
return this.id;
}
//缓存放入值
@Override
public void putObject(Object key, Object value) {
//因为此时是通过mybatis来实现redis的,所以不能用注解注入RedisTemplate,创建一个工具类
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
//序列化方式
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//使用Redis的hash类型作为缓存存储模型 key hashkey value
redisTemplate.opsForHash().put(id.toString(),key.toString(),value);
}
//缓存中获取数据
@Override
public Object getObject(Object key) {
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//根据key从redis缓存中的hash类型中获取数据
return redisTemplate.opsForHash().get(id.toString(),key.toString());
}
//删除指定的key的缓存
//这个方法为mybatis的保留方法,默认没有实现,后续版本可能会实现
@Override
public Object removeObject(Object o) {
return null;
}
//清空缓存,增删改操作时默认会走这个方法
@Override
public void clear() {
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//清空
redisTemplate.delete(id.toString());//清空缓存
}
//计算缓存数量
@Override
public int getSize() {
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//获取hash中key value 数量
return redisTemplate.opsForHash().size(id.toString()).intValue();
}
//可以把相同的操作封装成一个方法
}
配置
package com.baizhi.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
//用来获取springboot创建好的工厂
@Component
public class ApplicationContextUtils implements ApplicationContextAware {
//保留工厂
private static ApplicationContext applicationContext;
//将创建好的工厂以参数形式传递给这个类
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
//提供在工厂中获取对象的方法
public static Object getBean(String beanName){
return applicationContext.getBean(beanName);
}
}
问题
- 如果项目中表查询之间没有任何关联查询,那么使用现在的这种缓存方式没有问题
- 但如果有联表查询时,那么当一张表执行了增删改操作而清空缓存时,它只会清空自身对应的namespace的缓存,而不会清空关联表的缓存
如何在mybatis的缓存中解决有关联关系时更新缓存信息的问题?
在Emp表的xml文件中使用《cache-ref namespace=“com.baozhi.dao.UserDAO” /》标签,让Emp表和User表共享缓存,相当于Emp表使用了User表的缓存,这样在一张表上使用增删改操作时,所关联的表也会清空缓存
缓存优化策略
- 对放入redis中key进行优化:key的长度不能太长,可以将key进行MD5加密,因为一切文件字符串等经过md5加密处理之后,都会生成32位16进制字符串,而且不同内容的文件或字符串加密之后的结果一定不同(可以用来判断文件内容是否一致)
@Test
public void testMd5(){
String key = "yyygwgdgduscbgcyag:com.baizhi.dao.udhuwguqghdhadaguigdabcsd595151ed4f4vsfg";
//spring框架提供了md5工具类
String s = DigestUtils.md5DigestAsHex(key.getBytes());
System.out.println(s);//e0cc5e2d3c43bfba13537a476ffa67fd
}