Redis分布式缓存实现
什么是缓存
定义
-
就是计算机内存中一段数据
特点
-
读写快
-
断电即失
缓存解决的问题
- 1 .
提高网站吞吐量,提高网站运行效率 - 2.核心解决问题: 缓存的存在是用来
减轻数据库访问压力
缓存能提高效率,那项目中所有书库加入缓存是否更好?
- 注意: 使用缓存时一定是数据库中
数据极少发生修改(相对),更多用于查询这种情况
本地缓存与分布式缓存的区别?
本地缓存(local cache):存在应用程序服务器内存中的数据称之为本地缓存分布式缓存(distribute cache):存储在当前应用服务器内存之外的数据称之为分布式缓存集群:将同一种服务的多个节点放在一起共同对系统提供服务的过程称之为集群分布式(distribute system):由多个不同服务集群共同对系统提供服务 这个系统称之为分布式系统- 例如:mysql集群 + redis缓存集群 + 应用集群 共同组成了一个分布式系统
- 分布式系统一定是建立在集群基础之上的
利用mybatis自身本地缓存结合redis实现分布式缓存
mybatis中应用级缓存(二级缓存) SqlSessionFactory级别缓存 所有会话共享
如何开启二级缓存 ---本地缓存
-
maper.xml <cache/>
查看Cache标签缓存实现
- 得知:mybatis底层默认使用的是 org.apache.ibatis.cache.impl.PerpetualCache 实现
自定义RedisCache实现
-
通过mybatis默认cache源码得知 可以使用自定义Cache类 implements Cache 接口 并对里面方法进行实现
-
public class RedisCache implements Cache {} -
使用RedisCache实现
-
# 在mapper.xml中开启自定义缓存 <cache type="com.longda.cache.RedisCache"/> -
写一个获得工厂的实现类
-
//用来获取springboot创建好的工厂 @Component public class ApplicationContextUtils implements ApplicationContextAware { //保留下来工厂 private static ApplicationContext applicationContext; //将创建好的工厂以参数形式传递给这个类 @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext=applicationContext; } //提供在工厂中获取对象的方法 RedisTemplate redsTemplate public static Object getBean(String beanName){ return applicationContext.getBean(beanName); } } -
实现自定义的RedisCache
-
//自定义Redis缓存实现 public class RedisCache implements Cache { private final String id; //必须存在构造方法 //id -->当前放入缓存的mapper的namespace public RedisCache(String id){ this.id=id; } //返回cache 唯一标识 @Override public String getId() { return this.id; } //缓存放入值 //redis RedisTemplate stringRedisTemplate @Override public void putObject(Object key, Object value) { //通过工具类获取redisTemplate RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate"); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); //使用redisHash类型作为缓存存储模型 key hashKey value redisTemplate.opsForHash().put(id.toString(),key.toString(),value); } //缓存获取值 @Override public Object getObject(Object key) { //根据key 从redis的hash类型中获取数据 return getRedisTemplate().opsForHash().get(id.toString(),key.toString()); } //根据指定key删除缓存 //这个方法为mybatis保留方法,默认没有实现 @Override public Object removeObject(Object key) { return null; } //清空缓存 @Override public void clear() { //清空namespace getRedisTemplate().delete(id.toString()); //清空缓存 } //用来计算缓存数量 @Override public int getSize() { return getRedisTemplate().opsForHash().size(id.toString()).intValue(); } //封装redisTemplate private RedisTemplate getRedisTemplate(){ //通过工具类获取redisTemplate RedisTemplate redisTemplate = (RedisTemplate) ApplicationContextUtils.getBean("redisTemplate"); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); return redisTemplate; } }
-
缓存在项目中的应用
- 如果项目中表查询之间没有任何关联查询,使用现在这种缓存方式没有任何问题
- 但是一旦存在表连接查询,则一定会出现问题
如何解决关联关系时更新缓存信息的问题?
<cache-ref /> //用来处理关联关系缓存
例如:
有两张表User和Emp,它们之间有关联关系,则修改各自相对应的Mapper.xml为
EmpMapper.xml
<mapper namespace="com.longda.dao.EmpDao">
<cache-ref namespace="com,longda.dao.UserDao"/>
......
</mapper>
UserMapper.xml
<mapper namespace="com.longda.dao.UserDao">
<cache/>
......
</mapper>
通过这样设置,任何一方有更新的话,关联关系的缓存也会清空
缓存优化
# 1 对放入redis中key进行优化: key的长度不能太长
1834752025:4175315364:com.longda.dao.UserDao.findAll:0:2147483647:select id,name,age,bir from t_user:SqlSessionFactoryBean
- 通过算法 : MD5处理加密
- 特点 :
1 一切文件字符串等经过md5处理之后都会生成32为16进制字符串
2 不同内容文件经过md5进行加密,加密结果一定不一致
- aa.txt 和 bb.txt是否相同如何判断?
通过md5对文件进行加密,再判断加密后的字符串是否相同
3 相同内容文件多次经过md5生成结果始终一致
- 推荐 : 在redis整合mybatis过程中建议将key 进行md5优化处理
封装对key进行md5处理方法
在RedisCache中添加封装一个md5工具方法,并对缓存放入/取出值进行md5加密
//自定义Redis缓存实现
public class RedisCache implements Cache {
private final String id;
......
//缓存放入值
//redis RedisTemplate stringRedisTemplate
@Override
public void putObject(Object key, Object value) {
//通过工具类获取redisTemplate
RedisTemplate redisTemplate = getRedisTemplate();
//使用redisHash类型作为缓存存储模型 key hashKey value
redisTemplate.opsForHash().put(id.toString(),getKeyToMD5(key.toString()),value);
}
//缓存获取值
@Override
public Object getObject(Object key) {
System.out.println(key);
//根据key 从redis的hash类型中获取数据
return getRedisTemplate().opsForHash().get(id.toString(),getKeyToMD5(key.toString()));
}
......
//封装一个对key进行md5方法
public String getKeyToMD5(String key){
return DigestUtils.md5DigestAsHex(key.getBytes());
}
}
面试相关概念
# 1 什么是缓存穿透(击穿)?
- 定义 : 客户端查询了一个数据库中没有的数据记录导致缓存在这种情况下无法利用 称之为缓存穿透 或者是缓存击穿
例如 : 用户恶意查询id=-1,或者id=最大值+Mathron,大量访问,数据库可能会宕机
mybatis中cache解决了缓存穿透
- 方法 : 将数据库中没有查询到的结果也进行了缓存.即把这个key的value赋值为null
如果此时添加了这个数据,那你的缓存中还是null值呢?
- 注意:mybatis一旦执行了增删改操作,redis缓存立马清空
# 2 什么是缓存雪崩?
- 定义 : 在系统运行的某一时刻,突然系统中缓存全部失效,恰好在这一时刻涌来了大量客户端请求,导致所有模块缓存无法利用,大量请求涌向数据库,数据库阻塞或挂起
例如 : 缓存存储时,业务系统非常大 模块多 业务数据不同 不同模块在放入缓存时 都会设置一个缓存超时时间
- 方法 :
1 缓存永久存储 (不推荐)
2 `针对于不同业务数据一定要设置不同超时时间`
# 3 项目中有没有遇到? 如何解决?
缓存穿透:没有遇到,因为mybatis已经解决了这个问题

浙公网安备 33010602011771号