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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Java io流文件读取和写入

    Java io流文件读取和写入Javaio流操作demo类1.读取操作/***@authorlxw*@date2020/6/24*@desc获取文件输入流,这里读入内存中*@param[fileName]*@returnbyte[]**/publicbyte[]readPdfFile(StringfileName)throwsException{InputStreamin=null;

    2022年5月7日
    37
  • Delphi考虑sql注入 QuotedStr

    Delphi考虑sql注入 QuotedStr之前只在BS架构的项目中考虑了Sql注入问题,却很少考虑到用了多年的Delphi项目也应该考虑Sql注入的问题,今天做了个实验,成功完成注入,把表里数据全部删除,以后再做Delphi项目还真的考虑这个问题。总体讲,大体知道有两种方式可以避免Delphi中的Sql注入:1、用QuotedStr替代”’进行字符串拼接;2、采用传参数的方式与数据库交互,这种方式哪天再仔细体验一下。 以下为一个小测试,…

    2022年10月18日
    2
  • top命令 详解_top命令的用法

    top命令 详解_top命令的用法top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。下面详细介绍它的使用方法。top是一个动态显示过程,即可以通过用户按键来不断刷新当前状态.如果在前台执行该命令,它将独占前台,直到用户终止该程序为止.比较准确的说,top命令提供了实时的对系统处理器的状态监视.它将显示系统中CPU最“敏感”的任务列表.该命令可以按CPU使用.内存使用和执行时间对任务进行排序;而且该命令的很多特性都可以通过交互式命令或者在个人定制文件中进行设定.1...

    2022年9月25日
    2
  • C#软件开发实例.个人定制自己的屏幕抓图工具(八)加入了截图功能键盘

    C#软件开发实例.个人定制自己的屏幕抓图工具(八)加入了截图功能键盘

    2022年1月5日
    40
  • HttpServlet 实现 MySQL 数据库的插入操作

    HttpServlet 实现 MySQL 数据库的插入操作HttpServlet实现MySQL数据库的插入操作MySQL数据库及表的创建过程,请参考前文《MySQL数据库及数据表的创建》。本文重点来分步进行HttpServlet中,MySQL数据库的插入操作实现。实现步骤1.准备数据库的地址、用户名和密码publicstaticfinalStringurl=”jdbc:mysql://localhost:3306/budaye_test01″;publicstaticfinalStringname=”root

    2025年6月30日
    3
  • Linux-xsync分发脚本

    Linux-xsync分发脚本xsync集群分发脚本(1)需求:循环复制文件到所有节点的相同目录下(2)需求分析:(a)rsync命令原始拷贝:rsync-av/opt/moduleatguigu@hadoop103:/opt/(b)期望脚本:xsync要同步的文件名称(c)期望脚本在任何路径都能使用(脚本放在声明了全局环境变量的路径)[atguigu@hadoop102~]$echo$PATH/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/ho

    2022年5月15日
    31

发表回复

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

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