首页 > 其他分享 >缓存穿透,缓存击穿,缓存雪崩区别

缓存穿透,缓存击穿,缓存雪崩区别

时间:2023-09-19 22:23:27浏览次数:54  
标签:缓存 return String 击穿 Json 雪崩 key id

缓存穿透,缓存击穿,缓存雪崩区别


发现自己有时候明明已经做过了,可能是缺少回顾总结,过了几天这部分知识就会忘的一干二净,一点有记不住,还有这三个概念有时候确实不太能把他分清楚,面试的时候也有让自己说清楚,说的一塌糊涂,所以总结一下

一、缓存穿透,缓存击穿,缓存雪崩简单的区别

缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会失效,请求都会打到数据库

缓存击穿问题也叫做热点key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问回给瞬间的数据库带来巨大的冲击

缓存雪崩是指同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。

二、缓存穿透

缓存穿透常见的解决方案有两种:

1.缓存空对象

  • 优点:实现简单,维护简单
  • 缺点:额外的内存消耗,可能造成短期的不一致

2.布隆过滤

  • 优点:内存占用较少,没有多余的key
  • 缺点:实现复杂,存在误判的可能

3.解决缓存穿透的代码实现

  public <R,ID> R queryWithPassThrough(
            String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallBack,Long time ,TimeUnit unit){
        //1.查询缓存
        String key = keyPrefix + id;
        String Json = stringRedisTemplate.opsForValue().get(key);
        //2.判断缓存是否存在
        if(StrUtil.isNotBlank(Json)){
            //2.1 如果存在则直接返回数据给用户
            return JSONUtil.toBean(Json,type);
        }
        if(Json != null){
            return null;
        }

        //3.缓存不存在,查询数据库
        R r = dbFallBack.apply(id);
        //3.1如果数据库中也不存在,则返回错误信息
        if(r == null){
            //将一个“”空字符串直接存到redis中
            stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
            return null;
        }
        //4.将数据库中的数据存入redis
       this.set(key,r,time,unit);
        //5.返回
        return r;
    }

三、缓存击穿

缓存击穿有两种常见的解决方法:

1.互斥锁

2.逻辑过期

3.两者的优缺点

4.互斥锁的代码实现

流程图

代码

  public <R,ID>R queryWithMutex(
            String keyprefix,String lockKeyPrefix,ID id,Class<R>type,Function<ID,R>dbFallBack,Long time ,TimeUnit unit)  {
        //1.查询缓存
        String key = keyprefix + id;
        String Json = stringRedisTemplate.opsForValue().get(key);
        //2.判断缓存是否存在
        if(StrUtil.isNotBlank(Json)){
            //2.1 如果存在则直接返回数据给用户
            return JSONUtil.toBean(Json,type);
        }

        if(Json != null){
            //当缓存的内容不是空字符串""的时候
            return null;
        }

        //2.2 缓存不是空字符串"",而是不存在时进行缓存重建
        //3.缓存重建
        //3.1获取互斥锁
        String lockKey = lockKeyPrefix +id;
        R r = null;
        try {
            boolean flag = tryLock(lockKey);
            //3.2判断是否获取互斥锁成功
            if(!flag){
                //获取互斥锁失败
                //3.3失败则休眠并且重试
                Thread.sleep(50);
                return queryWithMutex(keyprefix,lockKeyPrefix,id,type,dbFallBack,time,unit);
            }
            //3.4成功则进行查询数据库
            //4.缓存不存在,查询数据库
            r = dbFallBack.apply(id);


            //模拟重建的延时
            Thread.sleep(200);

            //4.1如果数据库中也不存在,则返回错误信息
            if(r == null){
                stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL,TimeUnit.MINUTES);
                return null;
            }
            //5.将数据库中的数据存入redis
            stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(r),time, unit);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            //6.释放互斥锁
            unLock(lockKey);
        }

        //7.返回
        return r;
    }
/**
     * 开启锁
     * @param key
     * @return
     */
    private boolean tryLock(String key){
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", LOCK_SHOP_TTL, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);
    }

    /**
     * 解锁
     * @param key
     */
    private void unLock(String key){
        stringRedisTemplate.delete(key);
    }

5.逻辑过期的代码实现

流程图

代码

		public <R,ID>R queryWithLogicalExpire(
            String keyprefix,String lockKeyPrefix,ID id,Class<R>type,Function<ID,R>dbFallBack,Long time ,TimeUnit unit){
        //1.查询缓存
        String key = keyprefix + id;
        String Json = stringRedisTemplate.opsForValue().get(key);
        //2.判断缓存是否为空
        if(StrUtil.isBlank(Json)){
            //Json为空,直接返回给用户null
            return null;
        }
        //2.1命中,需要先把json反序列化为对象
        RedisData redisData = JSONUtil.toBean(Json, RedisData.class);
        R r = JSONUtil.toBean((JSONObject) redisData.getData(), type);
        LocalDateTime expireTime = redisData.getExpireTime();
        //2.2判断逻辑时间是否已经过期
        if(expireTime.isAfter(LocalDateTime.now())){
            //expireTime在当前时间之后,说明还没过期
            return r;
        }
        //3当逻辑时间已经过期,需要缓存重建
        //3.1获取互斥锁
        String lockKey = lockKeyPrefix + id;
        boolean flag = tryLock(lockKey);
        //判断是否获取锁成功
        if( flag){
            //开启缓存重建,开启独立线程重建
            CACHE_REBULID_EXECUTOR.submit(()->{
                try {
                    //先查数据库
                    R r1 = dbFallBack.apply(id);
                    //2.封装逻辑过期时间
                    RedisData redisData1=new RedisData();
                    redisData1.setData(r1);
                    redisData1.setExpireTime(LocalDateTime.now().plusSeconds(unit.toSeconds(time)));
                    //再写入Redis
                    stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(redisData));

                } catch (Exception e) {
                    throw new RuntimeException(e);
                }finally {
                    //释放锁
                    unLock(lockKey);
                }
            });
        }
        //4.返回
        return r;
    }
	/**
     * 开启锁
     * @param key
     * @return
     */
    private boolean tryLock(String key){
        Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", LOCK_SHOP_TTL, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(flag);
    }

    /**
     * 解锁
     * @param key
     */
    private void unLock(String key){
        stringRedisTemplate.delete(key);
    }

标签:缓存,return,String,击穿,Json,雪崩,key,id
From: https://www.cnblogs.com/MyBlogZxw/p/17715981.html

相关文章

  • 本地缓存无冕之王Caffeine Cache
    本文已收录至GitHub,推荐阅读......
  • 详解Spring缓存注解@Cacheable、@CachePut和@CacheEvict
    详解Spring缓存注解@Cacheable、@CachePut和@CacheEvict的使用简介在大型的应用程序中,缓存是一项关键技术,用于提高系统的性能和响应速度。Spring框架提供了强大的缓存功能,通过使用缓存注解可以轻松地集成缓存机制到应用程序中。本文将详细介绍Spring框架中的@Cacheable、@CachePu......
  • 详细解释一下redis的缓存击穿、缓存雪崩的原理,以及如何避免?
    缓存击穿和缓存雪崩是两种常见的缓存问题,它们会对系统性能和可用性产生负面影响。以下是对这两个问题的详细解释以及如何避免它们的方法:缓存击穿(CacheMiss)原理:缓存击穿是指在高并发的情况下,多个请求同时访问缓存,但缓存中不存在所需数据。这些请求会穿透缓存,直接访问底层数据库......
  • .NET MVC返回图片并通知浏览器进行缓存
    publicActionResultImage(){stringfilePath="D:\\123.png";Response.Cache.SetExpires(DateTime.Now.AddDays(365));//缓存有效期为365天Response.Cache.SetCacheability(HttpCacheability.Public);//允许任何缓存服务器......
  • Docker 日志缓存过大清理方案(亲测)
    Docker日志缓存过大清理方案(亲测)docker磁盘不足异常现象记录排查并定位问题解决方案参考地址自研产品推荐下载地址:docker磁盘不足异常现象记录docker-composeINTERNALERROR:cannotcreatetemporarydirectory!排查并定位问题1#查看磁盘占用情况[root@iZ25bmxsqmeZ~]#df......
  • Redis缓存穿透,击穿,雪崩问题改如何解决?
    无论在开发过程中还是面试过程中,这三个问题总是被遇到。下面是各个问题的原因和解决方案。缓存穿透原因缓存穿透其实是缓存的单点问题,是指查询一个一定不存在的数据。如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,可能导致DB挂掉......
  • nginx-缓存
    缓存内容并定义缓存的存储位置。proxy_cache_path/var/nginx/cache               keys_zone=CACHE:60m               levels=1:2                        ......
  • ProxmoxVE上用LVM缓存让SSD加速HDD
    在ProxmoxVirtualEnvironment(ProxmoxVE)上使用LVM缓存来加速机械硬盘的步骤如下:注意:在执行这些步骤之前,请务必备份您的数据。1.确保硬件和分区准备好:确保您的系统中有一个SSD设备和一个机械硬盘(HDD)。SSD用于缓存,HDD用于存储。对SSD和HDD分区并创建物理卷(PV),如果尚......
  • 分布式缓存
    集群/垂直架构缓存:1集群架构(1-2):集群架构1:a.所有业务都访问同一个集中式缓存.b.业务是混合进行部署,业务划分不清楚.集群架构2(多个应用单独部署):a.不同的业务做独立的划分,独立的部署.b.每种业务使用独立缓存,从业务层将缓存进行互相隔离,防止互相影响.c.业务项目......
  • .net core 浏览器缓存设置
    1、浏览器缓存设置  [ResponseCache(Duration=20)] 将ResponseCache特性设置在接口方法上就可以了,Duration中的时间是以秒为单位2、服务端缓存设置 a、将app.UseResponseCaching()方法放在app.MapControllers()方法之前  b、如果使用了app.UseCors()方法,要确保app.......