Redis进阶

一、Redis缓存作用

Redis在项目中常用作缓存来使用,主要用两大作用:

1.提升系统的性能

Redis基于内存,IO效率远高于MySql数据库

2.减少数据库压力

Redis处理很多请求,使用Redis作为缓存可以减少数据库的请求量,避免数据库因为请求过多、压力过大而导致宕机。

二、Redis的使用流程

1.查询数据

2.先查询Redis,判断是否存在。

3.如果存在就返回数据,如果不存在就查询MySql数据库

4.查询数据库是否存在,如果存在就保存在Redis中,如果存在就返回空的对象给数据。

Redis使用流程图

 三、Redis并发问题介绍

并发问题是大量并发量访问服务器可能导致的问题。

问题原因解决方案
雪崩

1.Redis热点数据同时过期,大量请求全部打在MySql上,mysql宕机。

2.单个Redis服务出现问题或者重启

1.将热点数据过期时间设为随机值,避免同时过期

2.配置Redis集群,结局单点故障的问题

击穿大量并发请求访问Redis同一个数据,还没有向Redis保存,有大量数据同时访问,导致MySql压力很大通过上锁(双检锁)实现线程同步执行
穿透大量请求访问MySQL没有的数据,Redis缓存无法命中,导致数据库压力过大1. 在Redis中保存空对象,给空对象设置过期时间 2. 使用布隆过滤器筛选掉不存在的数据

击穿问题解决案例

解决方法:使用双检锁DCL机制优化方法,通过给代码块加上synchronized锁,同步代码块的方式。再加上if else判断的方式来优化方法,解决击穿的问题,优化性能。

@Override
    public Student getStudentById(Long id) {
        //获得字符串操作对象
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        //先查询Redis
        Student stu = (Student) ops.get(PREFIX + id);
        if(stu == null) {
            synchronized (this) {
                System.out.println("进入了同步锁");
                //先查询Redis
                stu = (Student) ops.get(PREFIX + id);
                //如果Redis缓存存在数据,就直接返回
                if (stu != null) {
                    System.out.println("Redis查到,返回" + stu);
                    return stu;
                }
                //如果Redis没有查到数据,就查询MySQL
                stu = studentMapper.selectById(id);
                //MySQL查到数据,就保存到Redis
                if (stu != null) {
                    System.out.println("MySQL查到,返回" + stu);
                    ops.set(PREFIX + id, stu);
                    return stu;
                }
                //MySQL没有数据,就返回null
                System.out.println("MySQL没有数据,就返回null");
            }
        }else {
            System.out.println("没有执行同步锁");
        }
        return stu;
    }

穿透问题

解决方案1 : 保存空对象设置过期时间

@Override
    public Student getStudentById(Long id) {
        //获得字符串操作对象
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        //先查询Redis,如果存在数据就不执行同步代码块,直接返回
        Student stu = (Student) ops.get(PREFIX + id);
        if(stu == null) {
            synchronized (this) {
                System.out.println("进入了同步锁");
                //先查询Redis
                stu = (Student) ops.get(PREFIX + id);
                //如果Redis缓存存在数据,就直接返回
                if (stu != null) {
                    System.out.println("Redis查到,返回" + stu);
                    return stu;
                }
                //如果Redis没有查到数据,就查询MySQL
                stu = studentMapper.selectById(id);
                //MySQL查到数据,就保存到Redis
                if (stu != null) {
                    System.out.println("MySQL查到,返回" + stu);
                    ops.set(PREFIX + id, stu);
                    return stu;
                }else {
                    //MySQL没有数据,在Redis保存空对象,设置过期时间
                    System.out.println("MySQL没有数据");
                    Student student = new Student();
                    ops.set(PREFIX + id, student,5, TimeUnit.SECONDS);
                }
            }
        }else {
            System.out.println("没有执行同步锁");
        }
        return stu;
    }

解决方法2:使用布隆过滤器。

布隆过滤器:

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

判断不存在的数据一定不存在,判断存在的数据可能不存在。

使用布隆过滤器:

1) 将数据保存到布隆过滤器中

2) 使用布隆过滤器进行判断

 1.先引入redisson依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.16.6</version>
</dependency>

2.创建布隆过滤器

编写配置类

@Configuration
public class RedissonConfig {

    @Bean
    public RBloomFilter<String> bloomFilter(){
        Config config = new Config();
        config.setTransportMode(TransportMode.NIO);
        SingleServerConfig singleServerConfig = config.useSingleServer();
        //可以用"rediss://"来启用SSL连接
        singleServerConfig.setAddress("redis://127.0.0.1:6379");
        RedissonClient redisson = Redisson.create(config);
        //创建布隆过滤器
        RBloomFilter<String> bloomFilter = redisson.getBloomFilter("student-filter");
        //初始化 参数1 向量长度 参数2 误识别率
        bloomFilter.tryInit(10000000L,0.03);
        return bloomFilter;
    }
}

3.创建集合发送请求,将数据ID保存在过滤器中

@Autowired
    private RBloomFilter<String> rBloomFilter;

    /**
     * 初始化布隆过滤器
     * @return
     */
    @GetMapping("init-student-filter")
    public ResponseResult<String> initStudentFilter(){
        List<Student> list = studentService.list();
        //将所有id保存到过滤器中
        list.forEach(student -> {
            rBloomFilter.add(String.valueOf(student.getStuId()));
        });
        return ResponseResult.ok("ok");
    }

4.使用过滤器排除不存在的数据

@Autowired
    private RBloomFilter<String> rBloomFilter;

    @Override
    public Student getStudentById(Long id) {
        //获得字符串操作对象
        ValueOperations<String, Object> ops = redisTemplate.opsForValue();
        //先查询Redis
        Student stu = (Student) ops.get(PREFIX + id);
        if(stu == null) {
            synchronized (this) {
                System.out.println("进入了同步锁");
                //先查询Redis
                stu = (Student) ops.get(PREFIX + id);
                //如果Redis缓存存在数据,就直接返回
                if (stu != null) {
                    System.out.println("Redis查到,返回" + stu);
                    return stu;
                }
                //使用布隆过滤器判断数据库中是否存在该id
                if(rBloomFilter.contains(String.valueOf(id))) {
                    //如果Redis没有查到数据,就查询MySQL
                    stu = studentMapper.selectById(id);
                    //MySQL查到数据,就保存到Redis
                    if (stu != null) {
                        System.out.println("MySQL查到,返回" + stu);
                        ops.set(PREFIX + id, stu);
                        return stu;
                    }
                }else{
                    System.out.println("布隆过滤器判断id数据库不存在,直接返回");
                }
            }
        }else {
            System.out.println("没有执行同步锁");
        }
        return stu;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值