面试高频:什么情况下要用到缓存?如何应对缓存穿透、击穿及雪崩?

news2024/11/15 14:02:11

一、为什么要使用内存数据库?

我们先来看一下以往单体的Web系统架构图是什么样的:


从图中可以看出,早期的单体架构基本上是以业务为导向,同时用户群体不是很大,这种单体的架构基本上可以应付大多数使用场景。但随着互联网的推广,用户体量的迅速攀升,响应要求的不断提高,单体架构的系统往往会频繁的出现请求延迟、响应超时或是系统宕机。特别是在数据库层面,更是无法满足高访问量的读写。

为什么会产生这种情况呢?由于数据库的数据是存在硬盘上,硬盘的 I/O 读写瓶颈会直接影响并发量。既然磁盘 I/O 读写时瓶颈,我们是不是可以采用速度更快的内存来存储常用但数据量不算大的数据呢?

为了解决这个问题,目前通用的做法是引入基于内存的数据库,这样的数据库一般是把数据先放到内存里,引入缓存中间件之后的项目 Web 服务架构图如下所示:

这样便可以较大程度缓解传统数据库带来的磁盘 I/O 读写瓶颈,而我们最常使用的基于内存的数据库就是 Redis 和 MemCached。

二、Redis和Memcached有何区别?

1、存储方式

  • Memcached目前只支持单一的数据结构(Key-Value)。类似于java中的Map。
  • Redis则支持多种数据结构,如字符串、列表、集合、散列表、集合等。

2、持久化

持久化就是把数据从内存永久存储到磁盘里,可以防止断电等异常情况下数据丢失等问题。目前 Redis 支持持久化,而 MemCached 不支持。遇到灾难,MemCached 无法恢复数据,Redis 可以恢复数据,保证了数据的安全性。但这里也要注意,Redis不是实时去存储数据到硬盘中的,也是根据配置的保存机制来存储的。大家可以在另外一篇文章中去查看Redis的配置。

三、SpringBoot集成Redis

1、导入Redis依赖

在 Maven 中引入 Spring Boot 使用的 Redis 类库,如下代码所示:

<dependency>

   <groupId>org.springframework.boot</groupId>

   <artifactId>spring-boot-starter-data-redis</artifactId>

   <version>2.4.2</version>

</dependency>

2、获取RedisTemplate

通过注解方式获取 RedisTemplate,如下代码所示:

@Autowired

private RedisTemplate<String, String> redisTemplate;

3、使用内置的API实现业务数据的缓存读写

@GetMapping("/getRedisTestData")

public Result getRedisTestData(){

    String redisTestListData = null;

    try {

        redisTestListData = redisTemplate.boundValueOps("redisTest.findAll").get();

        //如果redis中没有数据的话

        if(null == redisTestListData){

            //查询数据库获得数据

            List<RedisTest> redisTestList = simulateSceneRepository.findAll();

            //转换成json格式字符串

            ObjectMapper om = new ObjectMapper();

            redisTestListData = om.writeValueAsString(redisTestList);

            //将数据存储到redis中,下次在查询直接从redis中获得数据,不用再查询数据库

            redisTemplate.boundValueOps("redisTest.findAll").set(redisTestListData);

            log.info("从Mysql数据库获得数据");

        }else{

            log.info("从redis缓存中获得数据");

        }

    } catch (Exception e){

        log.error("e:{}",e);

    }

    return Result.resultSuccess(null,redisTestListData,"数据读取成功");

}

四、Redis的特性及优缺点

1、Redis特性

(1)主从复制

虽然数据在内存中读写速度比较快,但是在高并发情况下也会产生读写压力特别大的情况,Redis 针对这一情况提供了主从复制功能。

主从复制的好处有如下两点:

  • 提供了 Redis 扩展性,当一台 Redis 不够用时,可以增加多台 Redis 作为从服务器向外提供服务;

  • 提供了数据备份和冗余服务器,当 Redis 主服务器意外宕机,从服务器可以顶替主服务器向外提供服务,增加了系统的高可用性。

(2)脚本操作

Redis 提供了 lua 脚本操作,你可以将 Redis 存取操作写到 lua 脚本里,然后通过 Redis 提供的 API 来执行 lua 脚本,这样就可以实现 Redis 相关操作。

我们同样可以用 Redis 提供的 API 直接实现 Redis 相关操作,那么为什么有时候又要绕一圈去操作 lua 脚本呢?因为 lua 脚本能够保证操作的原子性,即所有的操作当作一个操作,要么全部失败要么全部成功。而直接使用 API 不一定能保证一连串操作的原子性,所以当需要保证原子性的时候需要使用 lua 脚本。

(3)发布订阅

该特性可以将 Redis 作为消息中间件,在服务端产生消息,然后在客户端消费消息队列里的消息,但是作为消息队列不是 Redis 的强项,所以不推荐使用。比如 Redis 作为消息队列消息并非完全可靠,会产生消息丢失的问题,并且也不支持消息分组。在性能上,如果入队和出队操作频繁,那 Redis 性能比起 RabbitMq 等常用消息队列来说还是有差距的。

2、Redis缺点

(1)缓存穿透

缓存穿透的情况是 Redis 和 MySQL 数据库都没有这条数据,但是用户不断并发发起请求,请求压力会同时落到数据库和缓存上,这样的情况相对于设计初衷来说,对系统的压力就会大很多了,而且这也是黑客发起攻击的手段之一,找寻系统是否存在漏洞。

那在项目中如果遇到缓存穿透我们该如何解决呢?

遇到缓存穿透,我们可以在请求访问缓存和数据库都没查到数据时,给一个默认值或者 Null 值,即 Key-Null。然后该缓存值的有效时间可以设置得短点,比如 30s。在业务代码中判断如果是 Null 值就取消查询数据库,或者间隔 30s 之后重试,这样的方式可以大幅度减轻数据库的查询压力。

(2)缓存击穿

单个数据在缓存中不存在,而在数据库中存在。一般这种情况都是缓存失效导致的,在缓存失效的时间段有大量并发用户访问,首先访问缓存,因为 Key 已经过期了,所以查不到数据,然后所有查询压力都会落到数据库上,造成数据库的压力过大。并且还有可能因为并发问题导致重复更新缓存而过多占用缓存资源。

在项目中如果遇到缓存击穿问题,该如何解决呢?

  • 对于一些经常被访问的热点数据,可以根据业务特性主动检查使其 Redis 数据永不过期,当然这样的设置并不代表说这条数据一直不更新而处在 Redis 中,而是根据数据字段中的失效时间和系统时间的对比主动检查更新数据,使 Redis 数据不会过期;

  • 通过后台定时刷新,根据缓存失效时间节点去批量刷新缓存数据,这个适合 Key 失效时间相对固定的场景。

(3)缓存雪崩

大量数据在同一时间失效,会造成数据库查询压力过大导致宕机。缓存雪崩与缓存击穿的区别在于缓存击穿是单个数据失效,缓存雪崩是多个数据同一时间失效。

在项目中如果遇到缓存雪崩的问题,我们该如何解决呢?以下 3 种方法可以帮我们解决。

  • 如果程序设置的缓存过期时间统一为一个固定的值,比如 5s、10s、15s 等等,那么很有可能出现大量数据在同一时间失效。这个时候我们可以设置不同的过期时间,比如统一时间加上一个随机时间,这样可以让缓存的时间尽量均匀分布一点。

  • 不设置过期时间,让程序的定时任务自动定时更新或者清除缓存

  • 使用集群化的方式,保证高可用。

文章将持续更新,欢迎关注公众号:服务端技术精选。欢迎点赞、关注、转发

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

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

相关文章

Springboot+Vue项目-基于Java+MySQL的商业辅助决策系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

数据分析:生存分析原理和应用实例

介绍 生存分析的目的是分析某个时间点的“生存概率”是多少。基于这样的研究目的,需要提供生存数据,它是一种由不同的开始时间和结束时间组成的事件-时间的数据,比如在癌症研究领域,研究手术到死亡的过程、治疗到疾病进展等等。 在开展生存分析前,需要了解什么是删失(c…

博途安装【PLC】

1【安装】 mp.weixin.qq.com Pro功能强于Adv 关于Prof版本和Adv版本的区别&#xff0c;可以参考下图&#xff0c;专业版是包含高级版的&#xff0c;我们选择专业版进行安装 双击TIA_Portal_STEP7_Prof_Safety_WINCC_Prof_V16.exe进行安装 解压安装 如果电脑死循环重启&#…

Python-100-Days: Day05 Construct Program Logical

Python的核心语言元素&#xff1a; &#xff08;变量、类型、运算符、表达式、分支结构、循环结构&#xff09; 之后&#xff0c;必须做的一件事情就是尝试用所学知识去解决现实中的问题&#xff0c;换句话说就是锻炼自己把用人类自然语言描述的算法&#xff08;解决问题的方法…

redis基础(一)

启动与关闭 启动命令在/usr/local/bin目录 服务端后台启动&#xff1a;redis-server opt/redis-6.2.1/redis.conf 客户端连接&#xff1a;执行 redis-cli 关闭操作 ​ 方式1&#xff1a;进入终端后关闭 ​ 方式2&#xff1a;直接kill 掉进程 方式3&#xff1a;通过实例关闭 …

蓝桥杯ctf2024 部分wp

数据分析 1. packet 密码破解 1. cc 逆向分析 1. 欢乐时光 XXTEA #include<stdio.h> #include<stdint.h> #define DELTA 0x9e3779b9 #define MX (((z>>5^y<<2)(y>>3^z<<4))^((sum^y)(key[(p&3)^e]^z))) void btea(unsigned int* v…

ElasticSearch自动补全

一、拼音分词器&#xff1a; 当用户在搜索框输入字符时&#xff0c;我们应该提示出与该字符有关的搜索项&#xff0c;如图&#xff1a; 这种根据用户输入的字母&#xff0c;提示完整词条的功能&#xff0c;就是自动补全了。 GET /_analyze {"text":"我爱螺蛳粉…

快手AI小快走红:评论区里的聊天艺术家,让百万用户欲罢不能!

快手官方推出的AI互动小助手「AI小快」在短视频平台上掀起了一股热潮。那么&#xff0c;这个被誉为“评论区著名聊天艺术家”的AI小快究竟有何魅力呢&#xff1f;让我们一起来看看吧&#xff01; AI-321 | 专注全球AI工具推荐的网站 AI工具集 | 人工智能工具箱 | 全球顶尖AI工…

电磁兼容(EMC):详解压敏电阻的防静电性能如何

目录 1. 压敏电阻性能 2. 静电放电性能评价指标 3. 压敏电阻防静电实验数据 4. 总结 压敏电阻是一种防浪涌型保护器件&#xff0c;常用在防雷击浪涌以及电网浪涌电压的保护电路中。那压敏电阻的防静电性能如何呢&#xff1f;能不能像防电涌那般防住静电放电能量&#xff1f;…

回溯算法练习day.5

491.非递减子序列 链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。 数组中可能含有重…

2024全国大学生高新技术竞赛——算法智星挑战赛 解题报告(流水账版) | 珂学家

前言 评价 因为第一届的缘故吧&#xff0c;导致这场比赛异常的简单。所以不太好评价这块。 怎么说呢&#xff1f; 体验有点差 题目难度没有区分度有两题还存在SPJ判定问题&#xff0c;导致赛时没一人过。 题目分布&#xff0c;简单题占大部分&#xff0c;中等级占一小部分&…

【Linux】HTTP协议1

欢迎来到Cefler的博客&#x1f601; &#x1f54c;博客主页&#xff1a;折纸花满衣 &#x1f3e0;个人专栏&#xff1a;题目解析 目录 &#x1f449;&#x1f3fb;http概念初识http协议格式 &#x1f449;&#x1f3fb;URL&#x1f449;&#x1f3fb;简单实现http协议&#xff…

5G前传光纤传输的25G光模块晶振SG2016CAN

一款适用于5G前传光纤传输网络中的25G光模块的5G晶振SG2016CAN。随着5G时代的到来&#xff0c;5G晶振的重要性也不言而喻&#xff0c;小体积宽温晶振SG2016CAN可以用于5G前传的25G光模块&#xff0c;具有高稳定性、小体积、宽温等优势。在5G前传光纤传输网络中&#xff0c;25G光…

java.sql.SQLDataException: Cannot determine value type from string 报错

报错 "org.springframework.dao.DataIntegrityViolationException: Error attempting to get column title from result set. Cause: java.sql.SQLDataException: Cannot determine value type from string 标题\n; Cannot determine value type from string 标题; neste…

ssm089理发店会员管理系统的设计和实现+vue

理发店会员管理系统的设计与实现 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和…

Redis入门到通关之数据结构解析-SkipList

文章目录 ☃️概述☃️总结 欢迎来到 请回答1024 的博客 &#x1f353;&#x1f353;&#x1f353;欢迎来到 请回答1024的博客 关于博主&#xff1a; 我是 请回答1024&#xff0c;一个追求数学与计算的边界、时间与空间的平衡&#xff0c;0与1的延伸的后端开发者。 博客特色&…

Python_AI库 Pandas在商业环境中的实际用途

Python_AI库 Pandas在商业环境中的实际用途 在前文中我们介绍了Pandas的各种常见操作&#xff0c;由于Pandas提供了高效、灵活且易于使用的数据结构&#xff0c;使得数据处理、清洗、分析和可视化变得更为简单&#xff0c;因此它广泛应用于各种商业应用中。 我们学习一个工具…

如何在Windows 10上刷新DNS?这里提供详细步骤

电脑的DNS缓存出现问题可能会导致连接到互联网时出现问题。如果你尝试过清除浏览器缓存和cookie等常见技巧&#xff0c;刷新Windows 10的DNS可能会解决你的问题。 DNS缓存的作用是什么 域网络系统&#xff08;DNS&#xff09;服务器将熟悉的域名转换为计算机用来相互连接的IP…

Stable Diffusion中的embedding

Stable Diffusion中的embedding 嵌入&#xff0c;也称为文本反转&#xff0c;是在 Stable Diffusion 中控制图像样式的另一种方法。在这篇文章中&#xff0c;我们将学习什么是嵌入&#xff0c;在哪里可以找到它们&#xff0c;以及如何使用它们。 什么是嵌入embedding&#xf…

linux的压缩与备份

一、打包 格式&#xff1a;tar -参数 <打包文件名> <打包的目标> 作用&#xff1a;将文件或者目录打包 重要参数&#xff1a;-f 使用归档文件&#xff0c;一定要加上这个参数 -c 新建打包文件 -x 解包文件 -t 可以不用解包就能查看包文件内容 -v 打包和解包时显…