=============java
1). 自定义注解 @CacheFind(key=“xxx”,second=-1)
2). 使用自定义注解 标识业务方法 将方法的返回值保存到缓存中.
3). 利用AOP 拦截注解 利用环绕通知方法实现业务面试
`package com.jt.aop; import com.jt.anno.CacheFind; 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.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import redis.clients.jedis.Jedis; import java.lang.reflect.Method; import java.util.Arrays; /*@Service @Controller @Repository*/ @Component //组件 将类交给spring容器管理 @Aspect //表示我是一个切面 public class RedisAOP { @Autowired private Jedis jedis; /* * 实现AOP业务调用 * 1.拦截指定的注解 * 2.利用环绕通知实现 * 实现步骤: * 1.获取KEY 必须先获取注解 从注解中获取key? * 2.校验redis中是否有值 * * * 3.知识点补充: * 指定参数名称进行传值,运行期绑定参数类型完成注解的拦截 * joinPoint必须位于参数的第一位. */ @Around("@annotation(cacheFind)") public Object around(ProceedingJoinPoint joinPoint,CacheFind cacheFind){ Object result = null; //key=业务名称::参数 String key = cacheFind.key(); String args = Arrays.toString(joinPoint.getArgs()); key = key + "::" + args; //2.校验是否有值 if(jedis.exists(key)){ 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{ //redis中没有数据,因此须要查询数据库,将数据保存到缓存中 try { 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查询数据库"); } catch (Throwable throwable) { throwable.printStackTrace(); } } return result; } /** * //1.获取key 注解 方法对象 类 方法名称 参数 * Class targetClass = joinPoint.getTarget().getClass(); * //2.获取方法对象 * String methodName = joinPoint.getSignature().getName(); * Object[] args = joinPoint.getArgs(); * Class[] classArgs = new Class[args.length]; * for(int i=0;i<args.length;i++){ * classArgs[i] = args[i].getClass(); * } * try { * //反射实例化对象 * Method method = targetClass.getMethod(methodName,classArgs); * CacheFind cacheFind = method.getAnnotation(CacheFind.class); * String key = cacheFind.key(); * System.out.println(key); * } catch (NoSuchMethodException e) { * e.printStackTrace(); * } */ //公式 aop = 切入点表达式 + 通知方法 //@Pointcut("bean(itemCatServiceImpl)") //@Pointcut("within(com.jt.service.*)") //@Pointcut("execution(* com.jt.service.*.*(..))") //.* 当前包的一级子目录 /* @Pointcut("execution(* com.jt.service..*.*(..))") //..* 当前包的全部的子目录 public void pointCut(){ }*/ //如何获取目标对象的相关参数? //ProceedingJoinPoint is only supported for around advice /* @Before("pointCut()") public void before(JoinPoint joinPoint){ //链接点 Object target = joinPoint.getTarget(); Object[] args = joinPoint.getArgs(); String className = joinPoint.getSignature().getDeclaringTypeName(); String methodName = joinPoint.getSignature().getName(); System.out.println("目标对象:"+target); System.out.println("方法参数:"+Arrays.toString(args)); System.out.println("类名称:"+className); System.out.println("方法名称:"+methodName); }*/ }`
===============redis
说明: Redis中将数据都保存到了内存中,可是内存的特色断电及擦除. 为了保证redis中的缓存数据不丢失,则须要将内存数据按期进行持久化操做.
持久化: 将内存数据,写到磁盘中.算法
特色:
1.RDB模式是Redis默认的持久化规则.
2.RDB模式记录的是Redis内存数据快照(只保留最新数据)
3.RDB模式按期持久化(时间可调) 可能会致使数据丢失.
4.RDB模式备份效率是最高的.
5.RDB模式备份阻塞式的 在备份时不容许其余用户操做. 保证数据安全性.
命令:
1.主动备份 save 会阻塞用户操做
2.后台备份 bgsave 异步的方式进行持久化操做 不会阻塞.spring
1.save 900 1 900秒内,用户执行了一次更新操做时,那么就持久化一次
2.save 300 10 300秒内,用户执行了10次更新操做. 那么就持久化一次
3.save 60 10000 60秒内,用户执行了10000次的更新操做,则持久化一次.
4.save 1 1 1秒内 1次更新 持久化一次!! 性能特别低.数据库
默认的条件下,持久化文件名称 dump.rdbjson
./ 表明当前文件目录. 意义使用绝对路径的写法.api
1).AOF模式默认的条件下是关闭状态.须要手动开启.
2).AOF模式记录的是用户的操做过程. 能够实现实时持久化.保证数据不丢失.
3).AOF模式维护的持久化文件占用的空间较大.因此持久化效率不高. 而且须要按期的维护持久化文件.
4).AOF模式一旦开启,则redis以AOF模式为主 读取的是AOF文件.数组
1).开启AOF模式
2).持久化策略
always: 用户更新一次,则持久化一次.
everysec: 每秒持久化一次 效率更高
no: 不主动持久化. 操做系统有关. 几乎不用.缓存
业务场景:
小丽是一个特别漂亮的实习生.你是他的项目主管. 因为小丽业务不熟,在生产环境中无心执行了flushAll操做. 问如何补救??
场景1: redis中的服务只开启了默认的持久策略 RDB模式.
解决方案:
1.关闭现有的redis服务器.
2.检查RDB文件是否被覆盖. 若是文件没有覆盖.则重启redis便可.(但愿渺茫)
3.若是flushAll命令,同时执行了save操做,则RDB模式无效.
`场景2: redis中的服务开启了AOF模式. 解决方案: 1.关闭redis服务器. 2.编辑redis 持久化文件 将flushAll命令删除. 3.重启redis服务器 通常条件下: RDB模式和AOF模式都会开启. 经过save命令执行rdb持久化方式.`
1).redis运行环境在内存中,纯内存操做.
2).单线程操做 避免频繁的上下文切换. 避免了开关连接的开销.
3).采用了非阻塞I/O(BIO|NIO) 多路复用的机制(动态感知).
4). Redis最新版本 6.0版本 6.0之前的版本都是单线程操做方式. 6.0之后支持多线程操做方式. (执行时依旧是单线程操做).
若是频繁使用redis,不停的向其中保存数据,而且不作删除操做,则内存必然溢出. 可否优化内存策略.
可否自动的删除不用的数据,让redis中保留热点数据!!!.
LRU是Least Recently Used的缩写,即最近最少使用,是一种经常使用的页面置换算法,选择最近最久未使用的页面(数据)予以淘汰。该算法赋予每一个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 t,当须淘汰一个页面时,选择现有页面中其 t 值最大的,即最近最少使用的页面予以淘汰。
计算维度: 自上一次以来所经历的时间T.
说明:LRU算法是内存优化中最好用的算法.
LFU(least frequently used (LFU) page-replacement algorithm)。即最不常用页置换算法,要求在页置换时置换引用计数最小的页,由于常用的页应该有一个较大的引用次数。可是有些页在开始时使用次数不少,但之后就再也不使用,这类页将会长时间留在内存中,所以能够将引用计数寄存器定时右移一位,造成指数衰减的平均使用次数。
维度: 引用次数
常识: 计算机左移 扩大倍数
计算机右移 缩小倍数
随机删除数据.
说明:将剩余存活时间排序,将立刻要被删除的数据,提早删除.
说明1: Redis中采用的策略按期删除+惰性删除策略
说明2:
1.按期删除: redis默认每隔100ms 检查是否有过时的key, 检查时随机的方式进行检查.(不是检查全部的数据,由于效率过低.)
问题: 因为数据众多,可能抽取时没有被选中.可能出现 该数据已经到了超时时间,可是redis并无立刻删除数据.
问题: 因为数据众多, 用户不可能将全部的内存数据都get一遍.必然会出现 须要删除的数据一直保留在内存中的现象.占用内存资源.
3.能够采用上述的内存优化手段,主动的删除.
内存优化算法说明:
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 不主动删除数据,若是内存溢出则报错返回.
=============
说明: 单台redis存储的数据容量有限的. 若是须要存储海量的缓存数据,则使用单台redis确定不能知足要求.为了知足数据扩容的需求.则能够采用分片的机制实现.
分别准备3台redis 6379/6380/6381
说明: 将redis的配置文件放到shards目录中.
修改配置文件端口号 依次修改6380/6381
启动3台redis:
redis-server 6379.conf
redis-server 6380.conf
redis-server 6381.conf
校验服务器:
`package com.jt.test; import org.junit.jupiter.api.Test; import redis.clients.jedis.JedisShardInfo; import redis.clients.jedis.ShardedJedis; import java.util.ArrayList; import java.util.List; public class TestRedisShards { @Test public void testShards(){ List<JedisShardInfo> shards = new ArrayList<>(); shards.add(new JedisShardInfo("192.168.126.129",6379)); shards.add(new JedisShardInfo("192.168.126.129",6380)); shards.add(new JedisShardInfo("192.168.126.129",6381)); ShardedJedis shardedJedis = new ShardedJedis(shards); //3台redis当作1台使用 内存容量扩大3倍. 79/80/81??? shardedJedis.set("shards", "redis分片测试"); System.out.println(shardedJedis.get("shards")); } }
一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。 [1] 在移除或者添加一个服务器时,可以尽量小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题 [2] 。
常识: