面试官问如何实现二级缓存怎么进行回答以及延伸出更多知识点呢?

news2024/11/27 4:32:01

二级缓存的优势与缺点

优点:

1)二级缓存相比只调用一层 Redis 缓存,访问速度更快。对于一些不经常修改的数据而查询十分频繁的可以直接放在本地缓存(一级)里面。

作为面试者的扩展延伸:我在本地缓存的实现中,我使用到了本地缓存性能之王 Caffeine 作为一级缓存,在市面上很多像 Redisson、Druid、Hbase 等知名开源项目都用到了 Caffeine 。它实现了更加好用的缓存淘汰算法 W-TinyLFU 算法,结合了 LRU(最近最久未使用算法) 算法以及 LFU(最少使用算法) 算法的优点,所以选择它能使本地缓存的使用更加方便快速。

2)使用了本地缓存相比直接去 Redis 中取,能够减少与远程 Redis 的数据 I/O 网络交互,降低了网络之间的消耗。

缺点:

1)增加了本地缓存对于一致性的维护更加复杂,提高了维护成本。

2)在分布式环境下,如何解决各个节点本地缓存一致性问题?使用类 Redis 的发布订阅功能,当一个节点的数据发生修改时,直接通知其他节点的缓存进行更新。

是不是已经初步了解了二级缓存的应用咧~ 下来先带你实现一下二级缓存。

在这里插入图片描述

简单实现

1)引入依赖

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2)配置 Caffeine

@Configuration
public class CaffeineConfig {
    @Bean
    public Cache<String,Object> caffeineCache(){
        return Caffeine.newBuilder()
                .initialCapacity(128)//初始大小
                .maximumSize(1024)//最大数量
                .expireAfterWrite(60, TimeUnit.SECONDS)//过期时间
                .build();
    }
}

3)使用二级缓存

@Resource
private Cache<String, Object> cache;
@Resource
private RedisTemplate<String, Object> redisTemplate;
@Test
void testCache() {
    // 缓存测试存取
    cache.put("test", "test");
    assertEquals("test", cache.getIfPresent("test"));

    //实现二级缓存读取
    cache.get("test",
              k -> {
                  //先查询 Redis
                  Object obj = redisTemplate.opsForValue().get(k);
                  if (Objects.nonNull(obj)) {
                      System.out.println("缓存命中:" + k + " 从 Redis 读取成功");
                      return obj;
                  }

                  // Redis没有则查询 DB
                  System.out.println("缓存没有命中:" + k + " 从 DB 读取");
                  // 从 DB 读取 ..此处模拟省略
                  obj = "test";
                  // 存入 Redis
                  redisTemplate.opsForValue().set(k, "test");
                  //存入本地缓存由cache.get执行
                  return obj;
              });

}

这样一个缓存简单的二级缓存使用就是这样啦,但这样是不是感觉对代码的侵入性有点强了,使用起来不够灵活,下面再来带大家使用 Spring 提供的 CacheManager 接口以及注解来实现它。

image-20240426101016819

使用 Spring 的 CacheManager 实现

1)引入依赖

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>2.9.2</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

2)配置 CacheManager

@Configuration
public class CacheManagerConfig {
    @Bean
    public CacheManager cacheManager(){
        CaffeineCacheManager cacheManager=new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
                .initialCapacity(128)
                .maximumSize(1024)
                .expireAfterWrite(60, TimeUnit.SECONDS));
        return cacheManager;
    }
}

3)在启动类上面加上 @EnableCaching 注解

@SpringBootApplication()
@EnableCaching
public class Application {

}

4)使用二级缓存查询

@Resource
private RedisTemplate<String,Object> redisTemplate;
@Cacheable(value = "test",key = "#id")
public String getStringTextById(Long id) {
    String key= "test" + id;
    //先查询 Redis
    String obj = (String) redisTemplate.opsForValue().get(key);
    if (Objects.nonNull(obj)){
        log.info("从Redis中获取数据");
        return obj;
    }
    // Redis没有则查询 DB
    log.info("从DB中获取数据");
    //此处省略查询DB的代码
    obj = "test";
    //redis 中存入
    redisTemplate.opsForValue().set(key,obj,120, TimeUnit.SECONDS);
    return obj;
}

这里要注意的是!

1)@Cacheable 注解的 value 跟 cacheNames 是互为别名的关系,表示当前方法的结果被缓存到哪个 cache 上面,它可以是一个数组绑定多个 cache

2)@Cacheable 注解的 key 用来指定返回结果的 key,这个属性可以支持 SpringEL 表达式。

SpringEL表达式如下:

#参数名
#参数对象.属性名
#参数为数组对应下标

5)使用二级缓存更新数据

@CachePut(cacheNames = "test",key = "#id")
public String updateOrder(String testData, Long id) {
    log.info("更新数据");
    //更新数据库 此处省略
    //修改 Redis
    redisTemplate.opsForValue().set("test" + id,
                                    testData, 120, TimeUnit.SECONDS);
    //返回要缓存本地的数据
    return testData;
}

这里要注意的是!需要返回对象或值,因为要进行本地缓存操作。

6)使用二级缓存删除数据

@CacheEvict(cacheNames = "test",key = "#id")
public void deleteOrder(Long id) {
    log.info("删除数据");
    //此处省略删除数据库的代码
    //删除 Redis
    redisTemplate.delete("test" + id);
}

在这里简单的使用就到这里啦!还有更加优雅的方式大家可以去实现一下,通过 AOP 去实现二级缓存~

image-20240426103353051

二级缓存抛砖引玉

在上面跟大家一起谈了谈二级缓存的实现以及使用,这里我们可以跟面试官再次周旋一下!我曾经在阅读 Spring 源码时我还了解到了 Spring 的三级缓存实现。

在 Spring 框架中,循环依赖是指两个或多个 Bean 之间相互依赖,形成一个循环引用的情况。这种情况下,Spring IOC 容器在实例化 Bean 时可能会出现问题,因为它无法决定应该首先实例化哪个 Bean。为了解决这个问题,Spring 引入了三级缓存。

三级缓存是指 Spring IOC 容器中用于解决循环依赖问题的一种机制,它包含三个缓存阶段:

1)singletonObjects:这是 Spring IOC 容器的一级缓存,用于存储已经完全创建并初始化的 Bean实例。当 Bean 完全创建后,它会被放置在这个缓存中。

2)earlySingletonObjects:这是 Spring IOC 容器的二级缓存,用于存储提前暴露的、尚未完全初始化的 Bean 实例。当 Bean 正在被创建但尚未完成初始化时,它会被放置在这个缓存中。

3)singletonFactories:这是 Spring IOC 容器的三级缓存,用于存储 Bean 的工厂对象。在创建Bean 实例时,Spring 首先会在这个缓存中查找工厂对象,如果找到则使用该工厂对象来创建 Bean 实例。

三级缓存的作用

三级缓存的作用是为了解决循环依赖时的初始化顺序问题。在初始化 Bean 时,Spring 会首先将 Bean 的实例放入三级缓存中,然后进行属性注入等操作。如果发现循环依赖,Spring 会在二级缓存中查找对应的 Bean 实例,如果找到则直接返回,否则会调用Bean的工厂方法来创建 Bean 实例,并将其放入二级缓存中。当 Bean 实例化完成后,会从二级缓存中移除,并放入一级缓存中。

为什么需要三级缓存而不是二级缓存

二级缓存只存储了尚未初始化完成的 Bean 实例,而三级缓存存储了 Bean 的工厂对象。这样做的好处是,当发现循环依赖时,可以通过 Bean 的工厂对象来创建 Bean 实例,从而避免了直接从二级缓存中获取可能尚未完成初始化的 Bean 实例而导致的问题。因此,三级缓存提供了更加灵活和可靠的解决方案,能够更好地处理循环依赖问题。

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

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

相关文章

【MySQL 数据宝典】【索引原理】- 002 示例+逐个字段学明白 Explain

一、Explain 概述 使用 EXPLAIN 关键字可以模拟优化器来执行SQL查询语句&#xff0c;从而知道MySQL是如何处理我们的SQL语句的。分析出查询语句或是表结构的性能瓶颈。 1.1 MySQL 查询过程 通过explain我们可以获得以下信息&#xff1a; 表的读取顺序数据读取操作的操作类型…

华为ensp中链路聚合两种(lacp-static)模式配置方法

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月26日11点54分 链路聚合&#xff08;Link Aggregation&#xff09;&#xff0c;又称为端口聚合&#xff08;Port Trunking&#xff09;&#xff0c;是一种将多条物理…

OU和域用户的创建

OU和域用户的创建 导航 文章目录 OU和域用户的创建导航一、创建ou二、创建用户三、验证 一、创建ou 在服务器管理器里面点击右上角的工具,选择Active Directory 用户和计算机右击我们的域,选择新建,选择组织单位,并填入我们的单位名字 二、创建用户 右击我们刚刚新建的组织…

prompt提示词:AI英语词典优化版Pro,让AI教你学英语,通过AI实现一个网易有道英语词典

目录 一、前言二、效果对比三、优化《AI英语词典》提示词四、其他获奖作品链接 一、前言 不可思议&#xff01;我的AI有道英语字典助手竟然与百度千帆AI应用创意挑战赛K12教育主题赛榜首作品差之毫厘 &#xff0c;真的是高手都是惺惺相惜的&#xff0c;哈哈&#xff0c;自恋一…

pytest参数化数据驱动(数据库/execl/yaml)

常见的数据驱动 数据结构&#xff1a; 列表、字典、json串 文件&#xff1a; txt、csv、excel 数据库&#xff1a; 数据库链接 数据库提取 参数化&#xff1a; pytest.mark.parametrize() pytest.fixture()…

Stable Diffusion WebUI 使用 VAE 增加滤镜效果

本文收录于《AI绘画从入门到精通》专栏&#xff0c;专栏总目录&#xff1a;点这里&#xff0c;订阅后可阅读专栏内所有文章。 大家好&#xff0c;我是水滴~~ 本文主要介绍 VAE 模型&#xff0c;主要内容有&#xff1a;VAE 模型的概念、如果下载 VAE 模型、如何安装 VAE 模型、如…

苍穹外卖学习

并不包含全部视频内容&#xff0c;大部分都按照操作文档来手搓代码&#xff0c;资料&#xff0c;代码都上传git。 〇、实际代码 0.1 Result封装 package com.sky.result;import lombok.Data;import java.io.Serializable;/*** 后端统一返回结果* param <T>*/ Data pub…

【EP2C35F672C8 EDA试验箱下载 38译码器实现】

文章目录 前言一、实验设备1.实验箱2.下载器&#xff1a; 二、编译工程1.编译工程2.添加tcl引脚配置文件2.1将tcl文件拷贝到工程目录下&#xff1a;2.2 在软件中添加tcl文件 3.tcl文件简答讲解 三、下载四、实验结果总结 前言 提示&#xff1a;这里可以添加本文要记录的大概内…

【LeetCode:2095. 删除链表的中间节点 + 链表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

虚拟现实(VR)的应用场景

虚拟现实&#xff08;VR&#xff09;技术创建和体验三维虚拟世界的计算机仿真技术。用户通过佩戴VR头显等设备&#xff0c;可以完全沉浸在虚拟世界中&#xff0c;并与虚拟世界中的物体进行交互。VR技术具有广泛的应用前景&#xff0c;可以应用于各行各业。以下是一些VR的应用场…

C++ 并发编程 - 入门

目录 写在前面 并发编程&#xff0c;启动&#xff01; 写在前面 计算机的并发指在单个系统里同时执行多个独立的任务。 在过去计算机内只有一个处理器时并发是通过快速的切换进程上下文所实现的&#xff0c;而现在计算机已经步入了多核并发时代&#xff0c;所以多个进程的并…

利用STM32 HAL库实现USART串口通信,并通过printf重定向输出“Hello World“

一、开发环境 硬件&#xff1a;正点原子探索者 V3 STM32F407 开发板 单片机&#xff1a;STM32F407ZGT6 Keil版本&#xff1a;5.32 STM32CubeMX版本&#xff1a;6.9.2 STM32Cube MCU Packges版本&#xff1a;STM32F4 V1.27.1 上一篇使用STM32F407的HAL库只需1行代码实现US…

EDM营销工具效果如何评估?如何优化性能?

EDM营销工具的功能优势有哪些&#xff1f;怎么使用邮件营销工具&#xff1f; 对于许多营销人员来说&#xff0c;如何评估EDM营销工具的效果&#xff0c;却是一个颇具挑战性的问题。AokSend将围绕这一问题展开讨论&#xff0c;帮助读者更好地理解EDM营销工具效果的评估方法和技…

AI大模型蜂拥而至,PC准备好了吗?

通信世界网消息&#xff08;CWW&#xff09;在这个大模型风起云涌的年代&#xff0c;各行各业都在积极拥抱这波风口&#xff0c;试图让这个世界充满AI。根据最新公开的数据&#xff0c;截至2024年3月&#xff0c;中国共有117个大模型成功完成备案。这些大模型涵盖了各个领域&am…

Windows系统下将MySQL数据库表内的数据全量导入Elasticsearch

目录 下载安装Logstash 配置Logstash配置文件 运行配置文件 查看导入结果 使用Logstash将sql数据导入Elasticsearch 下载安装Logstash 官网地址 选择Windows系统&#xff0c;需下载与安装的Elasticsearch相同版本的&#xff0c;下载完成后解压安装包。 配置Logstash配…

黑马-设计模式-笔记(未完)

一、基础 UML类图 可见性&#xff1a; public- private#protected 表示方式&#xff1a;属性&#xff1a;可见性 名称:类型[默认值]方法&#xff1a;可见性 名称(参数)[:返回类型] 关系&#xff1a;关联关系&#xff1a;实线&#xff0c;引用关系&#xff0c;类属性里有另一个…

STM32存储左右互搏 SDIO总线FATS文件读写SD/MicroSD/TF卡

STM32存储左右互搏 SDIO总线FATS文件读写SD/MicroSD/TF卡 SD/MicroSD/TF卡是基于FLASH的一种常见非易失存储单元&#xff0c;由接口协议电路和FLASH构成。市面上由不同尺寸和不同容量的卡&#xff0c;手机领域用的TF卡实际就是MicroSD卡&#xff0c;尺寸比SD卡小&#xff0c;而…

switch语句深讲

一。功能 1.选择&#xff0c;由case N:完成 2.switch语句本身没有分支功能&#xff0c;分支功能由break完成 二。注意 1.switch语句如果不加break&#xff0c;在一次判断成功后会执行下面全部语句并跳过判断 2.switch的参数必须是整形或者是计算结果为整形的表达式,浮点数会…

3.7设计模式——Observer 观察者模式(行为型)

意图 定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于他的对象都得到通知并被自动更新。 结构 Subject&#xff08;目标&#xff09;知道它的观察者&#xff0c;可以有任意多个观察者观察同一个目标&#xff0c;提供注册和删…

C++(Qt)软件调试---crashpad捕获崩溃(19)

C(Qt)软件调试—crashpad捕获崩溃&#xff08;19&#xff09; 文章目录 C(Qt)软件调试---crashpad捕获崩溃&#xff08;19&#xff09;1、概述2、资源地址3、配置环境4、解决报错5、测试代码6、测试结果7、Qt中使用crashpad 更多精彩内容&#x1f449;个人内容分类汇总 &#x…