关于redis:第三阶段-Day13-AOP实现Redis缓存-redis基本用法持久化内存优化缓存击穿穿透雪崩

27次阅读

共计 6373 个字符,预计需要花费 16 分钟才能阅读完成。

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 等算法.

正文完
 0