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 容器治理 cacheAOP
public 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 容器治理 cacheAOP
public 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 的内存优化策略
- volatile-lru 在设定超时工夫的数据中采纳 LRU 算法
- allkeys-lru 所有的数据采纳 LRU 算法删除
- volatile-lfu 设定了超时工夫的数据采纳 LFU 算法删除
- allkeys-lfu 所有数据采纳 LFU 算法删除
- volatile-random 设定了超时工夫的数据采纳随机算法
- allkeys-random 所有数据的随机算法
- volatile-ttl 设定了超时工夫之后采纳 TTL 算法
- 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 什么是缓存雪崩
阐明: 在缓存服务器中, 因为大量的缓存数据生效, 导致用户拜访的命中率过低. 导致间接拜访数据库.
问题剖析:
- fluashAll 命令可能导致缓存雪崩.
- 设定超时工夫时, 应该采纳随机算法
- 采纳多级缓存能够无效避免.