Redis-替代Mybatis二级缓存
Posted 玩葫芦的卷心菜
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis-替代Mybatis二级缓存相关的知识,希望对你有一定的参考价值。
文章目录
编程不良人Redis链接
Redis面经属实有点懵,回头补一下
1、二级缓存
作缓存的对象类型需要实现序列化
1.1、cache使用
在dao层对应的mapper配置文件中使用cache标签即可开启二级缓存
<cache/>
缓存是key-value形式,key可以看作select语句(key还包含其他信息’)
开启后,查询语句首先去通过select查询本地缓存,
- 若发现对应select命中缓存则直接返回结果
- 若没发现则请求数据库后返回结果会被添加到本地缓存,下次查询发现是同一条语句就会直接返回,不需要请求数据库,大大增加了查询效率
1.2、原理
Mybatis开启Cache后会默认使用实现了Cache接口的PerpetualCache类做缓存操作
也可以通过指定type使用不同的Cache子类做二级缓存
<cache type=""/>
public class PerpetualCache implements Cache {
private final String id;
private final Map<Object, Object> cache = new HashMap();
public PerpetualCache(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
public int getSize() {
return this.cache.size();
}
public void putObject(Object key, Object value) {
this.cache.put(key, value);
}
public Object getObject(Object key) {
return this.cache.get(key);
}
public Object removeObject(Object key) {
return this.cache.remove(key);
}
public void clear() {
this.cache.clear();
}
public boolean equals(Object o) {
if (this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else if (this == o) {
return true;
} else if (!(o instanceof Cache)) {
return false;
} else {
Cache otherCache = (Cache)o;
return this.getId().equals(otherCache.getId());
}
}
public int hashCode() {
if (this.getId() == null) {
throw new CacheException("Cache instances require an ID.");
} else {
return this.getId().hashCode();
}
}
}
单体应用使用二级缓存绰绰有余,但是使用集群后,每个服务对应一个JVM,本地缓存是不共享的,所以就需要使用一个外界中间件提供共享缓存来实现二级缓存-Redis
2、Redis替代二级缓存
上面说到开启cache后使用Cache子类来进行缓存操作
所以我们通过实现Cache的自定义类
使用RedisTemplate来远程操作Redis进行缓存的操作,所有的缓存统一在redis上获取和添加
2.1、获取ApplicationContext
Cache实现类并没有添加到容器中,所以需要获取上下文来注入RedisTemplate
将类添加到容器的方式:@Service、@Controller、@Component等,里面可以随意使用自动注入
@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);
}
}
2.2、自定义cache实现类
1、必须有常量字符串id
2、必须构造器赋予id(为mapper对应namespace,一个dao通常对应一个表的所有操作)
发现使用以id作key的hash类缓存很适合,同一个id下所有key为同一个dao的所有查询语句
hset namespace (select xx from) xxxx
hset namespace (select xx from) xxxx
清空缓存 清空id的对应缓存就将表更新后的对应的所有缓存清掉了
del namespace
缺点是级联查询的缓存有问题,通过cache-ref可解决(在下方)
3、put为添加缓存
4、get为获取缓存
5、当表更新后,就需要清空这张表对应的所有缓存,通过clear实现
public class RedisCache implements Cache {
private final String id;
//id指定namespace
public RedisCache(String id) {
System.out.println("id==========="+id);
this.id = id;
}
@Override
public String getId() {
return this.id;
}
@Override
public void putObject(Object key, Object value) {
System.out.println("key==="+key);
System.out.println("value==="+value);
getRedisTemplate().opsForHash().put(id,key.toString(),value);
}
@Override
public Object getObject(Object key) {
System.out.println("key:"+key);
return getRedisTemplate().opsForHash().get(id,key.toString());
}
@Override
public Object removeObject(Object o) {
return null;
}
@Override
public void clear() {
getRedisTemplate().delete(id);
}
@Override
public int getSize() {
return 0;
}
//方便获取redisTemplate
private RedisTemplate getRedisTemplate(){
RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate");
redisTemplate.setKeySerializer(new StringRedisSerializer());//指定key的序列化类型
redisTemplate.setHashKeySerializer(new StringRedisSerializer());//指定hash类型的key序列化类型
return redisTemplate;
}
}
2.3、关联查询的缓存处理
关联查询的时候,会根据两个表进行查询
当其中一个表更新删除对应缓存后,另一张表的关联缓存可能还在就会导致一致性错误
所以通过缓存引用来解决关联查询的缓存问题
在关联查询的任意一方添加缓存引用即可
<cache-ref namespace="com.chime.redis_study.dao.UserDao"/>
当在对应mapper添加缓存引用后,之后该namespace的所有查询都会使用引用的缓存
如在EmpMapper里引用了EmpDao,EmpDao和UserDao共享UserDao的缓存空间,之后任意一表更新就会清空共享的所有缓存
3、缓存优化策略
3.1、对key进行优化
我们发现自动生成的key过长,可以通过MD5加密将其缩减为32位16进制字符串
MD5特点:
- 通过MD5加密后生成32位16进制字符串
- 不同内容文件经过加密,加密结果不一致(如何比较两文件内容不同?–通过MD5加密后比较加密串)
- 相同内容文件多次经过MD5加密结果一致
@Test
public void testMd5(){
String key="-453465999:1075164090:com.chime.redis_study.dao.UserDao.findAll:0:2147483647:select id,name,age from user:SqlSessionFactoryBean";
String s = DigestUtils.md5DigestAsHex(key.getBytes());
System.out.println(s);
}
a22ac04c2dcd56aa81f82243f644e503
优化的Cache实现类(只需要修改与key关联的put、set就行)
public class RedisCache implements Cache {
@Override
public void putObject(Object key, Object value) {
getRedisTemplate().opsForHash().put(id,getKeyToMd5(key.toString()),value);
}
@Override
public Object getObject(Object key) {
return getRedisTemplate().opsForHash().get(id,getKeyToMd5(key.toString()));
}
public String getKeyToMd5(String key){
return DigestUtils.md5DigestAsHex(key.getBytes());
}
}
推荐Redis做缓存时key长度过长使用MD5对key进行优化
以上是关于Redis-替代Mybatis二级缓存的主要内容,如果未能解决你的问题,请参考以下文章