CGB2009-京淘项目DAY13

1. Redis哨兵机制

1.1 分片机制存在的问题

如果redis分片机制其中有一台redis宕机,则整个Redis分片将不能正常的使用. 分片机制没有高可用的效果.

1.2 哨兵机制说明

1.2.1 配置哨兵的前提

说明:实现redis数据同步,是实现哨兵配置的前提条件.

1.2.2 复制文件

在这里插入图片描述

1.2.3 删除多余的持久化文件

在这里插入图片描述
删除完成之后,依次启动redis.如图
在这里插入图片描述

1.2.4 实现主从挂载

主机: 6379
从机: 6380/6381
命令:
1. 检查节点的状态 info replication
在这里插入图片描述
2.主从挂载命令
在这里插入图片描述
3. 主从检查
在这里插入图片描述

1.3 哨兵工作原理

在这里插入图片描述
步骤:
1).当哨兵启动时,会动态的监控主机,之后利用PING-PONG 心跳检测机制,检查主机是否正常.
2).哨兵链接主机之后,获取相关的主从的服务信息. 方便以后选举.
3).当哨兵发现主机宕机,之后采用随机算法 选择新的主机. 之后其他节点当新主机的从.

1.4 哨兵配置

1.4.1 复制文件

在这里插入图片描述

1.4.2 修改哨兵配置文件

1).关闭保护模式
在这里插入图片描述
2).开启后端运行
在这里插入图片描述
3).监控主机
在这里插入图片描述
4. 设定哨兵选举的时间
在这里插入图片描述

1.4.3 哨兵高可用测试

1.启动哨兵
在这里插入图片描述
2.哨兵高可用测试
第一步: 检查redis主机的状态.
第二步: 关闭redis主机.
第三步: 等待10秒 检查从机是否切换为主机.
在这里插入图片描述

第四步: 重启主机,检查是否为新主机的从.
在这里插入图片描述

1.4.4 哨兵入门案例

 /**
     * 实现redis哨兵测试
     */
    @Test
    public void testSentinel(){
        //1.链接哨兵的集合
        Set<String> sentinels = new HashSet<>();
        sentinels.add("192.168.126.129:26379");
        JedisSentinelPool pool
                = new JedisSentinelPool("mymaster",sentinels);
        Jedis jedis = pool.getResource();
        jedis.set("aaa", "aaaaaaa");
        System.out.println(jedis.get("aaa"));
    }

1.5 SpringBoot整合哨兵

1.5.1 编辑pro配置文件

在这里插入图片描述

1.5.2 编辑配置类

package com.jt.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.*;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Configuration  //标识我是一个配置类   一般和@Bean注解联用
@PropertySource("classpath:/redis.properties")
public class RedisConfig {

    //配置哨兵机制
    @Value("${redis.sentinel}")
    public String sentinel;

    @Bean
    public JedisSentinelPool jedisSentinelPool(){
        //1.设定连接池大小
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMinIdle(5);    //设定最小的空闲数量
        poolConfig.setMaxIdle(10);   //设定最大空闲数量
        poolConfig.setMaxTotal(100); //最大链接数

        //2.链接哨兵的集合
        Set<String> sentinels = new HashSet<>();
        sentinels.add(sentinel);
        return new JedisSentinelPool("mymaster",sentinels,poolConfig);
    }


   /* @Value("${redis.nodes}")
    private String nodes;   //指定分片节点  node,node,node.....

    @Bean
    public ShardedJedis shardedJedis(){
        List<JedisShardInfo> shards = new ArrayList<>();
        String[] nodeArray = nodes.split(",");
        for (String node : nodeArray){  //node=host:port
            String host = node.split(":")[0];                   //获取节点IP地址
            int port = Integer.parseInt(node.split(":")[1]);    //获取节点端口号
            JedisShardInfo jedisShardInfo = new JedisShardInfo(host,port);
            shards.add(jedisShardInfo);
        }
        return new ShardedJedis(shards);
    }*/

   /* @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private Integer port;

    @Bean
    public Jedis jedis(){
        return new Jedis(host,port);
    }*/
}

1.5.3 编辑CacheAOP

在这里插入图片描述

package com.jt.aop;

import com.jt.annotation.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 redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import redis.clients.jedis.ShardedJedis;


import java.util.Arrays;

@Component      //交给Spring容器管理
@Aspect         //标识AOP切面类
public class RedisAOP {

    @Autowired
    private JedisSentinelPool sentinelPool;
    //private ShardedJedis jedis;
    //private Jedis jedis;

    //1. 定义切入点表达式     2.定义通知方法


    /**
     * 实现AOP缓存:
     *      1.准备key = 获取key的前缀 + "动态拼接参数"
     *      2.从redis中获取数据
     *              结果1:  没有数据,查询数据库,之后将数据保存到缓存中
     *              结果2:  有数据,  直接将缓存数据返回
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("@annotation(cacheFind)")
    public Object around(ProceedingJoinPoint joinPoint, CacheFind cacheFind) throws Throwable {
        //从池中, 动态获取数据
        Jedis jedis = sentinelPool.getResource();

        String perkey = cacheFind.key();
        String args = Arrays.toString(joinPoint.getArgs());
        String key = perkey + "::" + args;
        Object result = null;
        //2.判断redis中是否有数据
        if(jedis.exists(key)){
           String json = jedis.get(key);
           //利用工具API动态获取返回值类型
           MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
           Class returnType = methodSignature.getReturnType();
           //如果采用object.class形式 则只能转化简单对象,不能转化嵌套对象
           result = ObjectMapperUtil.toObj(json,returnType);
           System.out.println("AOP查询Redis缓存!!!");
        }else{
            //表示缓存中没有数据,应该查询数据库动态获取
            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查询数据库!!!");
        }

        jedis.close();  //关闭链接
        return result;
    }










    //1.定义切入点表达式
    //@Pointcut("bean(itemCatServiceImpl)")
    //@Pointcut("within(com.jt.service.*)")  //按照某个类匹配
   /* @Pointcut("execution(* com.jt.service.*.*(..))")
    public void pointCut(){

    }

    //joinPoint连接点???????
    @Before("pointCut()")
    public void before(JoinPoint joinPoint){
        Class targetClass = joinPoint.getTarget().getClass();
        Object[] args = joinPoint.getArgs();
        String methodName = joinPoint.getSignature().getName();
        String ClassName = joinPoint.getSignature().getDeclaringTypeName();
        System.out.println("获取目标对象的类型:"+targetClass);
        System.out.println("获取目标参数:"+ Arrays.toString(args));
        System.out.println("获取目标方法名称:"+ methodName);
        System.out.println("获取目标类的路径:"+ ClassName);
        System.out.println("我是前置通知!!!");
    }*/
}

1.5.4 分片哨兵总结

1.分片主要实现了内存扩容, 没有高可用的效果.
2.哨兵主要实现了高可用效果, 没有实现内存数据的扩容. 哨兵本身没有高可用的效果.
如何优化: 内存扩容,节点实现高可用 redis集群实现.

2.Redis集群

2.1 关于Redis集群失败说明

如果redis集群搭建失败,则按照如下的步骤完成配置, 前提条件: 搭建集群节点必须为null
1).关闭所有的redis节点
2).删除多余的配置文件 dump.rdb/nodes.conf
在这里插入图片描述
3).检查redis.conf配置文件 参考文档…
4).重启redis节点
5).执行搭建命令

2.2 Redis分区算法

具体详情参见文档.

2.3 面试题

1).Redis集群中由于有16384个槽位,所有redis集群中只能存储16384个key? B错误
结论: 分区只是分片了数据归谁管理 到底能存储多少由内存大小决定.
hash(key1)%16384 = 2000
hash(key2)%16384 = 2000

2).通过redis-cli -p 7000-7005 链接任意的节点都可以执行set操作 B 错误 从库不能写
3).通过redis-cli -p 7000-7005 链接任意的主机都可以执行set操作 B 错误 数据存储严格按照分区算法完成.
4).redis集群中最多能够支持多少台主机? 16384台
5).redis集群一旦搭建,如果redis节点全部关机再次重启时,需要重新搭建集群? B 错误 集群的状态都已经写入nodes.conf文件中.所以重启之后集群恢复.

知识点: Redis集群崩溃的条件是主机缺失集群崩溃
6).如果有1主1从共3组组成了redis集群. 问题: redis节点至少宕机几台.集群崩溃?? 2台…
7).如果有1主2从共3组组成了redis集群. 问题: redis节点至少宕机几台.集群崩溃?? 5台

2.4 SpringBoot整合Redis集群

2.4.1 编辑pro配置文件

#设定redis数据
#redis.host=192.168.126.129
#redis.port=6379

# 配置redis分片机制
#redis.nodes=192.168.126.129:6379,192.168.126.129:6380,192.168.126.129:6381

#配置redis哨兵机制
#redis.sentinel=192.168.126.129:26379

#配置redis集群
redis.nodes=192.168.126.129:7000,192.168.126.129:7001,192.168.126.129:7002,192.168.126.129:7003,192.168.126.129:7004,192.168.126.129:7005

2.4.2 编辑配置类

package com.jt.config;

import org.apache.catalina.Host;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import redis.clients.jedis.*;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

@Configuration  //标识我是一个配置类   一般和@Bean注解联用
@PropertySource("classpath:/redis.properties")
public class RedisConfig {
    @Value("${redis.nodes}")
    private String nodes;   //node,node,node.....

    @Bean
    public JedisCluster jedisCluster(){
        Set<HostAndPort> nodeSet = new HashSet<>();
        String[] nodeArray = nodes.split(",");
        for (String node : nodeArray){
            String host = node.split(":")[0];
            int port = Integer.parseInt(node.split(":")[1]);
            HostAndPort hostAndPort = new HostAndPort(host, port);
            nodeSet.add(hostAndPort);
        }
        return new JedisCluster(nodeSet);
    }


   /* //配置哨兵机制
    @Value("${redis.sentinel}")
    public String sentinel;

    @Bean
    public JedisSentinelPool jedisSentinelPool(){
        //1.设定连接池大小
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMinIdle(5);    //设定最小的空闲数量
        poolConfig.setMaxIdle(10);   //设定最大空闲数量
        poolConfig.setMaxTotal(100); //最大链接数

        //2.链接哨兵的集合
        Set<String> sentinels = new HashSet<>();
        sentinels.add(sentinel);
        return new JedisSentinelPool("mymaster",sentinels,poolConfig);
    }*/


   /* @Value("${redis.nodes}")
    private String nodes;   //指定分片节点  node,node,node.....

    @Bean
    public ShardedJedis shardedJedis(){
        List<JedisShardInfo> shards = new ArrayList<>();
        String[] nodeArray = nodes.split(",");
        for (String node : nodeArray){  //node=host:port
            String host = node.split(":")[0];                   //获取节点IP地址
            int port = Integer.parseInt(node.split(":")[1]);    //获取节点端口号
            JedisShardInfo jedisShardInfo = new JedisShardInfo(host,port);
            shards.add(jedisShardInfo);
        }
        return new ShardedJedis(shards);
    }*/

   /* @Value("${redis.host}")
    private String host;
    @Value("${redis.port}")
    private Integer port;

    @Bean
    public Jedis jedis(){
        return new Jedis(host,port);
    }*/
}

2.4.3 编辑CacheAOP

在这里插入图片描述

2.5 关于Redis面试问题(二)

2.5.1 什么是缓存穿透

概念: 在高并发环境下,用户长时间访问数据库中不存在的数据,称之为缓存穿透.
原理:
在这里插入图片描述
解决方案:
1. IP限流 单位时间内设定IP的请求的次数.
2. 布隆过滤器

2.5.1.0 关于计算机进制换算

1 B/byte(字节) = 8 bit(比特)(二进制位) 。

1 KB(千字节) = 1024 B/byte(字节) 。

1 MB = 1024 KB 。

1 GB = 1024 MB 。

1TB =1024 GB 。

1 PB = 1024 TB 。

1 EB = 1024 PB。

2.5.1.1 布隆过滤器介绍

布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都比一般的算法要好的多,缺点是有一定的误识别率和删除困难。

2.5.1.2 布隆过滤器算法

在这里插入图片描述

2.5.1.3 算法优化

说明:由于hahs碰撞带来的问题.所以需要对算法进行优化 降低hash碰撞的概率.
解决方案:
1. 增加二进制向量的位数. 8/16/32/1024…
2. 增加hash函数的个数.

总结: 适当的增加二进制向量的位数和hash函数个数,可以有效的降低hash碰撞的概率
guva 包 谷歌大神手写布隆过滤器算法…
在这里插入图片描述

2.5.2 什么是缓存击穿

2.5.3 什么是缓存雪崩

  • 11
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闪耀太阳

感觉文章不错的记得打赏呀

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值