我的编程空间,编程开发者的网络收藏夹
学习永远不晚

面试官:你知道缓存击穿、缓存穿透、缓存雪崩吗?

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

面试官:你知道缓存击穿、缓存穿透、缓存雪崩吗?

面试题剖析

花里胡哨的名词

刚开始我以为“缓存击穿、缓存穿透、缓存雪崩”说的是3个问题,在各个博客以及视频的讲解下越来越绕。最后我捋了一下,这TM不是一个问题吗。

为了让大家也绕一绕,我把各博客对“缓存击穿、缓存穿透、缓存雪崩”的描述贴在这里:

缓存击穿是指一个热点的Key在某个瞬间过期失效了,大量的并发请求在缓存获取不到数据后直接请求数据库的现象。

缓存穿透是指查询一个根本不存在的数据,缓存和数据库都不会命中,导致每次请求都要到数据库去查询。

缓存雪崩指的是缓存由于宕机或者某些原因不能提供服务,导致所有的请求去访问数据库,造成数据库查询压力骤增从而宕机。

透过现象看本质

我就非常不理解了,为什么把缓存带来的一个问题分好几个场景去描述,还这解决方案,那解决方案的,花里胡哨的增加了大家的理解难度。

在我看来“缓存击穿、缓存穿透,缓存雪崩”都是在说一个问题,那就是:

缓存没命中,请求落到数据库了

而“缓存雪崩”才突出了问题的本质:

没有缓存的缓冲,数据库承受不了那么大的压力,可能会造成宕机等问题。

仔细想想是不是这样?“缓存击穿、缓存穿透、缓存雪崩”最终的描述都是请求落到数据库了,只不过场景不同罢了。但不论哪种场景,在并发高的情况下都会给数据库带来压力。

所以,一个问题分这么多场景,引出这么多名词,我认为就是在增加大家的理解难度。

面试题解决方案

有问题就会有解决方案,既然看了这篇文章就不要死记硬背了,不然过段时间又会忘记,跟着思路顺其自然的理解。

透过现象看本质

对于以上的几个场景,要解决的问题就是:

如何提高缓存命中率。

也就是尽量避免请求打到数据库中,尤其是高并发的请求。主要涉及两个层面:

  1. 缓存组件要可靠:首先要确保缓存组件足够可靠。
  2. 代码逻辑要严谨:在编写代码使用缓存时尽量要把各种场景考虑进去,把问题当作功能的一部分。

像“缓存击穿、缓存穿透”问题的产生都属于代码逻辑不严谨。热点Key怎么能突然消失呢?一个相同的请求怎么能并发访问到数据库呢?怎么能允许一个不存在的数据一直请求呢?这些问题在我看来都是不应该发生的。

接下来就针对引起“缓存击穿、缓存穿透、缓存雪崩”的几个问题进行剖析解决。

提高缓存命中率一:完美处理热点Key的消失

热点数据通常分为可控和不可控。拿电商系统来讲,商品分类属于可控,因为基本上这类数据是通过后台配置的。而一些商品可能会因为某个原因突然爆火成为热点数据,这类数据属于不可控。

不论可控或不可控,热点数据不可以突然就消失,所以在缓存时要有对应的策略。

  • 像商品分类这类数据就可以不设置过期时间。
  • 而像不可控的热点数据,要靠一些策略避免其过期,比如通过“看门狗”方式监控热点Key,快过期时进行“续命”。

可以都不设置过期时间,让淘汰策略去淘汰数据吗?

非常不建议。

之前生产环境曾遇到过一个问题:用户每次登录之后会莫名其妙退出。经过排查发现,原来是因为Redis服务容量不足,所以最近登录生成的token一直被淘汰。

虽然没有报错,但是给用户带来不好的体验,对产品造成非常不好的影响。

当然,避免不了热点Key被人为删除或者其他恶意破坏,当发生这种情况怎么办?

如果热点Key不存在缓存中,势必要去数据库中查询了。此时,如果并发请求过高,一定不能让所有请求打到数据库,可以对该key进行加锁处理,获取到锁的请求去数据库访问并缓存,其他请求则等待该key缓存后再访问缓存。

因为平时写代码会很自然考虑到这一点,所以这也是为什么我刚开始一直不理解“缓存击穿”这样的问题。

提高缓存命中率二:避免查询不存在的数据

造成“查询不存在的数据”的原因要么是代码或数据出现问题,要么是遭到恶意的攻击造成的空命中。总之,这种情况无法完全避免。

但是,我们知道哪些数据会被缓存。这样的话,我们可以将这些数据放在一个“大集合”中,当请求的数据不存在这个“大集合”时,直接返回NULL即可。

那么问题来了:这个“大集合”放在哪里?肯定不能是数据库,但是内存容量又是有限的。怎么办?

有一个叫布隆过滤器的数据结构可以解决这个问题。其主要用于检测一个元素是否在一个集合里,其原理是:数据通过一组哈希函数映射到位图中,不论该元素多大都只需要占用1位,从而节省大量空间。如下图

布隆过滤器原理

这样的话,我就可以将要缓存的数据先放在布隆过滤器中,当查询的数据不在布隆过滤器时就可以直接返回NULL了。

感兴趣的可以看下 面试官:如何在海量数据中快速检测某个数据

提高缓存命中率三:降低缓存服务的不可用

降低缓存服务的不可用也就是提高缓存服务的可用性,也就是Redis的高可用,这个没有什么逻辑就不展开了。

面试题案例

模拟案例

现在,通过代码模拟一个因“缓存击穿、缓存穿透、缓存雪崩”,请求并发到MySQL服务上,看会发生什么事。

服务器环境:1核1G

编程语言:Java

案例代码

public class MainTest {
    private static final String DB_URL = "jdbc:mysql://127.0.0.1:3306/test";
    private static final String USER = "root";
    private static final String PASS = "Mysql123.";

    public static void main(String[] args) throws InterruptedException {

        Timer timer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                QueryTask.cacheExist = false;
            }
        };
        timer.schedule(task, 60 * 1000);

        while (true) {

            ExecutorService executorService = Executors.newFixedThreadPool(1500);
            for (int i = 0; i <1500 ; i++) {
                executorService.submit(new MainTest.QueryTask());
                System.gc();
            }
        }
    }

    static class QueryTask implements Runnable {
        static boolean cacheExist = true;

        @Override
        public void run() {
            try {

                if (cacheExist) {
                    System.out.println("访问缓存");
                } else {
                    Class.forName("com.mysql.jdbc.Driver");
                    Connection conn = DriverManager.getConnection(DB_URL, USER, PASS);
                    Statement statement = conn.createStatement();
                    Thread.sleep(3000);
                    String query = "SELECT * FROM test_cache";
                    ResultSet rs = statement.executeQuery(query);
                    while (rs.next()) {
                        int id = rs.getInt("id");
                        String value = rs.getString("value");
                        System.out.println("ID: " + id + ", Value: " + value);
                    }

                    rs.close();
                    statement.close();
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

上面的代码主要做了两件事:

  1. 模拟1500个线程去查询数据。cacheExist为true时访问缓存,为false时去请求数据库。
  2. 通过定时任务在1分钟后将cacheExist设置为false。各位就想象成热点Key的突然消失、查询不存在的数据、redis的宕机。

案例执行效果

代码在执行1分钟后就会报下面的错误信息:

com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data source rejected establishment of connection,  message from server: "Too many connections"

这是因为MySQL最大连接数只有151,远远低于并发线程数1500。

mysql> show variables like '%max_connections%';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 151   |
+-----------------+-------+

此时,我将MySQL最大连接数设置为1500。

mysql> SET GLOBAL max_connections = 1500;
Query OK, 0 rows affected (0.00 sec)

mysql> show variables like '%max_connections%';
+-----------------+-------+
| Variable_name   | Value |
+-----------------+-------+
| max_connections | 1500  |
+-----------------+-------+

现在执行 SHOW STATUS LIKE 'Threads_connected' 去查看MySQL连接线程数会发现数值突然升高,当连接数为1283 左右时,就会发现MySQL服务已经断开连接或者服务器宕机,也就是缓存雪崩的效果。

图片

MySQL压力过高宕机

总结

面试时不要被花里胡哨的问题迷惑住,要思考一下问题的本质。

“缓存击穿、缓存穿透、缓存雪崩”问题的本质就是:

当缓存没命中或失效,并发的请求打到数据库怎么办?

通过上面的描述,此类问题要有以下考虑:

  1. 提高缓存命中率。比如,要解决热点Key的突然消失、要避免查询不存在的数据等。
  2. 数据库并发请求要设置合理。太低了浪费资源,太高了就会出现MySQL服务宕机情况。

本文转载自微信公众号「Hi程序员」,可以通过以下二维码关注。转载本文请联系Hi程序员公众号。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

面试官:你知道缓存击穿、缓存穿透、缓存雪崩吗?

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

面试官:你知道缓存击穿、缓存穿透、缓存雪崩吗?

现在执行 SHOW STATUS LIKE \'Threads_connected\'​ 去查看MySQL连接线程数会发现数值突然升高,当连接数为1283 左右时,就会发现MySQL服务已经断开连接或者服务器宕机,也就是缓存雪崩的效果。

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

使用redis作为缓存时,存在一些应用问题,包括缓存穿透、缓存击穿、缓存雪崩。 Redis缓存穿透、缓存击穿缓存雪崩redis常被用于作为后台数据库的缓存,缓存一些热点访问数据,根据局部性原理,缓存能够处理大部分请求。当请求数据未命中缓存时,才会引起对数
Redis缓存穿透、缓存击穿、缓存雪崩
2014-07-07

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

正常情况下,一个请求过来,首先判断key是否存在,如果key存在,直接返回;如果key不存在或者已过期,查询数据库,如果数据库中存在数据,则更新缓存并返回数据;如果不存在,则直接返回空。

大厂面试Redis:缓存雪崩、缓存穿透、缓存击穿

眼光不错,小伙子,看到这篇文章了就血赚,这篇文章绝对让你学到开心,这是面试的杀器,其实Redis这个东西吧,我个人认为,真的真的很强大,但是呢,又感觉被吹得有点过头了。

看完Redis缓存穿透、缓存击穿、缓存雪崩来吊打面试官!

今天我们深入具体的讨论了Redis缓存穿透、缓存击穿、缓存雪崩的产生原因和解决方案,补充了缓存污染和缓存一致性。

redis的缓存雪崩、缓存穿透和缓存击穿

缓存雪崩:比如给缓存中的key设置了统一的过期时间,而在过期时间点,有大量的请求进来,这个时候redis中没有用户请求的资源,所以所有的请求会全部拥到数据库,如果数据库有报警监测的话,可能会报一下警,然后数据库就挂掉了。如果这时候把数据重新起来,redis上还
2017-11-21

一篇学会缓存穿透、缓存击穿、缓存雪崩

当查询数据库和缓存都无数据时,因为数据库查询无数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库,这种情况就叫做缓存穿透。

Redis缓存击穿、缓存穿透、缓存雪崩如何解决

本篇内容介绍了“Redis缓存击穿、缓存穿透、缓存雪崩如何解决”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Redis缓存使用场景Redis
2023-07-05

redis缓存雪崩、缓存击穿和缓存穿透是什么

这篇文章主要介绍了redis缓存雪崩、缓存击穿和缓存穿透是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇redis缓存雪崩、缓存击穿和缓存穿透是什么文章都会有所收获,下面我们一起来看看吧。缓存雪崩首先来看看
2022-11-30

缓存穿透,缓存击穿,缓存雪崩解决方案分析

阅读本文大概需要 3.7 分钟。一、前言设计一个缓存系统,不得不要考虑的问题就是:缓存穿透、缓存击穿与失效时的雪崩效应。二、缓存穿透缓存穿透是指查询一个一定不存在的数据,由于缓存是不命中时被动写的,并且出于容错考虑,如果从存储层查不到数据则
2023-06-05

总结一下Redis的缓存雪崩、缓存击穿、缓存穿透

Redis的使用,可以有效地提高系统的性能和可用性。但是在使用过程中,需要注意缓存击穿、缓存穿透和缓存雪崩等问题,采用适当的解决方案来避免这些问题的发生,从而保证系统的稳定性和可靠性。

缓存击穿、穿透、雪崩专项测试

R2M缓存的使用,极大的提升了应用程序的性能和效率,特别是数据查询方面。而缓存最常见的问题是缓存穿透、击穿和雪崩,在高并发下这三种情况都会有大量请求落到数据库,导致数据库资源占满,引起数据库故障。

缓存穿透、缓存击穿、缓存雪崩区别和解决方案

前台请求,后台先从缓存中取数据,取到直接返回结果,取不到时从数据库中取,数据库取到更新缓存,并返回结果,数据库也没取到,那直接返回空结果。
数据库缓存2024-11-30

JAVA缓存击穿、缓存穿透、缓存雪崩的区别有哪些

这篇文章主要介绍“JAVA缓存击穿、缓存穿透、缓存雪崩的区别有哪些”,在日常操作中,相信很多人在JAVA缓存击穿、缓存穿透、缓存雪崩的区别有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”JAVA缓存击穿、
2023-06-25

Redis系列(六)Redis 的缓存穿透、缓存击穿和缓存雪崩

NoSQL 开发中或多或少都会用到,也是面试必问知识点。最近这几天的面试每一场都问到了。但是感觉回答的并不好,还有很多需要梳理的知识点。这里通过几篇 Redis 笔记整个梳理一遍,后面再加上面试题。Redis 系列:1. Redis系列(一)Redis入门2.
Redis系列(六)Redis 的缓存穿透、缓存击穿和缓存雪崩
2021-02-26

Redis 缓存击穿(失效)、缓存穿透、缓存雪崩怎么解决?

缓存击穿(失效)指的是数据库有数据,缓存本应该也有数据,但是缓存过期了,Redis 这层流量防护屏障被击穿了,请求直奔数据库。

缓存穿透、缓存雪崩、缓存击穿?再也不怕了,你随便问吧!

本文介绍了在使用缓存时经常会遇到的三种异常情况:缓存穿透、缓存雪崩和缓存击穿。三种异常情况从根本上来说都是因为本应该访问缓存的,但是缓存不存在或服务异常,导致流量直接进入了数据库层面。

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录