1. AOP缓存的实现
1.1 自定义注解
在hcds-common下创建一个新的包,名字叫com.hc.annotation,作为自定义注解的包,注解名称命名为CacheFind
package com.hc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD) //标识注解使用在方法中
@Retention(RetentionPolicy.RUNTIME)//什么时期有效
public @interface CacheFind {
//key-value value是方法的返回值
String key();//要求用户必须指定key
int seconds() default -1;//设定超时时间 -1 无需超时
}
1.2 使用缓存注解
在ItemCatServiceImpl里面找到findItemCatList方法,在方法上面使用缓存注解@CacheFind(key=“是哪里的业务逻辑就写成哪里的key”)
例如:@CacheFind(key=“ITEM_CAT_PARENTID”)
1.3 编辑RedisAOP
在hcds-common下找到对应的com.hc.aop包下的RedisAOP
1.3.1 通知选择:是否控制目标方法是否执行 环绕通知
原因:如果有缓存数据,直接查找缓存数据,但是如果没有缓存数据,需要走相应的目标方法
//通知选择:是否控制目标方法是否执行 环绕通知
//切入点的表达式:控制注解 @annotation(语法...)
@Around("@annotation(com.hc.annotation.CacheFind)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("执行环绕通知");
return joinPoint.proceed();
}
1.3.2 反射机制
获取目标对象~~~获取方法对象~获取注解 原始API
/**
* 需求:如何动态获取注解中的属性值...
* 原理 反射机制
* 获取目标对象~~~~~获取方法对象~~~获取注解 原始API
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("@annotation(com.hc.annotation.CacheFind)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//1.获取目标对象类型
Class targetClass=joinPoint.getTarget().getClass();
//2.获取方法
String name=joinPoint.getSignature().getName();
Object[] objArgs=joinPoint.getArgs();
Class[] classArgs=new Class[objArgs.length];
for (int i=0;i<objArgs.length;i++){
Object obj = objArgs[i];
classArgs[i] = obj.getClass();
}
Method method=targetClass.getMethod(name,classArgs);
System.out.println("执行环绕通知");
return joinPoint.proceed();
}
上面是运用反射机制写的一个比较复杂的写法,但是我们aop有一些方法可以直接取到我们想取的东西,例如第二种写法:
@Around("@annotation(com.hc.annotation.CacheFind)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature= (MethodSignature) joinPoint.getSignature();
Method method=methodSignature.getMethod();
CacheFind cacheFind=method.getAnnotation(CacheFind.class);
String key=cacheFind.key();
System.out.println("获取key"+key);
return joinPoint.proceed();
}
spring优化aop(动态获取属性的模板)
AOP中的语法规范1.:
* 如果通知方法有参数需要添加,则joinPoint 必须位于第一位.
* 报错信息: error at ::0 formal unbound in pointcut
* AOP中的语法规范2:
* 如果需要动态接受注解对象,则在切入点表达式中直接写注解参数名称即可
* 虽然看到的是名称,但是解析时变成了包名.类型
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) throws Throwable {
String key=cacheFind.key();
System.out.println("获取key"+key);
return joinPoint.proceed();
}
@Around("@annotation(cacheFind)")
public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) throws Throwable {
Object result = null;
//1.获取key="ITEM_CAT_PARENTID"
String key = cacheFind.key();
//2.动态拼接key 获取参数信息
String args = Arrays.toString(joinPoint.getArgs());
key += "::" + args;
//3.redis缓存实现
if(jedis.exists(key)){
String json = jedis.get(key);
//target标识返回值类型????
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
Class returnType = methodSignature.getReturnType();
result = ObjectMapperUtil.toObject(json, returnType);
System.out.println("AOP缓存查询!!!");
}else{
//查询数据库 执行目标方法
result = joinPoint.proceed();
String json = ObjectMapperUtil.toJSON(result);
if(cacheFind.seconds()>0)
jedis.setex(key,cacheFind.seconds(),json);
else
jedis.set(key, json);
System.out.println("AOP查询数据库");
}
return result;
}
2 关于Redis 持久化机制
2.1 业务需求
Redis中的运行环境在内存中, 但是内存特点 断电即擦除.
想法:能否保存redis中的内存数据不丢失呢?
持久化: 将内存数据定期保存到磁盘中.
2.2 RDB模式
2.2.1 关于RDB模式说明
1.Redis 定期 将内存数据保存到RDB文件中.
2.RDB模式 redis默认的规则.
3.RDB模式记录的是内存数据的快照 ,持久化效率更高(只保留最新数据)
4.RDB模式由于定期持久化,可能导致数据丢失.(致命问题)
(解析1和2)删除Linux下的redis文件夹下的dump.rdb 文件,你会发现之前redis客户端里存储的数据都消失了
(解析3)将dump.rdb文件进行cat操作之后,我们会发现文件里面大部分都是乱码,其实这就是内存数据的快照。
2.2.2 RDB命令
1: 持久化操作 save 同步操作 可能其他线程陷入阻塞
2: 后端持久化 bgsave 异步操作 用户操作不会陷入阻塞 该操作什么时候完成不清楚.
在redis客户端下,敲写save命令 进行持久化操作 大家会发现之前删除的dump.rdb文件又重新出现了
进行持久化操作的时候,因为是同步操作所以我们可能导致线程进入阻塞状态,大家可以想一下我们不能一边进行set操作,一边进行持久化操作吧,就像不能一边吃一边那啥一样
2.2.3 RDB模式配置
save 900 1 900秒内用户更新1次 则持久化1次
save 300 10 300秒内用户更新10次 持久化 1次
save 60 10000 60秒内用户更新10000次 持久化1次
save 1 1 1秒操作1次,持久化1次 保证数据安全性 问题:效率极低 阻塞…
如果想让持久化性能更优,则需要通过监控的手段灵活运用.
用户操作越频繁,则持久化周期越短.
我们进入redis文件夹下,vim redis.conf 进入redis的配置文件中 然后搜索save(:/save 检索save)
2.3 AOF模式
2.3.1 开启AOF模式
默认条件下AOF模式 默认关闭的 如果想修改直接将后面的no改成yes
注意事项:(:/appendonly 检索下appendonly )
(:set nu 赋予行号)
开启AOF(appendfilename AOF持久化文件名称叫"appendonly.aof")
关闭redis的服务,重启redis的服务,然后大家会发现redis.conf文件下多了一个appendonly.aof的文件,至此代表AOF持久化操作成功
2.3.2 AOF模式特点
说明: 当开启AOF策略之后,redis持久化以AOF为主.(之前redis里面的数据没了)
特点:
1. AOF文件默认关闭的,需要手动开启
2. AOF文件记录的**是用户的操作过程**.则可以实现实时持久化操作.(几乎不丢数据)
3. AOF文件做追加的操作,所有持久化文件较大.
4. AOF持久化时,采用异步的方式进行.
5. AOF文件需要定期清理.
第2条解析:
2.3.3 AOF持久化原则
appendfsync always 用户执行一次操作,持久化一次
appendfsync everysec 每秒持久化一次
appendfsync no 不主动持久化
2.3.4关于AOF与RDB如何选择
业务:
1.如果用户追求速度,允许少量的数据丢失 首选RDB模式. 快
2.如果用户追求数据的安全性. 首选AOF模式.
面试题:
如果你在redis中执行了flushAll命令,如何挽救??
答案: 修改AOF文件中,删除flushAll的命令.重启redis即可.
注意事项: 一般条件下 Redis会开启AOF与RDB 2种模式
常规用法: 一般会配置redis主从结构 主机开启RDB模式 从机开启AOF模式
3 关于Redis 内存优化策略
3.1 关于内存优化的说明
Redis运行环境,在内存中. 但是内存资源有限的.不能一味的扩容.所以需要对内存数据优化.
Redis内存大小的设定:
最大内存设定:
3.2 LRU算法
LRU是Least Recently Used的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面(数据)予以淘汰。该算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
维度: 时间T
3.3 LFU算法
LFU(least frequently used (LFU) page-replacement algorithm)。即最不经常使用页置换算法,要求在页置换时置换引用计数最小的页,因为经常使用的页应该有一个较大的引用次数。但是有些页在开始时使用次数很多,但以后就不再使用,这类页将会长时间留在内存中,因此可以将引用计数寄存器定时右移一位,形成指数衰减的平均使用次数。
维度: 引用次数
3.4随机算法
说明: 随机生成挑选数据删除.
3.5 TTL算法
说明: 根据设定了超时时间的数据,将马上要超时的数据提前删除.
3.6 算法优化
- volatile-lru 设定超时时间的数据中,采用lru算法删除数据.
2.allkeys-lru 在所有的数据中 采用LRU算法删除数据.
3.volatile-lfu 在设定了超时时间的数据中 采用LFU算法删除数据.
4.allkeys-lfu 在所有数据中. 采用LFU算法删除数据.
5.volatile-random 设定了超时时间数据,采用随机的方式删除数据
6.allkeys-random 所有数据采用随机算法.
7.volatile-ttl 设定超时时间的数据,采用TTl算法删除. - noeviction 默认不删除数据. 如果内存满了,则报错返回. 默认策略
4 关于Redis缓存常见面试题
5分钟:自己上网查找资料!!!
4.1 什么是缓存穿透
说明: 在高并发的条件下,用户频繁访问一个不存在的数据.导致大量的请求直接发往数据库.导致服务器宕机.
解决方案:
1. 只要能够保障用户访问的数据 数据库中一定存在. 布隆过滤器
2. IP限流 黑名单. 微服务中可以通过API网关进行控制
4.2 什么是缓存击穿
说明: 在高并发条件下 用户频繁访问一个热点数据. 但是该热点数据在缓存中失效.导致用户直接访问数据库.
俗语: 称它病,要它命
4.3 什么是缓存雪崩
说明: 在高并发的条件下, 有大量的缓存数据失效了. 导致redis缓存服务器命中率太低.从而导致用户直接访问数据库.
主要问题: 热点数据失效.
解决方案:
1.为热点数据设定超时时间时 采用随机数. 10秒+随机数 保障数据不会同时失效.
2. 设定多级缓存
5. 作业
1.预习什么是布隆过滤器 二进制数组长度 hash函数的个数
2.什么是redis分片机制/哨兵机制
3.什么是一致性hash算法.