Redis缓存穿透、缓存雪崩问题分析

Redis缓存穿透、缓存雪崩问题分析把redis作为缓存使用已经是司空见惯,但是使用redis后也可能会碰到一系列的问题,尤其是数据量很大的时候,经典的几个问题如下:(一)缓存和数据库间数据一致性问题分布式环境下(单机就不用说了)非常容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只能说,如果你的项目对缓存的要求是强一致性的,那么请不要使用缓存。我们只能采取合适的策略来降低缓存和数据库间数据不一致的概率,而无法保证两…

大家好,又见面了,我是你们的朋友全栈君。

把redis作为缓存使用已经是司空见惯,当redis中的数据量起来了以后你就得考虑以下几个问题:

(一)缓存和数据库间数据一致性问题

分布式环境下(单机就不用说了)非常容易出现缓存和数据库间的数据一致性问题,针对这一点的话,只能说,如果你的项目对缓存的要求是强一致性的,那么请不要使用缓存。我们只能采取合适的策略来降低缓存和数据库间数据不一致的概率,而无法保证两者间的强一致性。合适的策略包括 合适的缓存更新策略,更新数据库后要及时更新缓存、缓存失败时增加重试机制,例如MQ模式的消息队列。


()缓存穿透问题

现象:用户大量并发请求的数据(key)对应的数据在redis和数据库中都不存在,导致尽管数据不存在但还是每次都会进行查DB。

为什么key对应数据在缓存和db中不存在还会每次都进行DB查询呢?因为很多开发同学写的代码写的逻辑都是先从redis缓存中查一把,如果缓存中为空则从DB中查,如果DB中查到的数据不为空则设置到缓存并返回给接口。那么问题来了,如果从DB中查询的数据为空呢??

解决方案

  • 从DB中查询出来数据为空,也进行空数据的缓存,避免DB数据为空也每次都进行数据库查询;
  • 使用布隆过滤器,但是会增加一定的复杂度及存在一定的误判率;

bloomfilter就类似于一个hash set,用于快速判某个元素是否存在于集合中,其典型的应用场景就是快速判断一个key是否存在于某容器,不存在就直接返回。布隆过滤器的关键就在于hash算法和容器大小,下面先来简单的实现下看看效果,我这里用guava实现的布隆过滤器:

<dependencies>  
     <dependency>  
         <groupId>com.google.guava</groupId>  
         <artifactId>guava</artifactId>  
         <version>23.0</version>  
     </dependency>  
</dependencies>  
public class BloomFilterTest {

    private static final int capacity = 1000000;
    private static final int key = 999998;

    private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity);

    static {
        for (int i = 0; i < capacity; i++) {
            bloomFilter.put(i);
        }
    }

    public static void main(String[] args) {
        /*返回计算机最精确的时间,单位微妙*/
        long start = System.nanoTime();

        if (bloomFilter.mightContain(key)) {
            System.out.println("成功过滤到" + key);
        }
        long end = System.nanoTime();
        System.out.println("布隆过滤器消耗时间:" + (end - start));
        int sum = 0;
        for (int i = capacity + 20000; i < capacity + 30000; i++) {
            if (bloomFilter.mightContain(i)) {
                sum = sum + 1;
            }
        }
        System.out.println("错判率为:" + sum);
    }
}
成功过滤到999998
布隆过滤器消耗时间:215518
错判率为:318

可以看到,100w个数据中只消耗了约0.2毫秒就匹配到了key,速度足够快。然后模拟了1w个不存在于布隆过滤器中的key,匹配错误率为318/10000,也就是说,出错率大概为3%,跟踪下BloomFilter的源码发现默认的容错率就是0.03:

public static <T> BloomFilter<T> create(Funnel<T> funnel, int expectedInsertions /* n */) {
  return create(funnel, expectedInsertions, 0.03); // FYI, for 3%, we always get 5 hash functions
}

我们可调用BloomFilter的这个方法显式的指定误判率:

Redis缓存穿透、缓存雪崩问题分析

private static BloomFilter<Integer> bloomFilter = BloomFilter.create(Funnels.integerFunnel(), capacity,0.01);

我们断点跟踪下,误判率为0.02和默认的0.03时候的区别:

Redis缓存穿透、缓存雪崩问题分析

Redis缓存穿透、缓存雪崩问题分析

对比两个出错率可以发现,误判率为0.02时数组大小为8142363,0.03时为7298440,误判率降低了0.01,BloomFilter维护的数组大小也减少了843923,可见BloomFilter默认的误判率0.03是设计者权衡系统性能后得出的值。要注意的是,布隆过滤器不支持删除操作。用在这边解决缓存穿透问题就是:

public String getByKey(String key) {
    // 通过key获取value
    String value = redisService.get(key);
    if (StringUtil.isEmpty(value)) {
        if (bloomFilter.mightContain(key)) {
            value = userService.getById(key);
            redisService.set(key, value);
            return value;
        } else {
            return null;
        }
    }
    return value;
}

(三)缓存雪崩问题

现象:大量key同一时间点失效,同时又有大量请求打进来,导致流量直接打在DB上,造成DB不可用。

解决方案

  • 设置key永不失效(热点数据);
  • 设置key缓存失效时候尽可能错开;
  • 使用多级缓存机制,比如同时使用redsi和memcache缓存,请求->redis->memcache->db;
  • 购买第三方可靠性高的Redis云服务器;

books 引申阅读: 使用quartz实现高级定制化定时任务(包含管理界面)

books 推荐阅读:elastic search搜索引擎实战demo:https://github.com/simonsfan/springboot-quartz-demo,分支:feature_es

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/147755.html原文链接:https://javaforall.net

(0)
上一篇 2022年6月29日 下午9:36
下一篇 2022年6月29日 下午9:36


相关推荐

  • linux基本操作命令_vim常用命令

    linux基本操作命令_vim常用命令1.查找文件find/-name*文件名*2.开始、重启、结束进程#开始进程systemctlstartfilebeat#重启systemctlrestartfilebeat#结束systemctlstopfilebeat3.转到目录#从根目录开始搜索文件夹cd/文件名/#从当前目录开始cd文件名/4.编辑文件#编辑vim文件名vi文件名#查看cat文件名5.从编辑状态退出#先按Esc

    2026年4月14日
    4
  • 《Android移动应用基础教程》(Android Studio)(第二版)黑马程序员 课后习题答案

    《Android移动应用基础教程》(Android Studio)(第二版)黑马程序员 课后习题答案《Android移动应用基础教程》(AndroidStudio)(第二版)黑马程序员课后习题答案目录第1章Android基础入门第2章Android常见界面布局第3章Android常见界面控件第4章程序活动单元Activity第5章数据存储第7章使用内容提供者共享数据第8章广播机制第9章服务第10章Android事件处理第11章网络编程第1章Android基础入门一、填空题1、dex2、@color3、AndroidManifest.xml4、LogCat二、判断题

    2022年5月27日
    387
  • 2013.09.18《Win7旗舰版序列号》「建议收藏」

    2013.09.18《Win7旗舰版序列号》「建议收藏」TG664-TJ7YK-2VY3K-4YFY6-BCXF4MVG64-RQDVY-KB9RM-MX9WT-MW824TDTY2-6HJ49-46PCK-6HY88-KQXXXFKDJ2-RCXKD-TFW4H-2PTGK-MMMH8H67R8-4HCH4-WGVKX-GV888-8D79BJ4M92-42VH8-M9JWJ-BR7H6-KTFP676RJX-HDXWD-8BYQJ-GR

    2022年7月20日
    15
  • Nginx-rtmp、FFmpeg实现直播效果并在web页面播放「建议收藏」

    Nginx-rtmp、FFmpeg实现直播效果并在web页面播放「建议收藏」本文参考链接:https://blog.csdn.net/u011424614/article/details/113420000前情提示:本文使用的是windows10系统主要流程讲解1.本文选择的路线是视频文件–>FFmpeg–>nginx–>web播放2.FFmpeg是一个强大的视频编辑软件,基本干视频,音频的多多少少都会用到这个软件。本文中FFmpeg的作用是将视频整成视频流的形式。3.nginx的作用主要是将FFmpeg的视频流进行发布,供web进行访问。4.

    2022年10月7日
    12
  • mysql 读写分离 事务_mysql 读写分离(基础篇)

    mysql 读写分离 事务_mysql 读写分离(基础篇)基本的原理是让主数据库处理事务性查询,而从数据库处理SELECT查询。数据库复制被用来把事务性查询导致的变更同步到集群中的从数据库。JanKneschke在《MySQLProxylearnsR/WSplitting》中详细的介绍了这种技巧以及连接池问题:为了实现读写分离我们需要连接池。我们仅在已打开了到一个后端的一条经过认证的连接的情况下,才切换到该后端。MySQL协议首先进行握手。当…

    2022年6月6日
    42
  • NOIP 2013 解题报告

    NOIP 2013 解题报告noip2013

    2026年3月18日
    1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号