- AOP实现缓存业务
=============
1.1 业务需要
1). 自定义注解 @CacheFind(key=“xxx”,second=-1)
2). 应用自定义注解 标识业务办法 将办法的返回值保留到缓存中.
3). 利用AOP 拦挡注解 利用盘绕告诉办法实现业务
1.2 自定义注解@CacheFind
1.3 注解标识
1.4 编辑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惯例属性
===============
2.1 Redis中长久化策略-RDB
2.1.1 需要阐明
阐明: Redis中将数据都保留到了内存中,然而内存的特点断电及擦除. 为了保障redis中的缓存数据不失落,则须要将内存数据定期进行长久化操作.
长久化: 将内存数据,写到磁盘中.
2.1.2 RDB模式
特点:
1.RDB模式是Redis默认的长久化规定.
2.RDB模式记录的是Redis内存数据快照(只保留最新数据)
3.RDB模式定期长久化(工夫可调) 可能会导致数据失落.
4.RDB模式备份效率是最高的.
5.RDB模式备份阻塞式的 在备份时不容许其余用户操作. 保证数据安全性. save
命令:
1.被动备份 save 会阻塞用户操作
2.后盾备份 bgsave 异步的形式进行长久化操作 不会阻塞.
2.1.3 对于长久化配置
1.save 900 1 900秒内,用户执行了一次更新操作时,那么就长久化一次
2.save 300 10 300秒内,用户执行了10次更新操作. 那么就长久化一次
3.save 60 10000 60秒内,用户执行了10000次的更新操作,则长久化一次.
4.save 1 1 1秒内 1次更新 长久化一次!! 性能特地低.
2.1.4 对于长久化文件名称设定
默认的条件下,长久化文件名称 dump.rdb
2.1.5 文件存储目录
./ 代表以后文件目录. 意义应用绝对路径的写法.
2.2 Redis中长久化策略-AOF
2.2.1 AOF特点
1).AOF模式默认的条件下是敞开状态.须要手动开启.
2).AOF模式记录的是用户的操作过程. 能够实现实时长久化.保证数据不失落.
3).AOF模式保护的长久化文件占用的空间较大.所以长久化效率不高. 并且须要定期的保护长久化文件.
4).AOF模式一旦开启,则redis以AOF模式为主 读取的是AOF文件.
2.2.2 AOF配置
1).开启AOF模式
2).长久化策略
always: 用户更新一次,则长久化一次.
everysec: 每秒长久化一次 效率更高
no: 不被动长久化. 操作系统无关. 简直不必.
2.3 对于Redis面试题
2.3.1 对于flushAll操作
业务场景:
小丽是一个特地丑陋的实习生.你是他的我的项目主管. 因为小丽业务不熟,在生产环境中无心执行了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长久化形式.
2.3.2 单线程Redis为什么快
1).redis运行环境在内存中,纯内存操作.
2).单线程操作 防止频繁的上下文切换. 防止了开关链接的开销.
3).采纳了非阻塞I/O(BIO|NIO) 多路复用的机制(动静感知).
4). Redis最新版本 6.0版本 6.0以前的版本都是单线程操作形式. 6.0当前反对多线程操作形式. (执行时仍旧是单线程操作).
2.4 对于Redis内存优化策略
2.4.1 业务场景
如果频繁应用redis,不停的向其中保留数据,并且不做删除操作,则内存必然溢出. 是否优化内存策略.
是否主动的删除不必的数据,让redis中保留热点数据!!!.
2.4.2 LRU算法
LRU是Least Recently Used的缩写,即最近起码应用,是一种罕用的页面置换算法,抉择最近最久未应用的页面(数据)予以淘汰。该算法赋予每个页面一个拜访字段,用来记录一个页面自上次被拜访以来所经验的工夫 t,当须淘汰一个页面时,抉择现有页面中其 t 值最大的,即最近起码应用的页面予以淘汰。
计算维度: 自上一次以来所经验的工夫T.
阐明:LRU算法是内存优化中最好用的算法.
2.4.3 LFU算法
LFU(least frequently used (LFU) page-replacement algorithm)。即最不常常应用页置换算法,要求在页置换时置换援用计数最小的页,因为常常应用的页应该有一个较大的援用次数。然而有些页在开始时应用次数很多,但当前就不再应用,这类页将会长工夫留在内存中,因而能够将援用计数寄存器定时右移一位,造成指数衰减的均匀应用次数。
维度: 援用次数
常识: 计算机左移 扩充倍数
计算机右移 放大倍数
2.4.4 随机算法
随机删除数据.
2.4.5 TTL算法
阐明:将残余存活工夫排序,将马上要被删除的数据,提前删除.
2.4.6 Redis默认的内存优化策略
阐明1: Redis中采纳的策略定期删除+惰性删除策略
阐明2:
1.定期删除: redis默认每隔100ms 查看是否有过期的key, 查看时随机的形式进行查看.(不是查看所有的数据,因为效率太低.)
问题: 因为数据泛滥,可能抽取时没有被选中.可能呈现 该数据曾经到了超时工夫,然而redis并没有马上删除数据.
- 惰性策略: 当用户获取key的时候,首先检查数据是否曾经过了超时工夫. 如果曾经超时,则删除数据.
问题: 因为数据泛滥, 用户不可能将所有的内存数据都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分片机制
=============
3.1 业务需要
阐明: 单台redis存储的数据容量无限的. 如果须要存储海量的缓存数据,则应用单台redis必定不能满足要求.为了满足数据扩容的需要.则能够采纳分片的机制实现.
3.2 Redis分片机制实现
3.2.1搭建策略
别离筹备3台redis 6379/6380/6381
3.2.2 筹备文件目录
3.2.2 复制配置文件
阐明: 将redis的配置文件放到shards目录中.
批改配置文件端口号 顺次批改6380/6381
启动3台redis:
redis-server 6379.conf
redis-server 6380.conf
redis-server 6381.conf
校验服务器:
3.2.3 Redis分片入门案例
`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")); } }
3.3 一致性hash算法
3.3.1 算法介绍
一致性哈希算法在1997年由麻省理工学院提出,是一种非凡的哈希算法,目标是解决分布式缓存的问题。 [1] 在移除或者增加一个服务器时,可能尽可能小地扭转已存在的服务申请与解决申请服务器之间的映射关系。一致性哈希解决了简略哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动静伸缩等问题 [2] 。
作用: 解决缓存数据,在哪存储的问题…
3.3.2 算法阐明
常识:
- 如果数据雷同,则hash后果必然雷同.
- 常见hash值 由8位16进制数组成. 共用多少种可能性? 2^32
3.3.3 平衡性
①平衡性是指hash的后果应该平均分配到各个节点,这样从算法上解决了负载平衡问题 [4] 。
阐明:通过虚构节点实现数据的均衡
3.3.3 枯燥性
②枯燥性是指在新增或者删减节点时,不影响零碎失常运行 [4] 。
准则: 如果节点新增/缩小 应该尽可能保障原始数据尽可能不变.
3.3.4 分散性
③分散性是指数据应该扩散地寄存在分布式集群中的各个节点(节点本人能够有备份),不用每个节点都存储所有的数据 [4]
将数据扩散存储,即便未来服务器宕机,则影响只是一部分,.而不是全副.
谚语: 鸡蛋不要放到一个篮子里.
3.4 SpringBoot整合Redis分片机制
3.4.1 编辑pro配置文件
3.4.2 编辑配置类
3.4.3 批改AOP中的配置
4 Redis哨兵机制
4.1 Redis分片存在问题
阐明:Redis分片机制,尽管能够实现Redis Redis内存扩容,然而redis 节点并没有实现高可用.如果节点宕机,则整合redis分片将不可应用.
4.2 Redis主从构造搭建
规定: 6379主机 /6380/6381 从机
4.2.1 复制文件目录
4.2.2 删除长久化文件
4.2.3 启动3台Redis服务器
1.redis-server 6379.conf
2.redis-server 6380.conf
3.redis-server 6381.conf
4.2.4 实现redis主从挂载
命令1.: slaveof host port
命令阐明: 在从机中执行上述命令 挂载的是主机的地址.
命令2: info replication
主从构造关系:
4.3 Redis哨兵工作原理
4.3.1 工作流程图
原理阐明:
1.哨兵监控主机的运行的状态. 通过心跳检测机制(PING-PONG)如果间断3次节点没有响应,则判定主机宕机,哨兵开始进行选举.
2.哨兵通过链接主机,获取主机的相干配置信息(蕴含主从构造),筛选链接以后主机的从机.依据随机算法筛选出新的主机. 并且将其余的节点设置为新主机的从.
4.3.2 编辑哨兵配置文件
1).复制哨兵的配置文件
2).敞开保护模式
3).开启后端运行
4).设定哨兵的投票数
5).批改选举的超时工夫
6).批改哨兵的状态
4.3.3 哨兵测试
哨兵命令: redis-sentinel sentinel.conf
查看redis服务:
redis高可用测试:
1.敞开redis主机6379
2.期待10秒 查看6380/6381到底谁是主机.
3.重启6379服务器,查看是否充当了新主机的从
4.3.4 哨兵的入门案例
`@Test public void test01(){ //定义哨兵的汇合信息 Set<String> sentinels = new HashSet<>(); sentinels.add("192.168.126.129:26379"); //定义链接池信息 JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setMaxTotal(200); //链接池 最多200个链接 poolConfig.setMaxIdle(20); //最大闲暇链接数20 poolConfig.setMinIdle(10); //最小闲暇链接数10 JedisSentinelPool pool = new JedisSentinelPool("mymaster",sentinels,poolConfig); //动静获取jedis链接 Jedis jedis = pool.getResource(); jedis.set("abc", "redis赋值操作"); System.out.println(jedis.get("abc")); jedis.close(); }
4.3.5 作业
将哨兵的机制交给SpringBoot治理.
4.4 对于分片/哨兵总结
1.分片机制: 能够实现内存数据的扩容. 然而自身没有实现高可用的成果.
2.哨兵机制: 哨兵能够实现redis节点的高可用.然而哨兵自身没有实现高可用的成果.
需要: 1.不依赖第三方实现高可用
2.实现内存数据的扩容
3.各个节点能够高可用.
- Redis集群搭建 性能蕴含上述的3种机制. 个别公司中都会采纳集群的形式部署redis.
5.Redis集群搭建(重点)
5.1 搭建具体环节参见work文档
5.2 对于redis集群搭建问题阐明
注意事项:
1.启动6个redis节点
2.保障redis节点中的数据都是null的
3.依据报错提醒 排查问题. 查看IP地址 查看防火墙…
1). 敞开所有的Redis服务器
sh stop.sh
2).查看redis配置文件
3).删除多余文件
5.3 对于Redis集群高可用测试
1).敞开7000节点
redis-cli -p 7000 shutdown
2).查看主从状态
3).重启7000 查看状态