1. AOP实现redis缓存

1.1 AOP作用

利用AOP能够实现对办法(性能)的扩大.实现代码的解耦.
2.2 切面组成因素
切面 = 切入点表达式 + 告诉办法

1.2 切入点表达式

1).bean(bean的ID) 拦挡bean的所有的办法 具体的某个类 粗粒度的.
2).within(包名.类名) 扫描某个包下的某个类 com.jt.* 粗粒度的.
3).execution(返回值类型 包名.类名.办法名(参数列表)) 细粒度的
4).@annotation(包名.注解名) 细粒度的

1.3 告诉办法

阐明: 告诉相互之间没有程序可言. 每个告诉办法都实现特定的性能,切记AOP的告诉办法与指标办法之间的程序即可.
1).before 指标办法执行前
2).afterReturning 指标办法执行后
3).afterThrowing 指标办法执行抛出异样时执行.
4).after 不论什么状况,最初都要执行的.
上述四大告诉类型,个别用来记录程序的运行状态的.(监控机制)
5).around 指标办法执行前后都要执行.
如果要对程序的运行轨迹产生影响,则首选around.

1.4 AOP入门案例

@Aspect     //标识我是一个切面@Component  //将对象交给spring容器治理  cacheAOPpublic class CacheAOP {    //切面 = 切入点表达式 + 告诉办法    //表达式1: bean(itemCatServiceImpl)  ItemCatServiceImpl类    //@Pointcut("bean(itemCatServiceImpl)")    //@Pointcut("within(com.jt.service.*)")        // .* 一级包下的类   ..* 所有子孙后代的包和类        //返回值类型任意, com.jt.service包下的所有类的add办法参数类型任意类型        //写参数类型时留神类型的大小写    @Pointcut("execution(* com.jt.service..*.*(..))")    public void pointcut(){    }    /**     *     * joinPoint代表连接点对象,个别实用于前四大告诉类型(除around之外的)     *        客人                         路人     */    @Before("pointcut()")    public void before(JoinPoint joinPoint){        //1.获取指标对象        Object target = joinPoint.getTarget();        System.out.println(target);        //2.获取指标对象的门路 包名.类名.办法名        String className = joinPoint.getSignature().getDeclaringTypeName();        String methodName = joinPoint.getSignature().getName();        System.out.println("指标办法的门路:"+(className+"."+methodName));        //3.获取参数类型        System.out.println(Arrays.toString(joinPoint.getArgs()));    }    @Around("pointcut()")    public Object around(ProceedingJoinPoint joinPoint){        System.out.println("盘绕告诉执行");        Object data = null;        try {            data = joinPoint.proceed(); //执行指标办法        } catch (Throwable throwable) {            throwable.printStackTrace();        }        return data;    }}

1.5 业务需要

须要通过自定义注解的模式动静实现缓存操作.通过注解获取其中的key.超时工夫.

1.6 自定义注解的用法

1.7 编辑CacheAOP

package com.jt.aop;import com.jt.annotation.CacheFind;import com.jt.pojo.ItemDesc;import com.jt.util.ObjectMapperUtil;import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.aspectj.lang.annotation.Pointcut;import org.aspectj.lang.reflect.MethodSignature;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.autoconfigure.cache.CacheProperties;import org.springframework.stereotype.Component;import redis.clients.jedis.Jedis;import sun.misc.Cache;import java.util.Arrays;@Aspect     //标识我是一个切面@Component  //将对象交给spring容器治理  cacheAOPpublic class CacheAOP {    @Autowired    private Jedis jedis;    /**     * 实现思路:     *      1.动静获取key   用户自定义的前缀+用户的参数[0,xx]     *      2.判断key是否存在     *             存在: 间接从redis中获取数据 JSON, 须要将json转化为具体对象  依照返回值类型进行转化     *             不存在: 执行指标办法.取得返回值数据. 将返回值后果转化为JSON格局. 之后保留到缓存中.     * @param joinPoint     * @return     */    @Around("@annotation(cacheFind)")    public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind) throws NoSuchMethodException {        Object result = null;        //1.获取key的前缀        String key = cacheFind.key();        //2.获取办法参数        String argString = Arrays.toString(joinPoint.getArgs());        key = key + "::" + argString;        try {             //3.判断缓存中是否有数据            if(jedis.exists(key)){                String json = jedis.get(key);                //5.获取返回值类型                MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();                result = ObjectMapperUtil.toObject(json,methodSignature.getReturnType());                System.out.println("AOP查问redis缓存");            }else{                //示意缓存中没有数据,执行指标办法                result = joinPoint.proceed();                String json = ObjectMapperUtil.toJSON(result);                //4.判断数据中是否有超时工夫                if(cacheFind.seconds()>0){                    jedis.setex(key,cacheFind.seconds(),json);                }else{                    jedis.set(key,json);                }                System.out.println("AOP执行数据库调用!!!!!");            }        } catch (Throwable throwable) {            throwable.printStackTrace();            throw new RuntimeException(throwable);        }        return result;    }

1.8 商品分类名称优化

优化商品分类名称的名称. 在业务层增加缓存注解.

2 Redis属性阐明

2.1 Redis长久化策略

2.1.1 为什么要长久化

Redis中的记录都保留在内存中,如果内存断电或者服务器宕机,则内存数据间接失落.业务中不容许产生. 所以须要将数据定期进行保护.

2.1.2 RDB模式

阐明: RDB模式是Redis的默认的长久化策略.无需手动的开启.
特点:
1.Redis会定期的执行RDB长久化操作. 毛病:可能导致内存数据失落.
2.RDB记录的是内存数据的快照,并且后续的快照会笼罩之前的快照.每次只保留最新数据.效率更高.
命令:
1).save 命令 要求立刻执行长久化操作 save会造成线程的阻塞.
2).bgsave 命令 后盾执行长久化操作 后盾运行不会造成阻塞. 异步操作, 不能保障立刻执行

2.1.3 AOF模式

阐明: AOF模式默认条件下是敞开的,须要手动的开启,如果开启了AOF模式则RDB模式将生效.然而如果手动执行save命令,则也会生成RDB文件.
1).开启AOF模式

特点:
1.AOF模式记录程序的执行的过程.所以能够保证数据不失落.
2.因为AOF记录程序运行的过程,所以整个长久化文件绝对大,所以须要定期维护. 效率低

2.1.4 RDB与AOF模式长久化比照

1).RDB模式
save 900 1 如果在900秒内,执行了一次更新操作则长久化一次
save 300 10
save 60 10000 操作越快 ,长久化的周期越短.
2).AOF模式
appendfsync always 用户执行一次更新操作,则长久化一次 异步操作
appendfsync everysec 每秒操作一次
appendfsync no 不被动操作 个别不必.

2.1.5 对于RDB与AOF总结

策略: 如果数据容许大量失落,首选RDB模式,
如果数据不容许失落则首选AOF模式.
企业策略: 又要满足效率,同时满足数据不失落.
主机: 采纳RDB模式
从机: 采纳AOF模式

2.1.6面试题

题目: 小丽是公司特地丑陋的妹子,误操作将redis服务器执行了flushAll命令,问你作为项目经理如何解决??
A. 申斥一顿,之后HR开革.
B. 秀一下本人的技术,让小丽崇拜 一起过上了幸福的生存
解决方案: 须要将从库中的AOF文件 进行编辑,删除多余的flushAll命令,之后重启redis即可.
问题2: 小丽在执行完上述操作之后,因为好奇 误将aof文件一并删除,问如何解决???
答: 杀人祭天!!!

3. Redis内存策略

3.1 LRU算法

LRU是Least Recently Used的缩写,即最近起码应用,是一种罕用的页面置换算法,抉择最近最久未应用的页面予以淘汰。该算法赋予每个页面一个拜访字段,用来记录一个页面自上次被拜访以来所经验的工夫 t,当须淘汰一个页面时,抉择现有页面中其 t 值最大的,即最近起码应用的页面予以淘汰。
判断维度: 工夫T

3.2 LFU算法

LFU(least frequently used (LFU) page-replacement algorithm)。即最不常常应用页置换算法,要求在页置换时置换援用计数最小的页,因为常常应用的页应该有一个较大的援用次数。然而有些页在开始时应用次数很多,但当前就不再应用,这类页将会长工夫留在内存中,因而能够将援用计数寄存器定时右移一位,造成指数衰减的均匀应用次数。
判断维度: 应用次数

3.3 随机算法

随机算法

3.4 TTL算法

将剩余时间短的数据,提前删除.

3.5 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 不做任何操作,只是返回报错信息.

3.6 对于Redis常见面试题

业务场景: 高并发环境下.用户长时间对服务器进行操作,可能产生如下的问题.

3.3.1 什么是缓存穿透

阐明: 用户高并发环境下拜访数据库缓存中都不存在的数据称之为缓存穿透景象.

解决方案:
1). 禁用IP 限度IP拜访.
2). 限流 每秒最多拜访3次
3). 布隆过滤器

布隆过滤器

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器能够用于检索一个元素是否在一个汇合中。它的长处是空间效率和查问工夫都比个别的算法要好的多,毛病是有肯定的误识别率和删除艰难。
原理:

布隆过滤器优化:
问题:如何解决hash碰撞问题
知识点: 因为hash碰撞问题,可能由多个key有雷同的地位,所以得出结论,布隆过滤器认为数据存在,那么数据可能存在.如果布隆过滤器认为数据不存在,则数据肯定不在.
如何升高hash碰撞的几率:
答:
1.扩容二进制向量位数.
2.减少hash函数的个数
当位数减少/函数适当减少,则能够无效的升高hash碰撞的几率. 默认值 0.03

3.3.2 什么是缓存击穿

阐明: 某个(一个)热点数据在缓存中忽然生效.导致大量的用户间接拜访数据库.导致并发压力过高造成异样.

解决方案:
1.尽可能将热点数据的超时工夫 设定的长一点
2.设定多级缓存 超时工夫采纳随机算法.

3.3.3 什么是缓存雪崩

阐明: 在缓存服务器中,因为大量的缓存数据生效,导致用户拜访的命中率过低.导致间接拜访数据库.
问题剖析:

  1. fluashAll命令可能导致缓存雪崩.
  2. 设定超时工夫时,应该采纳随机算法
  3. 采纳多级缓存能够无效避免.