Redis 缓存穿透、缓存雪崩、缓存击穿

这篇具有很好参考价值的文章主要介绍了Redis 缓存穿透、缓存雪崩、缓存击穿。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

缓存穿透

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

常见的解决方案有两种:

        缓存空对象

                 优点:实现简单,维护方便

                缺点: 额外的内存消耗 可能造成短期的不一致(恶意攻击的对象id对应的redis空值缓存失效前成为了新插入的id,造成真实客户只能得到空缓存)

        布隆过滤(类似于位示图0、1代表是否存在)

                优点:内存占用较少,没有多余key

                缺点: 实现复杂 存在误判可能(对于过滤结果:假的一定为假,真的有小概率为假)

Redis 缓存穿透、缓存雪崩、缓存击穿Redis 缓存穿透、缓存雪崩、缓存击穿

 Redis 缓存穿透、缓存雪崩、缓存击穿

 缓存穿透的解决方案有哪些?

缓存null值

布隆过滤

增强id的复杂度,避免被猜测id规律

做好数据的基础格式校验

加强用户权限校验

做好热点参数的限流

代码解释,这是一个通用的工具类方法:

public <R,ID> R queryWithPassThrough(
            String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallback,Long time,TimeUnit unit){
        String key=keyPrefix+id;
        String json = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(json)){
            return JSONUtil.toBean(json,type);
        }
        if (json!=null){
            return null;
        }
        R apply = dbFallback.apply(id);
        if (apply==null){    //生成空值缓存,避免缓存穿透
            stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);  //对象转Json
            return null;
        }
        this.set(key,JSONUtil.toJsonStr(apply),time, unit);
        return apply;
    }

缓存雪崩

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

解决方案:

给不同的Key的TTL添加随机值

利用Redis集群提高服务的可用性

给缓存业务添加降级限流策略

给业务添加多级缓存

缓存击穿

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

常见的解决方案有两种:

互斥锁、逻辑过期

Redis 缓存穿透、缓存雪崩、缓存击穿

Redis 缓存穿透、缓存雪崩、缓存击穿

 Redis 缓存穿透、缓存雪崩、缓存击穿文章来源地址https://www.toymoban.com/news/detail-431363.html

基于代码实现互斥锁

public <R,ID> R queryWithMutex(String keyPrefix,ID id,Class<R> type,Function<ID,R> dbrFallback,Long time,TimeUnit unit){
        String key= keyPrefix+id;
        String shopJson= stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(shopJson)){  //命中Redis缓存
            return JSONUtil.toBean(shopJson, type);
        }
        if(shopJson!=null){     //命中空值缓存,代表恶意攻击
            return null;
        }

        if (!tryLock(LOCK_SHOP_KEY+id)) {
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return queryWithMutex(keyPrefix,id,type,dbrFallback,time,unit);
        }
        String shopJson2= stringRedisTemplate.opsForValue().get(key);
        //doubleCheck
        if (StrUtil.isNotBlank(shopJson2)){  //命中Redis缓存
            unlock(LOCK_SHOP_KEY+id);
            return JSONUtil.toBean(shopJson2, type);
        }
        if(shopJson2!=null){     //命中空值缓存,代表恶意攻击
            unlock(LOCK_SHOP_KEY+id);
            return null;
        }
        R apply = dbrFallback.apply(id);
        if (apply==null){    //生成空值缓存,避免缓存穿透
            stringRedisTemplate.opsForValue().set(key,"",CACHE_NULL_TTL, TimeUnit.MINUTES);  //对象转Json
            return null;
        }
        this.setWithLogicalExpire(key,JSONUtil.toJsonStr(apply),time,unit);
        unlock(LOCK_SHOP_KEY+id);
        return apply;
    }

基于代码实现逻辑过期 

    public <R,ID> R queryWithLogicalExpire(
            String keyPrefix, ID id, Class<R> type, Function<ID,R> dbFallback,Long time,TimeUnit unit){
        String key= CACHE_SHOP_KEY+id;
        String Json= stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isBlank(Json)){  //未命中Redis缓存
            return null;
        }
        RedisData redisData = JSONUtil.toBean(Json, RedisData.class);
        R r = JSONUtil.toBean((JSONObject) redisData.getData(), type);
        if (redisData.getExpireTime().isAfter(LocalDateTime.now())){
            return r;
        }
        if (tryLock(key)){
            String json=stringRedisTemplate.opsForValue().get(key);
            if (StrUtil.isNotBlank(json)){  //命中Redis缓存 二次命中缓存,兜底
                RedisData redisData1 = JSONUtil.toBean(json, RedisData.class);
                if (redisData1.getExpireTime().isAfter(LocalDateTime.now())){
                    unlock(key);
                    return JSONUtil.toBean((JSONObject) redisData1.getData(), type);
                }
            }
            CACHE_REBUILD_EXECUTOR.submit(()->{
                R apply = dbFallback.apply(id);
                this.setWithLogicalExpire(key,apply,time,unit);
                unlock(key);
            });
        }
        return r;
    }

附工具类方法

    public void set(String key, Object value, Long time, TimeUnit util){
        stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value),time,util);
    }
    public void setWithLogicalExpire(String key, Object value, Long time, TimeUnit util){
        RedisData redisData=new RedisData();
        redisData.setData(value);
        redisData.setExpireTime(LocalDateTime.now().plusSeconds(util.toSeconds(time)));
        stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(redisData));
    }
    //线程池
    public static final ExecutorService CACHE_REBUILD_EXECUTOR =             
    Executors.newFixedThreadPool(10);
    /**
     * 获取互斥锁
     * @param key
     * @return
     */
    public Boolean tryLock(String key){
        Boolean aBoolean = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", LOCK_SHOP_TTL, TimeUnit.SECONDS);
        return BooleanUtil.isTrue(aBoolean);
    }

    /**
     * 释放互斥锁
     * @param key
     * @return
     */
    public void unlock(String key){
        stringRedisTemplate.delete(key);
    }

到了这里,关于Redis 缓存穿透、缓存雪崩、缓存击穿的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Redis之缓存穿透+缓存雪崩+缓存击穿

    在生产环境中,会因为很多的原因造成访问请求绕过了缓存,都需要访问数据库持久层,虽然对Redsi缓存服务器不会造成影响,但是数据库的负载就会增大,使缓存的作用降低   缓存穿透是指查询一个根本不存在的数据,缓存层和持久层都不会命中。在日常工作中出于容错

    2023年04月09日
    浏览(50)
  • Redis的缓存穿透,缓存击穿,缓存雪崩

    什么是缓存穿透? 缓存穿透说简单点就是大量请求的 key 是不合理的, 根本不存在于缓存中,也不存在于数据库中 。这就导致这些请求直接到了数据库上,根本没有经过缓存这一层,对数据库造成了巨大的压力,可能直接就被这么多请求弄宕机了。 eg:某个黑客故意制造一

    2024年02月10日
    浏览(24)
  • 68、Redis:缓存雪崩、缓存穿透、缓存击穿

    缓存雪崩是指缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉。 缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生 给每一个缓存数据增加相应的缓存标记,记录缓存是否失效,如果缓存标记失效

    2024年02月16日
    浏览(28)
  • Redis(缓存预热,缓存雪崩,缓存击穿,缓存穿透)

    目录 一、缓存预热 二、缓存雪崩 三、缓存击穿 四、缓存穿透   开过车的都知道,冬天的时候启动我们的小汽车之后不要直接驾驶,先让车子发动机预热一段时间再启动。缓存预热是一样的道理。 缓存预热就是系统启动前,提前将相关的缓存数据直接加载到缓存系统。避免

    2024年02月10日
    浏览(34)
  • Redis 缓存预热+缓存雪崩+缓存击穿+缓存穿透

    面试题: 缓存预热、雪萌、穿透、击穿分别是什么?你遇到过那几个情况? 缓存预热你是怎么做的? 如何造免或者减少缓存雪崩? 穿透和击穿有什么区别?他两是一个意思还是载然不同? 穿适和击穿你有什么解决方案?如何避免? 假如出现了缓存不一致,你有哪些修补方

    2024年02月10日
    浏览(34)
  • Redis缓存预热-缓存穿透-缓存雪崩-缓存击穿

    什么叫缓存穿透? 模拟一个场景: 前端用户发送请求获取数据,后端首先会在缓存Redis中查询,如果能查到数据,则直接返回.如果缓存中查不到数据,则要去数据库查询,如果数据库有,将数据保存到Redis缓存中并且返回用户数据.如果数据库没有则返回null; 这个缓存穿透的问题就是这个

    2024年03月09日
    浏览(26)
  • Redis 缓存穿透击穿和雪崩

             Redis 缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。但同时,它也带来了一些问题。其中,最要害的问题,就是数据的一致性问题,从严格意义上讲,这个问题无解。如果对数据的一致性要求很高,那么就不能使用缓存。 2.1 概念    

    2024年02月10日
    浏览(36)
  • redis缓存雪崩、穿透和击穿

    缓存雪崩   对于系统 A,假设每天高峰期每秒 5000 个请求,本来缓存在高峰期可以扛住每秒 4000 个请求,但是缓存机器意外发生了全盘宕机或者大量缓存集中在某一个时间段失效。缓存挂了,此时 1 秒 5000 个请求全部落数据库,数据库必然扛不住,它会报一下警,然后就挂了

    2024年01月22日
    浏览(29)
  • Redis 缓存雪崩、穿透、击穿、预热

            在实际工程中,Redis 缓存问题常伴随高并发场景出现。例如, 电商大促、活动报名、突发新闻 时,由于缓存失效导致大量请求访问数据库,导致 雪崩 、 击穿 、 穿透 等问题。因此,新系统上线前需 预热 缓存,以应对高并发,减轻数据库压力。本章主要围绕这

    2024年04月12日
    浏览(33)
  • Redis的缓存穿透、缓存击穿和缓存雪崩

    目录 一、解释说明 二、缓存穿透  1. 什么是缓存穿透?  2. 常见的两种解决方案  (1)缓存空对象  (2)布隆过滤 3. 编码解决商品查询的缓存穿透问题 三、缓存击穿  1.  什么是缓存击穿?  2、缓存击穿解决方案(2种) (1)互斥锁 (2)逻辑过期  3.  互斥锁与逻辑过

    2024年02月14日
    浏览(23)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包