首页 > 数据库 >Redis缓存三剑客

Redis缓存三剑客

时间:2025-01-05 09:30:34浏览次数:3  
标签:缓存 courseId 数据库 Redis course coursePublish CoursePublish 三剑客

为什么会存在缓存三剑客的问题

我们要知道我是引路缓存的主要目的是减少大量对数据库的直接访问请求导致数据库扛不住这么多并发而宕机。如果单单只把数据放入缓存这个操作,在某些情况下还是会发生大量请求打在数据库。

缓存击穿

比如当我们一个热点key(经常被访问的数据)突然过期了,从缓存中获取不到数据就会从数据库中查,如果此时有大量的请求,这些请求都会打在数据库层面而数据库扛不住。

解决方案:

1.很容易想到的就是不给这个热key设置过期时间,让他们永不过期

2.提前通过缓存预热的方式将他们全部写入缓存可以避免大量的请求访问

3.使用分布式锁来控制访问数据库的请求数量。

下面我用代码演示一下第3种方案

    //分布式锁解决缓存击穿问题
    public CoursePublish getCoursePublishCache2(Long courseId) {
        //查询缓存不存在在查数据库
        Object o = redisTemplate.opsForValue().get("course" + courseId);
        if(o!=null){
            String s = o.toString();
            return JSON.parseObject(s, CoursePublish.class);
        }else{
            RLock lock = redissonClient.getLock("coursqueeylock" + courseId);
            lock.lock();
            try {
                //从数据库中查
                CoursePublish coursePublish = getCoursePublish(courseId);
                redisTemplate.opsForValue().set("course"+courseId,JSON.toJSONString(coursePublish),30, TimeUnit.SECONDS);
                return coursePublish;
            }finally {
                //释放锁
                lock.unlock();
            }
        }
    }

当缓存中查询不到,我们就尝试获取锁,再从数据库里面查,这样看似通过锁机制控制了线程访问次数。如果又大量的请求在第一步查缓存的时候都没有查到,那么他们都会执行else语句查询数据库,性能还是不高。

此时我们可以在获的锁之后,再判断一次缓存是否存在,只要某一个请求查询完数据库,那么后面来的请求都会再判断一次缓存从缓存中拿到数据,进一步又减少了数据库访问量。代码如下:

    //分布式锁解决缓存击穿问题
    public CoursePublish getCoursePublishCache2(Long courseId) {
        //查询缓存不存在在查数据库
        Object o = redisTemplate.opsForValue().get("course" + courseId);
        if(o!=null){
            String s = o.toString();
            return JSON.parseObject(s, CoursePublish.class);
        }else{
            RLock lock = redissonClient.getLock("coursqueeylock" + courseId);
            lock.lock();
            try {
                o = redisTemplate.opsForValue().get("course" + courseId);
                if(o!=null) {
                    String s = o.toString();
                    return JSON.parseObject(s, CoursePublish.class);
                }
                //从数据库中查
                CoursePublish coursePublish = getCoursePublish(courseId);
                redisTemplate.opsForValue().set("course"+courseId,JSON.toJSONString(coursePublish),30, TimeUnit.SECONDS);
                return coursePublish;
            }finally {
                //释放锁
                lock.unlock();
            }
        }
    }

缓存穿透

如果用户恶意大量访问一个数据库不存在的值,此时查缓存并不能查到,所有请求又都到了数据库导致数据库负载急剧增加。

解决方案:

1.缓存设为空值,及查询缓存的时候返回的是null。(附上代码理解)

    public CoursePublish getCoursePublishCache2(Long courseId) {
        //查询缓存不存在在查数据库
        Object o = redisTemplate.opsForValue().get("course" + courseId);
        if(o!=null){
            String s = o.toString();
            return JSON.parseObject(s, CoursePublish.class);
        }else{
            if(redisTemplate.hasKey("course" + courseId)){
                throw new RuntimeException("课程不存在");
            }
                //从数据库中查
                CoursePublish coursePublish = getCoursePublish(courseId);
            if(coursePublish==null) {
                redisTemplate.opsForValue().set("course"+courseId,"",30,TimeUnit.SECONDS);
            }else {
                redisTemplate.opsForValue().set("course" + courseId, JSON.toJSONString(coursePublish), 30, TimeUnit.SECONDS);
            }
            return coursePublish;
            }
        
    }

2.使用布隆过滤器过滤掉哪些不存在的值。

以Redis中的布隆过滤器实现为例,Redis中的布隆过滤器底层是一个大型位数组(二进制数组,默认都是0)+多个无偏hash函数,用于判断元素是否再集合中,一般我们再添加数据的时候会将数据的主键啊放入布隆过滤器,布隆过滤器会通过多次hash函数得到一个数组下标并把数组下标的值改为1。当我们需要查询某个元素的是否存在,通过hash得到的值检查对于的位数组下标的值是否为1,如果是则证明该元素存在。

//布隆过滤器解决缓存穿透
    public CoursePublish getCoursePublishCache3(Long courseId) {
        //查询缓存不存在在查数据库
        Object o = redisTemplate.opsForValue().get("course" + courseId);

        if(o!=null){
            String s = o.toString();
            return JSON.parseObject(s, CoursePublish.class);
        }else{
            boolean contains = rBloomFilter.contains(String.valueOf(courseId));
            if(!contains){
                throw new RuntimeException("课程不存在");
            }
            //从数据库中查
            CoursePublish coursePublish = getCoursePublish(courseId);
                redisTemplate.opsForValue().set("course" + courseId, JSON.toJSONString(coursePublish), 30, TimeUnit.SECONDS);
            return coursePublish;
            }
        }

    }

缓存雪崩 

缓存雪崩是指在某一时刻,大量缓存数据同时失效,导致大量请求直接访问数据库,造成数据库压力过大甚至宕机的现象。缓存雪崩的发生通常有两种情况:一是大量热门缓存同时失效,二是缓存服务器宕机

解决方案:

1.过期时间加随机数,为避免缓存同时失效,可以在设置缓存过期时间时,加上一个随机数。例如,过期时间可以设置为原定时间加上1到60秒的随机数。这样即使在高并发情况下,也不会有太多缓存同时失效。

2.针对缓存服务器宕机的情况,可以在系统设计时采用高可用架构。例如,使用Redis的哨兵模式或集群模式,避免单节点故障导致整个缓存服务不可用。哨兵模式下,当某个主节点下线时,会自动将其下的某个从节点提升为主节点,继续处理请求。

如果高可用架构仍然无法避免缓存服务宕机,可以采用服务降级策略。配置一些默认的兜底数据,当检测到缓存服务不可用时,程序可以直接返回这些默认数据1。例如,可以设置一个全局开关,当在一定时间内有多个请求从缓存中获取数据失败时,开启全局开关,后续请求直接从配置中心获取默认数据。

3.请求限流机制是只将少部分请求发送到数据库进行处理,再多的请求就在入口直接拒绝服务,等到 Redis 恢复正常并把缓存预热完后,再解除请求限流的机制。该方案减少对业务的影响

标签:缓存,courseId,数据库,Redis,course,coursePublish,CoursePublish,三剑客
From: https://blog.csdn.net/Liulijie_/article/details/144936280

相关文章

  • 【详解】Spring整合Redis
    目录Spring整合Redis1.环境准备1.1技术栈1.2工具2.添加依赖3.配置Redis4.创建Redis配置类5.使用RedisTemplate6.测试7.总结1.添加依赖2.配置Redis连接3.创建Redis配置类4.创建服务类5.创建控制器6.启动应用7.测试API1.添加依赖2.配置Redi......
  • 面试提问:Redis为什么快?
    Redis为什么快?引言Redis是一个高性能的开源内存数据库,以其快速的读写速度和丰富的数据结构支持而闻名。本文将探讨Redis快速处理数据的原因,帮助大家更好地理解Redis的内部机制和性能优化技术。目录完全基于内存高效的内存数据结构单线程模型I/O多路复用技术简单高效的通......
  • vue中keep-alive从详情页返回列表页缓存,保持原滚动条位置
    项目需求:需要从商品列表页diamond中点击某一个商品进入商品详情页productDetail后,从详情页返回到列表页时需保持原来的滚动位置,并使用keep-alive进行组件缓存实现性能优化1、在路由中设置keepAlive属性默认为true,避免无法及时识别是否需要缓存{path:'/diamond',n......
  • Redis数据库笔记——内存分配器
    大家好,这里是GoodNote,关注公主号:Goodnote,专栏文章私信限时Free。本文详细介绍Redis数据库的内存分配器,这是redis为什么这么快的原因,以及其作为内存数据库的内存管理策略。文章目录Redis的内存分配器内存分配器的作用Redis支持的内存分配器jemalloclibcmalloctc......
  • Redis数据库——内存淘汰机制
    本文详细介绍Redis的8种内存淘汰机制。文章目录过期键删除策略内存淘汰机制内存限制设置常见策略Redis3.0的淘汰机制——近似LRU算法Redis4.0的新增的淘汰机制——LFU算法过期键删除策略Redis为管理内存,对设置了过期时间的键采用了以下三种删除策略......
  • Redis 实现分布式锁
    文章目录引言一、Redis的两种原子操作1.1Redis的原子性1.2单命令1.3Lua脚本1.4对比单命令与Lua脚本二、Redis实现分布式锁2.1分布式锁的概念与需求2.1.1什么是分布式锁?2.1.2分布式锁的常见应用场景2.2基于Redis的分布式锁实现2.2.1锁的获取与释放2.2.2......
  • Spring三级缓存揭秘:为何二级缓存不够用?
    Spring循环依赖需要三级缓存而不是二级,我们可以用一个日常生活中的场景来解释。想象一下,你在一家餐厅里点了两道菜,一道是宫保鸡丁,另一道是麻婆豆腐。但是呢,这两道菜的制作过程中,宫保鸡丁需要用到麻婆豆腐里的豆腐,而麻婆豆腐又需要用到宫保鸡丁里的鸡肉丁。这就好像两个Bean之......
  • docker安装redis5
    1、拉取镜像dockerpullredis:5.02、docker运行容器dockerrun--nameredis5--networksome-network-dredis:5.03、docker-compose3.1、在当前目录下,创建conf目录,并添加redis.conf配置文件redis配置文件,可从:https://mirrors.huaweicloud.com/redis/下载相应的版......
  • Linux缓存管理:如何安全地清理系统缓存
    理解/proc/sys/vm/drop_caches在Linux系统中,/proc/sys/vm/drop_caches是一个用于手动清理系统缓存的内核接口。它可以有以下三个值:1:清理页面缓存2:清理dentries和inodes3:清理页面缓存、dentries和inodes默认情况下,这个值是0,表示系统没有执行任何手动缓存清理操作。为......
  • Go实战全家桶之三十二:指标系统查询加缓存
    测试用例func(self*TestGeneralserviceTestSuite)Test066_CacheQueryFrontSumReportShopl(){varreq=frontdto.NewStatRequest()req.ObjectType=esentity.OBJECT_TYPE_SHOPreq.ShopIds=[]int64{814560415908069377}varret=reportsum.Find......