1 AOP实现Redis缓存

1.1 如何了解AOP

名称: 面向切面编程
作用: 升高零碎中代码的耦合性,并且在不扭转原有代码的条件下对原有的办法进行性能的扩大.
公式: AOP = 切入点表达式 + 告诉办法

1.2 告诉类型

1.前置告诉 指标办法执行之前执行
2.后置告诉 指标办法执行之后执行
3.异样告诉 指标办法执行过程中抛出异样时执行
4.最终告诉 无论什么时候都要执行的告诉
特点: 上述的四大告诉类型 不能干涉指标办法是否执行.个别用来做程序运行状态的记录.监控

5.盘绕告诉 在指标办法执行前后都要执行的告诉办法 该办法能够控制目标办法是否运行.joinPoint.proceed(); 性能作为弱小的.

1.3 切入点表达式

了解: 切入点表达式就是一个程序是否进入告诉的一个判断(IF)
作用: 当程序运行过程中 ,满足了切入点表达式时才会去执行告诉办法,实现业务的扩大.
品种(写法):

  1. bean(bean的名称 bean的ID) 只能拦挡具体的某个bean对象 只能匹配一个对象

lg: bean(“itemServiceImpl”)

  1. 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模式因为是定期长久化 所以可能导致数据的失落.

命令:

  1. save 要求立刻马上长久化 同步的操作 其余的redis操作会陷入阻塞的状态.
  2. bgsave 开启后盾运行 异步的操作 因为是异步操作,所以无奈保障rdb文件肯定是最新的须要期待.

配置:
1.长久化文件名称:

2.长久化文件地位
dir ./ 相对路径的写法
dir /usr/local/src/redis 绝对路径写法

3.RDB模式长久化策略

3.2.2 AOF模式

特点:
1.AOF模式默认条件下是敞开的,须要用户手动的开启

  1. AOF模式是异步的操作 记录的是用户的操作的过程 能够避免用户的数据失落
  2. 因为AOF模式记录的是程序的运行状态 所以长久化文件绝对较大,复原数据的工夫长.须要人为的优化长久化文件

配置:

3.2.2 对于长久化操作的总结

1.如果不容许数据失落 应用AOF形式
2.如果谋求效率 运行大量数据失落 采纳RDB模式
3.如果既要保障效率 又要保证数据 则应该配置redis的集群 主机应用RDB 从机应用AOF

3.3 对于Redis内存策略

3.3.1 对于内存策略的阐明

阐明:Redis数据的存储都在内存中.如果始终想内存中存储数据 必然会导致内存数据的溢出.
解决形式:

  1. 尽可能为保留在redis中的数据增加超时工夫.
  2. 利用算法优化旧的数据.

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内存数据优化

  1. 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服务器呈现问题 则可能导致整个零碎解体.
运行速度:

  1. tomcat服务器 150-250 之间 JVM调优 1000/秒
  2. NGINX 3-5万/秒
  3. 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等算法.