1 AOP实现Redis缓存
1.1 如何了解AOP
名称: 面向切面编程
作用: 升高零碎中代码的耦合性,并且在不扭转原有代码的条件下对原有的办法进行性能的扩大.
公式: AOP = 切入点表达式 + 告诉办法
1.2 告诉类型
1.前置告诉 指标办法执行之前执行
2.后置告诉 指标办法执行之后执行
3.异样告诉 指标办法执行过程中抛出异样时执行
4.最终告诉 无论什么时候都要执行的告诉
特点: 上述的四大告诉类型 不能干涉指标办法是否执行.个别用来做程序运行状态的记录.监控
5.盘绕告诉 在指标办法执行前后都要执行的告诉办法 该办法能够控制目标办法是否运行.joinPoint.proceed(); 性能作为弱小的.
1.3 切入点表达式
了解: 切入点表达式就是一个程序是否进入告诉的一个判断(IF)
作用: 当程序运行过程中 ,满足了切入点表达式时才会去执行告诉办法,实现业务的扩大.
品种(写法):
- bean(bean的名称 bean的ID) 只能拦挡具体的某个bean对象 只能匹配一个对象
lg: bean(“itemServiceImpl”)
- within(包名.类名) within(“com.jt.service.*”) 能够匹配多个对象
粗粒度的匹配准则 按类匹配
`3. execution(返回值类型 包名.类名.办法名(参数列表)) 最为弱小的用法 lg : execution(* com.jt.service..*.*(..)) 返回值类型任意 com.jt.service包下的所有的类的所有的办法都会被拦挡. 4.@annotation(包名.注解名称) 依照注解匹配.` * 1* 2* 3* 4
1.4 AOP入门案例
`package com.jt.aop;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component;import org.springframework.stereotype.Service;import java.util.Arrays;@Aspect //我是一个AOP切面类@Component //将类交给spring容器治理public class CacheAOP { //公式 = 切入点表达式 + 告诉办法 /** * 对于切入点表达式的应用阐明 * 粗粒度: * 1.bean(bean的Id) 一个类 * 2.within(包名.类名) 多个类 * 细粒度 */ //@Pointcut("bean(itemCatServiceImpl)") //@Pointcut("within(com.jt.service..*)") //匹配多级目录 @Pointcut("execution(* com.jt.service..*.*(..))") //办法参数级别 public void pointCut(){ //定义切入点表达式 只为了占位 } //区别: pointCut() 示意切入点表达式的援用 实用于多个告诉 共用切入点的状况 // @Before("bean(itemCatServiceImpl)") 实用于单个告诉.不须要复用的 // 定义前置告诉,与切入点表达式进行绑定. 留神绑定的是办法 /** * 需要:获取指标对象的相干信息. * 1.获取指标办法的门路 包名.类名.办法名 * 2.获取指标办法的类型 class * 3.获取传递的参数 * 4.记录以后的执行工夫 */ @Before("pointCut()") //@Before("bean(itemCatServiceImpl)") public void before(JoinPoint joinPoint){ String className = joinPoint.getSignature().getDeclaringTypeName(); String methodName = joinPoint.getSignature().getName(); Class targetClass = joinPoint.getTarget().getClass(); Object[] args = joinPoint.getArgs(); Long runTime = System.currentTimeMillis(); System.out.println("办法门路:" +className+"."+methodName); System.out.println("指标对象类型:" + targetClass); System.out.println("参数:" + Arrays.toString(args)); System.out.println("执行工夫:" + runTime+"毫秒"); } /* @AfterReturning("pointCut()") public void afterReturn(){ System.out.println("我是后置告诉"); } @After("pointCut()") public void after(){ System.out.println("我是最终告诉"); }*/ /** * 盘绕告诉阐明 * 注意事项: * 1.盘绕告诉中必须增加参数ProceedingJoinPoint * 2.ProceedingJoinPoint只能盘绕告诉应用 * 3.ProceedingJoinPoint如果当做参数 则必须位于参数的第一位 */ @Around("pointCut()") public Object around(ProceedingJoinPoint joinPoint){ System.out.println("盘绕告诉开始!!!"); Object result = null; try { result = joinPoint.proceed(); //执行下一个告诉或者指标办法 } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("盘绕告诉完结"); return result; }}`
2 对于AOP实现Redis缓存
2.1 自定义缓存注解
问题: 如何管制 哪些办法须要应用缓存? cacheFind()
解决方案: 采纳自定义注解的模式 进行定义,如果 办法执行须要应用缓存,则标识注解即可.
对于注解的阐明:
1.注解名称 : cacheFind
2.属性参数 :
2.1 key: 应该由用户本人手动增加 个别增加业务名称 之后动静拼接造成惟一的key
2.2 seconds: 用户能够指定数据的超时的工夫
`@Target(ElementType.METHOD) //注解对办法无效@Retention(RetentionPolicy.RUNTIME) //运行期无效public @interface CacheFind { public String preKey(); //用户标识key的前缀. public int seconds() default 0; //如果用户不写示意不须要超时. 如果写了以用户为准.}`
2.2 编辑CacheAOP
`package com.jt.aop;import com.jt.anno.CacheFind;import com.jt.config.JedisConfig;import com.jt.util.ObjectMapperUtil;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.stereotype.Service;import redis.clients.jedis.Jedis;import java.lang.reflect.Method;import java.util.Arrays;@Aspect //我是一个AOP切面类@Component //将类交给spring容器治理public class CacheAOP { @Autowired private Jedis jedis; /** * 切面 = 切入点 + 告诉办法 * 注解相干 + 盘绕告诉 控制目标办法是否执行 * * 难点: * 1.如何获取注解对象 * 2.动静生成key prekey + 用户参数数组 * 3.如何获取办法的返回值类型 */ @Around("@annotation(cacheFind)") public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){ Object result = null; try { //1.拼接redis存储数据的key Object[] args = joinPoint.getArgs(); String key = cacheFind.preKey() +"::" + Arrays.toString(args); //2. 查问redis 之后判断是否有数据 if(jedis.exists(key)){ //redis中有记录,无需执行指标办法 String json = jedis.get(key); //动静获取办法的返回值类型 向上造型 向下造型 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); Class returnType = methodSignature.getReturnType(); result = ObjectMapperUtil.toObj(json,returnType); System.out.println("AOP查问redis缓存"); }else{ //示意数据不存在,须要查询数据库 result = joinPoint.proceed(); //执行指标办法及告诉 //将查问的后果保留到redis中去 String json = ObjectMapperUtil.toJSON(result); //判断数据是否须要超时工夫 if(cacheFind.seconds()>0){ jedis.setex(key,cacheFind.seconds(),json); }else { jedis.set(key, json); } System.out.println("aop执行指标办法查询数据库"); } } catch (Throwable throwable) { throwable.printStackTrace(); } return result; }}`
3 对于Redis 配置阐明
3.1 对于Redis长久化的阐明
redis默认条件下反对数据的长久化操作. 当redis中有数据时会定期将数据保留到磁盘中.当Redis服务器重启时 会依据配置文件读取指定的长久化文件.实现内存数据的复原.
3.2长久化形式介绍
3.2.1 RDB模式
特点:
1.RDB模式是redis的默认的长久化策略.
2.RDB模式记录的是Redis 内存数据的快照. 最新的快照会笼罩之前的内容 所有RDB长久化文件占用空间更小 长久化的效率更高.
3.RDB模式因为是定期长久化 所以可能导致数据的失落.
命令:
- save 要求立刻马上长久化 同步的操作 其余的redis操作会陷入阻塞的状态.
- bgsave 开启后盾运行 异步的操作 因为是异步操作,所以无奈保障rdb文件肯定是最新的须要期待.
配置:
1.长久化文件名称:
2.长久化文件地位
dir ./ 相对路径的写法
dir /usr/local/src/redis 绝对路径写法
3.RDB模式长久化策略
3.2.2 AOF模式
特点:
1.AOF模式默认条件下是敞开的,须要用户手动的开启
- AOF模式是异步的操作 记录的是用户的操作的过程 能够避免用户的数据失落
- 因为AOF模式记录的是程序的运行状态 所以长久化文件绝对较大,复原数据的工夫长.须要人为的优化长久化文件
配置:
3.2.2 对于长久化操作的总结
1.如果不容许数据失落 应用AOF形式
2.如果谋求效率 运行大量数据失落 采纳RDB模式
3.如果既要保障效率 又要保证数据 则应该配置redis的集群 主机应用RDB 从机应用AOF
3.3 对于Redis内存策略
3.3.1 对于内存策略的阐明
阐明:Redis数据的存储都在内存中.如果始终想内存中存储数据 必然会导致内存数据的溢出.
解决形式:
- 尽可能为保留在redis中的数据增加超时工夫.
- 利用算法优化旧的数据.
3.3.2 LRU算法
特点: 最好用的内存优化算法.
LRU是Least Recently Used的缩写,即最近起码应用,是一种罕用的页面置换算法,抉择最近最久未应用的页面予以淘汰。该算法赋予每个页面一个拜访字段,用来记录一个页面自上次被拜访以来所经验的工夫 t,当须淘汰一个页面时,抉择现有页面中其 t 值最大的,即最近起码应用的页面予以淘汰。
维度: 工夫 T
3.3.3 LFU算法
LFU(least frequently used (LFU) page-replacement algorithm)。即最不常常应用页置换算法,要求在页置换时置换援用计数最小的页,因为常常应用的页应该有一个较大的援用次数。然而有些页在开始时应用次数很多,但当前就不再应用,这类页将会长工夫留在内存中,因而能够将引用计数寄存器定时右移一位,造成指数衰减的均匀应用次数。
维度: 应用次数
3.3.4 RANDOM算法
随机删除数据
3.3.5 TTL算法
把设定了超时工夫的数据将要移除的提前删除的算法.
3.3.6 Redis内存数据优化
- 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算法
8.noeviction 如果内存溢出了 则报错返回. 不做任何操作. 默认值
4 对于Redis 缓存面试题
问题形容: 因为海量的用户的申请 如果这时redis服务器呈现问题 则可能导致整个零碎解体.
运行速度:
- tomcat服务器 150-250 之间 JVM调优 1000/秒
- NGINX 3-5万/秒
- REDIS 读 11.2万/秒 写 8.6万/秒 均匀 10万/秒
4.1 缓存穿透
问题形容: 因为用户高并发环境下拜访 数据库中不存在的数据时 ,容易导致缓存穿透.
如何解决: 设定IP限流的操作 nginx中 或者微软服务机制 API网关实现.
4.2 缓存击穿
问题形容: 因为用户高并发环境下, 因为某个数据之前存在于内存中,然而因为非凡起因(数据超时/数据意外删除)导致redis缓存生效. 而使大量的用户的申请间接拜访数据库.
俗语: 趁他病 要他命
如何解决:
1.设定超时工夫时 不要设定雷同的工夫.
2.设定多级缓存
4.3 缓存雪崩
阐明: 因为高并发条件下 有大量的数据生效.导致redis的命中率太低.而使得用户间接拜访数据库(服务器)导致奔溃,称之为缓存雪崩.
解决方案:
1.不要设定雷同的超时工夫 随机数
2.设定多级缓存.
3.进步redis缓存的命中率 调整redis内存优化策略 采纳LRU等算法.