面试篇-Redis-1缓存三兄弟+数据一致性

news2024/10/6 6:10:32

文章目录

  • 前言
  • 一、你们项目中使用Redis都做了什么:
  • 二、使用过程中遇到缓存穿透,缓存击穿,缓存雪崩你们如何处理:
    • 2.1 缓存穿透:
      • 2.1.1 通过缓存key值为null 进行处理:
      • 2.1.2 使用布隆过滤器:
      • 2.1.3 说说布隆过滤器的原理
    • 2.2 缓存击穿是什么:
      • 2.2.1缓存击穿的场景
      • 2.2.1 你们项目中是怎么处理的:
    • 2.3 缓存雪崩是什么:
  • 三、你们项目中缓存与数据库数据不一致怎么办:
    • 3.1 延时双删:
    • 3.2 延时双删一定能保证数据的一致性吗:
    • 3.3 怎么保证数据的一致性:
      • 3.3.1 强一致性场景使用锁:
      • 3.3.2 最终一致性:
  • 总结


前言

本文对Redis的使用场景及使用过程中遇到的问题进行总结,重点对面试的问题进行介绍,祝愿每位程序员都能上岸!!!


一、你们项目中使用Redis都做了什么:

Redis 相信各位小伙伴在项目中都用到过,在我们的项目中 通常使用Redis 进行:
  • 使用Redis 进行数据的缓存,避免每次都到数据库获取数据,达到高效数据获取和节约系统资源的目的;
  • 在分布式服务中对公用资源进行修改,使用Redis 作为分布式锁,保证并发的安全性;

二、使用过程中遇到缓存穿透,缓存击穿,缓存雪崩你们如何处理:

既然将部分数据存入Redis 中,随之而来的问题就是,从缓存中获取不到数据,从而到数据库进行数据查询,
造成数据库的压力,此时需要针对不同的场景进行不同的处理。

2.1 缓存穿透:

 当访问一个在redis 中不存在的key 时,此时就会到数据库进行数据产线,然而数据库也没有这个key 的数据,当然不会将改key
  缓存到redis 中去,但是一直有大量请求来访问这个在数据库中不存在的key,从而击垮数据库,这种情况通常是服务受到了攻击。

在这里插入图片描述

2.1.1 通过缓存key值为null 进行处理:

当数据库中也查不到改key 值的信息,则在redis 对改key 设置null 值 如: {key:1,value:null},并且设置下改key 的过期时间,这样在key 过期之前,访问改key 可以获取到null 数据,避免直接访问数据库;改方法的优点是:实现起来简单;缺点是:会造成数据的在缓存和数据库的不一致性;

伪代码如下(示例):

String value = redisTemplate.get(key);
if (null == value){
  // 从db 进性数据获取
  String  dbValue = getKeyValue()...
  if (null == dbValue){
  	 // 从数据库中获取不到数据设置key 为null
     redisTemplate.set(key,null,30s);
  }
}

2.1.2 使用布隆过滤器:

 1) 通过多维护一份数据到布隆过滤器中,查询数据时先到布隆过滤器访问key 是否存在,如果存在在进行redis 的访问;

在这里插入图片描述2) 布隆过滤器的原理:

 布隆过期是一个存放0,1位图的数组结构数据,通过算法将给定key 进行 hash 运算然后与数组的长度取余数得到具体的数组下标,
 经过多次运算就可以得到多个index 下标,此时就可以将对应数组小标的位置置为1;当查询的时候同样通过hash 运算得到几个
 下标,如果数组下标位置全为1 则认为改key 在redis 可能存在,否则这个key 在redis 一定不存在;

在这里插入图片描述
因为不同的key可能产生相同的hashcode 值,所以布隆过滤器有一定概率的误判:
在这里插入图片描述3) 布隆过滤器的优缺点

  • 占用较少的内存;
  • 存储redis中的数据也要在布隆过滤器存储一份
  • 布隆过滤器的元素一般不能够进行删除
  • 出现误判:布隆过滤器返回true 实际上这个key并不存在

4)在java 中的使用:
4.1) 引入Google Guava库的依赖:

<dependency>
	<groupId>com.google.guava</groupId>
	<artifactId>guava</artifactId>
	<version>30.1-jre</version> <!-- 请检查并使用最新版本 -->
</dependency>

4.2) 在将key 放入redis 的同时也放入到布隆过滤器一份:

我们首先指定了预期插入的元素数量(expectedInsertions)和期望的误报率(fpp)。然后,我们使用BloomFilter.create方法创建一个布隆过滤器,并使用字符串作为输入类型。我们使用Funnels.stringFunnel(StandardCharsets.UTF_8)来指定如何将字符串转换为字节数组,这是布隆过滤器所需的格式。
接下来,我们使用put方法向布隆过滤器中添加一些元素。然后,我们使用mightContain方法来检查一个元素是否可能存在于布隆过滤器中。请注意,由于布隆过滤器的性质,mightContain方法可能会产生误报(即返回true,但实际上元素并不存在于过滤器中),但永远不会产生误报(即返回false,但实际上元素存在于过滤器中)。

import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;

import java.nio.charset.StandardCharsets;

public class BloomFilterDemo {

public static void main(String[] args) {
	// 预期插入的元素数量
	int expectedInsertions = 1000000;
	
	// 误报率,通常设置为一个非常小的值,如0.01或更低
	double fpp = 0.01;
	
	// 创建布隆过滤器
	BloomFilter<CharSequence> bloomFilter = BloomFilter.create(
		Funnels.stringFunnel(StandardCharsets.UTF_8),
		expectedInsertions,
		fpp);
	
	// 插入一些元素
	bloomFilter.put("element1");
	bloomFilter.put("element2");
	// ... 插入其他元素
	
	// 检查元素是否存在(注意:可能会有误报)
	boolean mightContainElement1 = bloomFilter.mightContain("element1");
	System.out.println("mightContainElement1: " + mightContainElement1); // 预期输出: true
	
	boolean mightContainElement3 = bloomFilter.mightContain("element3");
	System.out.println("mightContainElement3: " + mightContainElement3); // 可能输出: false 或 true(误报)
	
	// 插入一个新元素后,再次检查
	bloomFilter.put("element3");
	boolean definitelyContainsElement3 = bloomFilter.mightContain("element3");
	System.out.println("definitelyContainsElement3: " + definitelyContainsElement3); // 预期输出: true
	}
}

2.1.3 说说布隆过滤器的原理

布隆过滤器主要是用于检索一个元素是否在一个集合中。我们当时使用的是redisson实现的布隆过滤忝。
它的底层主要是先去初始化一个比较大数组,里面存放的二进制0或1。在一开始都是0,当一个key来了之后经过3次hash计算,模于数组长度找到数据的下标然后把数组中原来的0改为1,这样的话,三个数组的位置就能标明一个key的存在。查找的过程也是一样的。
当然是有缺点的,布隆过滤器有可能会产生一定的误判,我们一般可以设置这个误判率,大概不会超过5%,其实这个误判是必然存在的,要不就得增加数组的长度,其实已经算是很划分了,5%以内的误判率一般的项目也能接受,不至于高并发下压倒数据库。

2.2 缓存击穿是什么:

2.2.1缓存击穿的场景

给某一个key设置了过期时间,当key过期的时候,恰好这时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮。

在这里插入图片描述

2.2.1 你们项目中是怎么处理的:

1)针对时间要求低的场景可以使用互斥锁来解决:

每次进行缓存重建的这个过程就先获取锁,然后查询db ,然后在将数据进行redis 缓存。

在这里插入图片描述
2)针对时间要求比较高的场景 热点数据的key 不设置缓存失效时间:

热点key 本身不设置过期时间,永不失效,但是在业务上对key 对应的value 增加一个有效期,标识数据的失效时间,这样每次都可以获取到数据,
如果数据过期了,则开启一个新的线程去更新数据,在缓存重建过程中获取互斥锁失败则直接返回已过期的数据;

在这里插入图片描述3) 两种方案对比:

  • 互斥锁的可以保证数据的高一致性,但是效率低
  • 逻辑互斥可以保证高可用,不能保证数据的绝对一致

2.3 缓存雪崩是什么:

缓存雪崩是redis 的key 在某一个时间段内,出现大量失效或者redis 服务崩溃,大量的key 直接访问数据库,击垮数据库

在这里插入图片描述
你们项目中怎么应对缓存雪崩问题:

  • 在设置key的过期时间时增加一个随机值,防止key 批量过期;
  • 使用redis集群保证redis 服务的高可用

三、你们项目中缓存与数据库数据不一致怎么办:

因为redis 和数据库是两个服务,在对数据进行更新的时候,因为redis 并没有实现XA协议,所以无法使用分布式事务,此时就要考虑,redis 和数据库
中的数据一致性的保持;

3.1 延时双删:

在大多数场景下都可以使用延时双删 进行处理,即先删除缓存数据,然后修改数据库数据,然后延时一定时间后再删除缓存:

在这里插入图片描述这里为什么要删除两次?

因为无法保证线程执行顺序,所以只有进行两次删除,确保后面线程可以读到数据库中最新的数据;
在这里插入图片描述

第二次为什么要进行一定的延迟:

  • 保证mysql 数据的落盘;
  • 确保mysql数据的主从同步;

3.2 延时双删一定能保证数据的一致性吗:

在对Mysql 进行集群时,常常会用到主-从架构,在项目中也会用到Mysql 的读写分离,此时如果在 延时一定时间之后,数据依然没有从Master节点同步到Slave 节点,此时线程访问redis 的某个key 发现没有数据,则会从数据库中加载 到脏数据,出现数据不一致性;

3.3 怎么保证数据的一致性:

3.3.1 强一致性场景使用锁:

1) 使用互斥锁:
在这里插入图片描述

2) 使用redisson读写锁:
在这里插入图片描述
代码实现参考:
在这里插入图片描述

3.3.2 最终一致性:

1) 更新数据库后发送MQ消息,然后消费MQ消息进行数据更新:
在这里插入图片描述

2)Canal 监听Mysql 特定表的某些修改:

在这里插入图片描述


总结

本位对Redis 常见的缓存穿透,缓存击穿,缓存雪崩,数据库和缓存数据一致性进行总结。

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

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

相关文章

OpenCV基础(2)

目录 滤波处理 均值滤波 基本原理 函数用法 程序示例 高斯滤波 基本原理 函数用法 程序示例 中值滤波 基本原理 函数用法 程序示例 形态学 腐蚀 膨胀 通用形态学函数 前言&#xff1a;本部分是上一篇文章的延续&#xff0c;前面部分请查看&#xff1a;OpenCV…

深入理解如何撤销 Git 中不想提交的文件

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

图增强LLM + 可穿戴设备实时数据,生成个性化健康见解

图增强LLM 可穿戴设备实时数据&#xff0c;生成个性化健康见解 提出背景图增强LLM 子解法1&#xff08;使用层次图模型&#xff09; 子解法2&#xff08;动态数据整合&#xff09; 子解法3&#xff08;LLM引导评估&#xff09; 提出背景 论文&#xff1a;https://arxiv.or…

【js正则】去除文本中的a标签及其内容

场景&#xff1a;有时候服务端返回的文本中&#xff0c;包含a标签&#xff0c;前端不需要展示。 // 示例 const inputText 【提醒&#xff1a;XXXX】\nXXXXXX: 1\n\n<a href"https://export.shobserver.com/baijiahao/html/767805.html">详情</a>;JS正…

【营销策划模型大全】私域运营必备

营销策划模型大全&#xff1a;战略屋品牌屋、电商运营模型、营销战略、新媒体运营模型、品牌模型、私域运营模型…… 该文档是一份策划总监工作模型的汇总&#xff0c;包括战略屋/品牌屋模型、营销战略模型、品牌相关模型、电商运营模型、新媒体运营模型和私域运营模型等&…

JavaScript基础-函数(完整版)

文章目录 函数基本使用函数提升函数参数arguments对象&#xff08;了解&#xff09;剩余参数(重点)展开运算符(...) 逻辑中断函数参数-默认参数函数返回值-return作用域(scope)全局作用域局部作用域变量的访问原则垃圾回收机制闭包 匿名函数函数表达式立即执行函数 箭头函数箭头…

全自动内衣洗衣机什么牌子好?四大热门内衣洗衣机多角度测评

内衣洗衣机是近几年新兴的一种家用电器产品&#xff0c;正日益引起人们的重视。但是&#xff0c;面对市面上品牌繁多、款式繁多的内衣洗衣机&#xff0c;使得很多人都不知道该如何选择。身为一个数码家电博主&#xff0c;我知道这类产品在挑选方面有着比较深入的了解。为此&…

AIGC对设计师积极性的影响

随着科技的迅猛发展&#xff0c;生成式人工智能&#xff08;AIGC&#xff09;工具正逐渐深入设计的每个角落&#xff0c;对设计师的工作方式和思维模式产生了深远的影响。AIGC不仅极大提升了设计师的工作效率&#xff0c;更激发了他们的创新思维&#xff0c;为设计行业带来了翻…

好文阅读-日志篇

https://mp.weixin.qq.com/s/jABbG4MKvEiWXwdYwUk8SA 这里直接看最佳实践。 Maven 依赖 <dependencyManagement><dependencies><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version>1.7.36…

聊聊 CTO 和 技术总监的区别

前言 CTO&#xff08;Chief Technology Officer&#xff09;&#xff0c;是首席技术官的意思。 技术总监&#xff0c;顾名思义&#xff0c;就是负责指导和监督公司的技术团队&#xff0c;确保技术和产品的开发与创新顺利进行。 有的软件公司同时有 CTO 和技术总监&#xff0…

第二届计算机、视觉与智能技术国际会议(ICCVIT 2024)

随着科技的飞速发展&#xff0c;计算机、视觉与智能技术已成为推动现代社会进步的重要力量。为了汇聚全球顶尖专家学者&#xff0c;共同探讨这一领域的最新研究成果和前沿技术&#xff0c;第二届计算机、视觉与智能技术国际会议&#xff08;ICCVIT 2024&#xff09;将于2024年1…

JAVA高级进阶11多线程

第十一天、多线程 线程安全问题 线程安全问题 多线程给我们带来了很大性能上的提升,但是也可能引发线程安全问题 线程安全问题指的是当个多线程同时操作同一个共享资源的时候,可能会出现的操作结果不符预期问题 线程同步方案 认识线程同步 线程同步 线程同步就是让多个线…

swiftui中几个常用的手势控制单击点击,双击和长按事件

简单做了一个示例代码&#xff0c;包含三个圆形形状&#xff0c;配置了不同的事件&#xff0c;示例代码&#xff1a; // // RouterView.swift // SwiftBook // // Created by song on 2024/7/4. //import SwiftUIstruct RouterView: View {State var isClick falsevar bod…

中实新材料:领航绿色建材新纪元,北京创新力量再攀高峰!

近期,北京中实新材料有限责任公司(以下简称“中实新材料”)以一系列耀眼的成果,彰显了其在绿色建材领域的卓越领导地位,不仅在生产效能、技术创新、市场拓展上取得了显著突破,更在社会责任与荣誉表彰上赢得了广泛赞誉。作为中关村科技发展(控股)股份有限公司旗下的璀璨明珠,中实…

Java - 程序员面试笔记记录 实现 - Part3

4.1 线程与进程 线程是程序执行的最小单元&#xff0c;一个进程可以拥有多个线程&#xff0c;各个线程之间共享程序的内存空间以及一些进程级资源&#xff0c;但拥有自己的栈空间。 4.3 Java 多线程 方法一&#xff1a;继承 Thread 类&#xff0c;重写 run 方法&#xff1b;…

实验五 数据库完整性约束的实现与验证

题目 在实验四的基础上&#xff0c;重新创建以下三个表&#xff1a; 会员表&#xff1a;member(memno,memname,address,telephone,username,userpwd)&#xff0c;主码为memno&#xff0c;属性memname不能取空值 员工表&#xff1a;employee(empno,empname,depno,sex,telephone…

【电源专题】DC-DC电路设计为什么一般只考虑电感DCR而不考虑Q值呢?

什么是电感器(线圈)的Q值&#xff1f; Q值是表示电感器质量的参数。Q是Quality Factor&#xff08;质量系数&#xff09;的简称。线圈会顺利流过直流电流&#xff0c;但会对交流电流产生电阻。这称为感抗&#xff0c;交流频率越高则越大。 此外&#xff0c;绕组虽是导体…

基于Istio的多网关运行时:配置、部署和应用

1. 引言 Istio是一个开源的服务网格&#xff0c;主要应用于简化微服务架构中的服务间通信、提供强大的监控能力以及加强服务的安全管理。通过利用Sidecar模式部署的Envoy代理&#xff0c;Istio能够在几乎无需修改服务代码的情况下&#xff0c;实现服务发现、负载均衡、加密通信…

C语言实战 | Flappy Bird游戏

Flappy Bird游戏是由一名越南游戏制作者独自开发的&#xff0c;曾经风靡全球。游戏规则非常简单&#xff0c;玩家必须控制一只小鸟&#xff0c;跨越由各种长度的水管所组成的障碍物&#xff0c;如果撞上管道游戏就结束&#xff0c;如图11.11所示。 ■ 图11.11Flappy Bird 游戏 …

go语言day08 泛型 自定义错误处理 go:协程

泛型&#xff1a; 抛错误异常 实现error接口类型 用java语言解释的话&#xff0c;实现类需要重写error类型的抽象方法Error().这样就可以自定义异常处理。 回到go语言&#xff0c;在Error()方法中用*argError 这样一个指针类来充当error接口的实现类。 在f2()方法中定义返回值…