10道不得不会的缓存面试题【缓存】

news2025/1/17 14:22:28

博主介绍: 🚀自媒体 JavaPub 独立维护人,全网粉丝15w+,csdn博客专家、java领域优质创作者,51ctoTOP10博主,知乎/掘金/华为云/阿里云/InfoQ等平台优质作者、专注于 Java、Go 技术领域和副业。🚀


最少必要面试题 ,获取《10万字301道Java经典面试题总结(附答案)》pdf,背题更方便,一文在手,面试我有

突击面试 | 突击面试 | 突击面试

我是JavaPub,专注于面试、副业,技术人的成长记录。

以下是 缓存 面试题,相信大家都会有种及眼熟又陌生的感觉、看过可能在短暂的面试后又马上忘记了。JavaPub在这里整理这些容易忘记的重点知识及解答建议收藏,经常温习查阅

评论区见

缓存

说到缓存,首先你一定要对 Java容器、和 Redis 有一定了解,建议阅读《最少必要面试题》: 【Java容器篇】 【Redis篇】

文章目录

  • 缓存
      • 1. 什么是缓存?
      • 2. 为什么要用缓存?
      • 3. 请说说有哪些缓存算法?是否能手写一下 LRU 代码的实现?
      • 4. 常见的常见的缓存工具和框架有哪些?
      • 5. 用了缓存之后,有哪些常见问题?
      • 6. 如何处理缓存穿透的问题
      • 7. 如何处理缓存雪崩的问题
      • 8. 如何处理缓存击穿的问题
      • 9. 缓存和 DB 的一致性如何保证?
      • 10. 什么是缓存预热?如何实现缓存预热?
      • 拓展:缓存数据的淘汰策略有哪些?

1. 什么是缓存?

缓存,就是数据交换的缓冲区,针对服务对象的不同(本质就是不同的硬件)都可以构建缓存。而我们平时说的缓存,大多是指内存。

目的是, 把读写速度【慢】的介质的数据保存在读写速度【快】的介质中,从而提高读写速度,减少时间消耗。 例如:

  • CPU 高速缓存 :高速缓存的读写速度远高于内存。
    • CPU 读数据时,如果在高速缓存中找到所需数据,就不需要读内存
    • CPU 写数据时,先写到高速缓存,再回写到内存。
  • 磁盘缓存:磁盘缓存其实就把常用的磁盘数据保存在内存中,内存读写速度也是远高于磁盘的。
    • 读数据,时从内存读取。
    • 写数据时,可先写到内存,定时或定量回写到磁盘,或者是同步回写。

2. 为什么要用缓存?

使用缓存的目的,就是提升读写性能。而实际业务场景下,更多的是为了提升读性能,带来更好的性能,更高的并发量。

日常业务中,我们使用比较多的数据库是 MySQL ,缓存是 Redis 。Redis 比 MySQL 的读写性能好很多。那么,我们将 MySQL 的热点数据,缓存到 Redis 中,提升读取性能,也减小 MySQL 的读取压力。例如说:

  • 论坛帖子的访问频率比较高,且要实时更新阅读量,使用 Redis 记录帖子的阅读量,可以提升性能和并发。
  • 商品信息,数据更新的频率不高,但是读取的频率很高,特别是热门商品。

3. 请说说有哪些缓存算法?是否能手写一下 LRU 代码的实现?

缓存算法,比较常见的是三种:

  • LRU(least recently used ,最近最少使用)
  • LFU(Least Frequently used ,最不经常使用)
  • FIFO(first in first out ,先进先出)

这里我们可以借助 LinkedHashMap 实现

public class LRULinkedMap<K,V> {


    /**
     * 最大缓存大小
     */
    private int cacheSize;

    private LinkedHashMap<K,V> cacheMap ;


    public LRULinkedMap(int cacheSize) {
        this.cacheSize = cacheSize;

        cacheMap = new LinkedHashMap(16,0.75F,true){
            @Override
            protected boolean removeEldestEntry(Map.Entry eldest) {
                if (cacheSize + 1 == cacheMap.size()){
                    return true ;
                }else {
                    return false ;
                }
            }
        };
    }

    public void put(K key,V value){
        cacheMap.put(key,value) ;
    }

    public V get(K key){
        return cacheMap.get(key) ;
    }


    public Collection<Map.Entry<K, V>> getAll() {
        return new ArrayList<Map.Entry<K, V>>(cacheMap.entrySet());
    }
}

使用案例:

    @Test
    public void put() throws Exception {
        LRULinkedMap<String,Integer> map = new LRULinkedMap(3) ;
        map.put("1",1);
        map.put("2",2);
        map.put("3",3);

        for (Map.Entry<String, Integer> e : map.getAll()){
            System.out.print(e.getKey() + " : " + e.getValue() + "\t");
        }

        System.out.println("");
        map.put("4",4);
        for (Map.Entry<String, Integer> e : map.getAll()){
            System.out.print(e.getKey() + " : " + e.getValue() + "\t");
        }
    }
    
//输出
1 : 1	2 : 2	3 : 3	
2 : 2	3 : 3	4 : 4	 

4. 常见的常见的缓存工具和框架有哪些?

在 Java 后端开发中,常见的缓存工具和框架列举如下:

  • 本地缓存:Guava LocalCache、Ehcache、Caffeine 。

    Ehcache 的功能更加丰富,Caffeine 的性能要比 Guava LocalCache 好。

  • 分布式缓存:Redis、Memcached、Tair 。

    Redis 最为主流和常用。

5. 用了缓存之后,有哪些常见问题?

常见的问题,可列举如下:

写入问题

  • 缓存何时写入?并且写时如何避免并发重复写入?
  • 缓存如何失效?
  • 缓存和 DB 的一致性如何保证?

经典三连问

  • 如何避免缓存穿透的问题?
  • 如何避免缓存击穿的问题?
  • 如果避免缓存雪崩的问题?

6. 如何处理缓存穿透的问题

缓存穿透,是指查询一个一定不存在的数据,由于缓存是不命中时被动写,并且处于容错考虑,如果从 DB 查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到 DB 去查询,失去了缓存的意义。

在流量大时,可能 DB 就挂掉了,要是有人利用不存在的 key 频繁攻击我们的应用,这就是漏洞。如下图:

缓存穿透

如何解决

有两种方案可以解决:

  1. 方案一,缓存空对象。
    当从 DB 查询数据为空,我们仍然将这个空结果进行缓存,具体的值需要使用特殊的标识,能和真正缓存的数据区分开。另外,需要设置较短的过期时间,一般建议不要超过 5 分钟。

  2. 方案二,BloomFilter 布隆过滤器。
    在缓存服务的基础上,构建 BloomFilter 数据结构,在 BloomFilter 中存储对应的 KEY 是否存在,如果存在,说明该 KEY 对应的值不为空。

如何选择

这两个方案,各有其优缺点。

缓存空对象BloomFilter 布隆过滤器
适用场景1、数据命中不高 2、保证一致性1、数据命中不高, 2、数据相对固定、实时性低
维护成本1、代码维护简单 2、需要过多的缓存空间 3、数据不一致1、代码维护复杂,2、缓存空间占用小

实际情况下,使用方案二比较多。因为,相比方案一来说,更加节省内容,对缓存的负荷更小。

7. 如何处理缓存雪崩的问题

缓存雪崩,是指缓存由于某些原因无法提供服务( 例如,缓存挂掉了 ),所有请求全部达到 DB 中,导致 DB 负荷大增,最终挂掉的情况。

如何解决

预防和解决缓存雪崩的问题,可以从以下多个方面进行共同着手。

  1. 缓存高可用:通过搭建缓存的高可用,避免缓存挂掉导致无法提供服务的情况,从而降低出现缓存雪崩的情况。假设我们使用 Redis 作为缓存,则可以使用 Redis Sentinel 或 Redis Cluster 实现高可用。

  2. 本地缓存:如果使用本地缓存时,即使分布式缓存挂了,也可以将 DB 查询到的结果缓存到本地,避免后续请求全部到达 DB 中。如果我们使用 JVM ,则可以使用 Ehcache、Guava Cache 实现本地缓存的功能。

当然,引入本地缓存也会有相应的问题,例如说:

本地缓存的实时性怎么保证?
方案一,可以引入消息队列。在数据更新时,发布数据更新的消息;而进>程中有相应的消费者消费该消息,从而更新本地缓存。
方案二,设置较短的过期时间,请求时从 DB 重新拉取。
方案三,手动过期。

  1. 请求 DB 限流: 通过限制 DB 的每秒请求数,避免把 DB 也打挂了。如果我们使用 Java ,则可以使用 Guava RateLimiter、Sentinel、Hystrix 实现限流的功能。这样至少能有两个好处:

    • 可能有一部分用户,还可以使用,系统还没死透。
    • 未来缓存服务恢复后,系统立即就已经恢复,无需再处理 DB 也挂掉的情况。
  2. 提前演练:在项目上线前,演练缓存宕掉后,应用以及后端的负载情况以及可能出现的问题,在此基础上做一些预案设定。

8. 如何处理缓存击穿的问题

缓存击穿,是指某个极度“热点”数据在某个时间点过期时,恰好在这个时间点对这个 KEY 有大量的并发请求过来,这些请求发现缓存过期一般都会从 DB 加载数据并回设到缓存,但是这个时候大并发的请求可能会瞬间 DB 压垮。

  • 对于一些设置了过期时间的 KEY ,如果这些 KEY 可能会在某些时间点被超高并发地访问,是一种非常“热点”的数据。这个时候,需要考虑这个问题。
  • 区别:
    • 和缓存“雪崩“”的区别在于,前者针对某一 KEY 缓存,后者则是很多 KEY 。
    • 和缓存“穿透“”的区别在于,这个 KEY 是真实存在对应的值的。

如何解决

有两种方案可以解决:

  1. 方案一,使用互斥锁。请求发现缓存不存在后,去查询 DB 前,使用分布式锁,保证有且只有一个线程去查询 DB ,并更新到缓存。

  2. 方案二,手动过期。缓存上从不设置过期时间,功能上将过期时间存在 KEY 对应的 VALUE 里。流程如下:

    1. 获取缓存。通过 VALUE 的过期时间,判断是否过期。如果未过期,则直接返回;如果已过期,继续往下执行。
    2. 通过一个后台的异步线程进行缓存的构建,也就是“手动”过期。通过后台的异步线程,保证有且只有一个线程去查询 DB。
    3. 同时,虽然 VALUE 已经过期,还是直接返回。通过这样的方式,保证服务的可用性,虽然损失了一定的时效性。

选择

这两个方案,各有其优缺点。

使用互斥锁手动过期
优点1、思路简单 2、保证一致性1、性价最佳,用户无需等待
缺点1、代码复杂度增大 2、存在死锁的风险1、无法保证缓存一致性

9. 缓存和 DB 的一致性如何保证?

产生原因

主要有两种情况,会导致缓存和 DB 的一致性问题:

  1. 并发的场景下,导致读取老的 DB 数据,更新到缓存中。

主要指的是,更新 DB 数据之前,先删除 Cache 的数据。在低并发量下没什么问题,但是在高并发下,就会存在问题。在(删除 Cache 的数据, 和更新 DB 数据)时间之间,恰好有一个请求,我们如果使用被动读,因为此时 DB 数据还是老的,又会将老的数据写入到 Cache 中。

  1. 缓存和 DB 的操作,不在一个事务中,可能只有一个 DB 操作成功,而另一个 Cache 操作失败,导致不一致。

当然,有一点我们要注意,缓存和 DB 的一致性,我们指的更多的是最终一致性。我们使用缓存只要是提高读操作的性能,真正在写操作的业务逻辑,还是以数据库为准。例如说,我们可能缓存用户钱包的余额在缓存中,在前端查询钱包余额时,读取缓存,在使用钱包余额时,读取数据库。

解决方案

在开始说解决方案之前,胖友先看看如下几篇文章,可能有一丢丢多,保持耐心。

当然无论哪种方案,比较重要的就是解决两个问题:

    1. 将缓存可能存在的并行写,实现串行写。
    1. 实现数据的最终一致性。
  1. 先淘汰缓存,再写数据库
    因为先淘汰缓存,所以数据的最终一致性是可以得到有效的保证的。为什么呢?先淘汰缓存,即使写数据库发生异常,也就是下次缓存读取时,多读取一次数据库。

那么,我们需要解决缓存并行写,实现串行写。比较简单的方式,引入分布式锁。

  • 在写请求时,先淘汰缓存之前,先获取该分布式锁。
  • 在读请求时,发现缓存不存在时,先获取分布式锁。
  1. 先写数据库,再更新缓存

按照 “先写数据库,再更新缓存”,我们要保证 DB 和缓存的操作,能够在 “同一个事务”中,从而实现最终一致性

10. 什么是缓存预热?如何实现缓存预热?

缓存预热

在刚启动的缓存系统中,如果缓存中没有任何数据,如果依靠用户请求的方式重建缓存数据,那么对数据库的压力非常大,而且系统的性能开销也是巨大的。

此时,最好的策略是启动时就把热点数据加载好。这样,用户请求时,直接读取的就是缓存的数据,而无需去读取 DB 重建缓存数据。举个例子,热门的或者推荐的商品,需要提前预热到缓存中。

如何实现

一般来说,有如下几种方式来实现:

  • 数据量不大时,项目启动时,自动进行初始化。
  • 写个修复数据脚本,手动执行该脚本。
  • 写个管理界面,可以手动点击,预热对应的数据到缓存中。

拓展:缓存数据的淘汰策略有哪些?

除了缓存服务器自带的缓存自动失效策略之外,我们还可以根据具体的业务需求进行自定义的“手动”缓存淘汰,常见的策略有两种:

  1. 定时去清理过期的缓存。
  2. 当有用户请求过来时,再判断这个请求所用到的缓存是否过期,过期的话就去底层系统得到新数据并更新缓存。

两者各有优劣,第一种的缺点是维护大量缓存的 key 是比较麻烦的,第二种的缺点就是每次用户请求过来都要判断缓存失效,逻辑相对比较复杂!Redis 的缓存淘汰策略就是很好的实践方式。

具体用哪种方案,大家可以根据自己的应用场景来权衡。

低谷蓄力

最少必要面试题

10道不得不会的Java基础面试题

10道不得不会的Java并发基础面试题

10道不得不会的Java容器面试题

10道不得不会的JVM面试题

10道不得不会的MySQL基础面试题

10道不得不会的MyBatis面试题

10道不得不会的ElasticSearch面试题

10道不得不会的Spring面试题

10道不得不会的Redis面试题

10道不得不会的Kafka面试题

10道不得不会的Docker面试题

10道不得不会的Zookeeper面试题

10道不得不会的JavaEE面试题

10道不得不会的SpringBoot面试题

10道不得不会的缓存面试题

关注公众号,回复1024,获取Java学习路线思维导图、加入源码计划学习交流群

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/49154.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Conda

0. Anaconda Prompt 命令提示符 0.1 验证conda是否被安装 conda --version0.2 conda管理环境 可以用命令复制和删除环境 参考. 1. Conda管理包 1.1 常用包管理功能 查找包查看包安装包 查找分为精确查找和模糊查找&#xff0c;如下图所示 卸载包更新包 1.2 conda管理环…

【实习之velocity】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、图示二、应用场景1.Web应用程序&#xff1a;作为为应用程序的视图&#xff0c;显示数据2.源代码生成&#xff1a;Velocity可用于基于模板生成java源代码3.自…

[c++基础]-vector类

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正。 目录 一、 vect…

scanf导致程序运行时出现stack smashing

一 栈溢出stack smashing 程序在运行期间破坏了已在操作系统里定义好的栈边界&#xff0c;这种行为具有破坏性&#xff0c;操作系统使用stack smashing detect机制来检测栈溢出。 二 栈溢出简单例子 实例一&#xff1a; #include <stdio.h> #include <stdlib.h>…

基于智能优化算法实现的机械臂避障路径规划(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 针对目前空间机械臂避障路径规划算法计算量大难以达到在线实时规划的缺点,对空间机械臂的在线实时避障路径规划问题进行了研究和…

计算机四级网络-网络技术-第六章 网络管理与网络安全

6.1 网络管理技术 CMIP 采用委托监控机制。 CMIP协议是由IS0组织制定的一种管理协议。管理进程根据事件发生时对网络服务影响的大小来划分事件的严重等级&#xff0c;然后再产生相应的故障处理方案。CMIP的所有功能都要映射到应用层的相关协议上实现。操作和事件报告是通过远…

如何使用腾讯会议录屏?腾讯会议不允许录屏怎么办?这个方法教你解决

​近年来&#xff0c;线上活动越来越活跃&#xff0c;很多人都开始使用腾讯会议进行开会、网课教学等活动。很多人希望使用腾讯会议进行录屏。那么如何使用腾讯会议录屏&#xff1f;腾讯会议不允许录屏怎么办&#xff1f;这个方法教你解决&#xff01; 一、如何使用腾讯会议录屏…

计算机组成原理习题课第三章-5(唐朔飞)

计算机组成原理习题课第三章-5&#xff08;唐朔飞&#xff09; ✨欢迎关注&#x1f5b1;点赞&#x1f380;收藏⭐留言✒ &#x1f52e;本文由京与旧铺原创&#xff0c;csdn首发&#xff01; &#x1f618;系列专栏&#xff1a;java学习 &#x1f4bb;首发时间&#xff1a;&…

[ElasticSearch]-初识Elastic Stack

[ElasticSearch]-初识Elastic Stack 森格 | 2022年11月 本文是第一次接触Elasticsearch的一些个人总结&#xff0c;介绍了Elastic Stack&#xff0c;如有错误之处&#xff0c;敬请指正。 一、ELK 到 Elastic Stack 注&#xff1a;上图来源于Elasticsearch中文博客 ELK分别是Ela…

PyTorch深度学习笔记之五(使用神经网络拟合数据)

使用神经网络拟合数据 1. 人工神经网络 1.1 神经网络和神经元 神经网络&#xff1a;一种通过简单函数的组合来表示复杂函数的数学实体。 人工神经网络和生理神经网络似乎都使用模糊相似的数学策略来逼近复杂的函数&#xff0c;因为这类策略非常有效。 这些复杂函数的基本构…

多线程轮流打印

一、背景 面试的时候&#xff0c;有一个高频的笔试题&#xff1a; 让2个线程轮流打印&#xff0c;a线程是打印ABCDEFGHIJ&#xff0c;b线程是打印1、2、3、4、5、6、7、8、9、10 二、原理 这种类型的面试题&#xff0c;主要是考察object的wait()方法和notify()方法的使用 …

spring整合Mybatis-P23,24,25

复习Mybatis&#xff08;都是之前的内容&#xff0c;不再解释&#xff09; 6个需要修改或创建的文件 UserMapper package com.Li.mapper;import com.Li.pojo.User;import java.util.List;public interface UserMapper {public List<User> selectUser(); }UserMapper.xm…

如何全面提升架构设计的质量

低成本 低成本本质上是对架构的一种约束&#xff0c;与高性能等架构是冲突的 手段和应用 先设计架构方案&#xff0c;再看如何降低成本 优化 引入缓存虚拟化、容器化性能调优采用高性能硬件采用开源方案 创新 NoSQL vs SQLSQL vs 倒排索引Hadoop vs MySQL 安全性 复杂…

《码出高效:Java开发手册》 四-走进JVM

前言 JVM是java中底层的知识&#xff0c;这里的内容比较复杂&#xff0c;对于一些软件编程&#xff0c;会经常使用&#xff0c;但很多业务其实碰不到这里的知识&#xff0c;下图为目录 介绍 JVM&#xff0c;java虚拟机&#xff0c;它的前身是99年的hotspot java虚拟机&…

vue 计算属性未重新计算 / computed 未触发 / computed 原理源码分析

点击可打开demo 这里在一秒后改了数组里value属性的值 虽然数据有更新&#xff0c;但打开控制台&#xff0c;可以发现computed函数只在初始化时执行了一次 按理说一秒后改变了value值&#xff0c;应该执行两次才对呀&#xff1f; 但如果computed属性这样写&#xff0c;明确写…

数据分析之大数据分析

一 什么是大数据分析 大数据是指无法在一定时间范围内用常规软件工具进行捕捉、管理和处理的数据集合&#xff0c;是需要新处理模式才能具有更强的决策力、洞察发现力和流程优化能力的海量、高增长率和多样化的信息资产。大数据的特点可以概括为5个V&#xff1a;数据量大&…

当湿度达到70蜂鸣器警报

1.编写设备树&#xff0c;添加蜂鸣器等设备 驱动代码&#xff1a; #include <linux/init.h> #include <linux/module.h> #include <linux/i2c.h> #include <linux/fs.h> #include <linux/uaccess.h> #include <linux/device.h> #include …

QCSPCChart for Java R3x0 Crack

Java 的 SPC 控制图工具 版本 3.04 QCSPCChart添加变量控制图&#xff08;X-Bar R、X-Bar Sigma、Individual Range、Median Range、EWMA、MA、MAMR、MAMS 和 CuSum 图&#xff09;、属性控制图&#xff08;p-、np-、c-、u- 和DPMO 图&#xff09;、频率直方图和 Pareto 图到…

[附源码]Python计算机毕业设计Django的旅游景点管理系统的设计与实现

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

[附源码]Python计算机毕业设计SSM老年公寓管理系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…