面试题:Redis(三)

news2024/12/23 10:27:04

1. 面试题

背景

 

问题,上面业务逻辑你用java代码如何写?

 

2. 缓存双写一致性谈谈你的理解?

 3. 双检加锁策略

多个线程同时去查询数据库的这条数据,那么我们可以在第一个查询数据的请求上使用一个 互斥锁来锁住它

其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。

后面的线程进来发现已经有缓存了,就直接走缓存。 

4. 数据库和缓存一致性的几种更新策略

目的

给缓存设置过期时间,定期清理缓存并回写,是保证最终一致性的解决方案。

 

我们可以对存入缓存的数据设置过期时间,所有的写操作以数据库为准,对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存,达到一致性,切记,要以mysql的数据库写入库为准

4.1 可以停机的情况下

凌晨维护,服务降级

单线程,这种重量级数据操作最好不要多线程 

4.2 不停机情况下四种更新策略

4.2.1 先更新数据库在更新缓存

异常情况1

 1 先更新mysql的某商品的库存,当前商品的库存是100,更新为99个。

 

 2 先更新mysql修改为99成功,然后更新redis。

 

 3 此时假设异常出现,更新redis失败了,这导致mysql里面的库存是99而redis里面的还是100 。

 

 4  上述发生,会让数据库里面和缓存redis里面数据不一致,读到redis脏数据

异常情况2

【先更新数据库,再更新缓存】,A、B两个线程发起调用

【正常逻辑】

1 A update mysql 100

2 A update redis 100

3 B update mysql 80

4 B update redis 80

=============================

【异常逻辑】多线程环境下,A、B两个线程有快有慢,有前有后有并行

1 A update mysql 100

3 B update mysql 80

4 B update redis 80

2 A update redis 100

 =============================

最终结果,mysql和redis数据不一致,o(╥﹏╥)o,

mysql80,redis100

4.2.2 先更新缓存在更新数据库

不推荐该做法,因为业务上一般以mysql数据库作为底单数据库(类似于数据原件),所有数据以MySQL数据库为准,而redis可以理解为副本 (拷贝)

异常情况

【先更新缓存,再更新数据库】,A、B两个线程发起调用

【正常逻辑】

1 A update redis 100

2 A update mysql 100

3 B update redis 80

4 B update mysql 80

====================================

【异常逻辑】多线程环境下,A、B两个线程有快有慢有并行

A update redis  100

B update redis  80

B update mysql 80

A update mysql 100

 

----mysql100,redis80

4.2.3 先删缓存再更新数据库

异常情况

(1)请求A进行写操作,删除redis缓存后,工作正在进行中,更新mysql......A还么有彻底更新完mysql,还没commit

(2)请求B开工查询,查询redis发现缓存不存在(被A从redis中删除了)

(3)请求B继续,去数据库查询得到了mysql中的旧值(A还没有更新完)

(4)请求B将旧值写回redis缓存

(5)请求A将新值写入mysql数据库 

上述情况就会导致不一致的情形出现。 

时间
线程A
线程B
出现的问题
t1
请求A进行写操作,删除缓存成功后,工作正在mysql进行中......
t2
1 缓存中读取不到,立刻读mysql,由于A还没有对mysql更新完,读到的是旧值
2 还把从mysql读取的旧值,写回了redis
1 A还没有更新完mysql,导致B读到了旧值
2 线程B遵守回写机制,把旧值写回redis,导致其它请求读取的还是旧值,A白干了。
t3
A更新完mysql数据库的值,over
redis是被B写回的旧值,
mysql是被A更新的新值。
出现了,数据不一致问题。

总结一下:

先删除缓存,再更新数据库
如果数据库更新失败或超时或返回不及时,导致B线程请求访问缓存时发现redis里面没数据,缓存缺失,B再去读取mysql时, 从数据库中读取到旧值,还写回redis,导致A白干了,o(╥﹏╥)o
 异常解决方案:延迟双删

假设有A、B两个线程,A先把缓存进行删除,此时正在写入数据(但未完成),B进行查询操作,发现缓存内没有命令数据就到MySQL数据中进行数据查询,因为线程A还未完成更新数据,故B线程查到的为旧数据并写入到缓存中,当线程B完成以上一系列操作后,A线程完成数据更新就会把缓存的数据再次删除(延迟双删),当下一个线程来查询数据时发现缓存没有就会到数据库中重新查询,此时查询并回写的数据为最新的数据,保证了数据一致性

 延迟双删面试题

1. 这个删除该休眠多久

线程A sleep的时间,就需要大于线程B读取数据再写入缓存的时间。

 

这个时间怎么确定呢?

 第一种方法:

在业务程序运行的时候,统计下线程读数据和写缓存的操作时间,自行评估自己的项目的读数据业务逻辑的耗时,

以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可。

 

 

这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据。

 

 第二种方法:

 

新启动一个后台监控程序,比如后面要讲解的WatchDog监控程序,会加时

2. 这种同步淘汰策略,吞吐量降低怎么解决?

异步删除 

 

4.2.4 先更数据库在删除缓存 

异常问题

 先更新数据库,再删除缓存

时间
线程A
线程B
出现的问题
t1
更新数据库中的值......
t2
 缓存中立刻命中,此时B读取的是缓存旧值。
A还没有来得及删除缓存的值,导致B缓存命中读到旧值。
t3
更新缓存的数据,over
先更新数据库,再删除缓存
假如缓存删除失败或者来不及,导致请求再次访问redis时缓存命中, 读取到的是缓存旧值。

解决方案

1 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka/RabbitMQ等)。
2 当程序没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新。
3 如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了,否则还需要再次进行重试
4 如果重试超过的一定次数后还是没有成功,我们就需要向业务层发送报错信息了,通知运维人员。

 

总结

在大多数业务场景下, 

个人建议是(仅代表我个人,不权威),优先使用先更新数据库,再删除缓存的方案(先更库→后删存)。理由如下:

 

1 先删除缓存值再更新数据库,有可能导致请求因缓存缺失而访问数据库,给数据库带来压力导致打满mysql。

 

2 如果业务应用中读取数据库和写缓存的时间不好估算,那么,延迟双删中的等待时间就不好设置。

 

 多补充一句:如果使用先更新数据库,再删除缓存的方案

如果业务层要求必须读取一致性的数据,那么我们就需要在更新数据库时,先在Redis缓存客户端暂停并发读请求,等数据库更新完、缓存值删除后,再读取数据,从而保证数据一致性,这是理论可以达到的效果,但
实际,不推荐,因为真实生产环境中,分布式下很难做到实时一致性, 一般都是最终一致性,请大家参考。

策略
高并发多线程条件下
问题
现象
解决方案
先删除redis缓存,再更新mysql
缓存删除成功但数据库更新失败
Java程序从数据库中读到旧值
再次更新数据库,重试
缓存删除成功但数据库更新中......
有并发读请求
并发请求从数据库读到旧值并回写到redis,导致后续都是从redis读取到旧值
延迟双删
先更新mysql,再删除redis缓存
数据库更新成功,但缓存删除失败
Java程序从redis中读到旧值
再次删除缓存,重试
数据库更新成功但缓存删除中......
有并发读请求
并发请求从缓存读到旧值
等待redis删除完成,这段时间有
数据不一致,短暂存在。

 

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

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

相关文章

内核编译 设备驱动 驱动程序

内核编译 一、内核编译的步骤 编译步骤: (linux 内核源码的顶层目录下操作 ) 1. 拷贝默认配置到 .config cp config_mini2440_td35 .config 2. make menuconfig 内核配置 make menuconfig 3. make uImage make u…

docker-compose无法切换用户

问题描述 jupyter:image: flink:1.19-pyprivileged: trueuser: rootports:- "9999:8888"volumes:- /data/docker_data/jupyter:/workcommand: sh -c "cd / && jupyter notebook --ip 0.0.0.0 --port 8888 --allow-root --NotebookApp.passwordsha1:658…

循环神经网络-LSTM网络

文章目录 前言一、LSTM网络简介二、LSTM的门结构1.遗忘门2.输入门3.输出门 三、总结 前言 循环神经网络(Recurrent Neural Networks,RNN)是一种特殊的神经网络,具有能够处理序列数据的能力,然而,RNN在处理…

团员申请书怎么写?这里归纳了一些模板

团员申请书怎么写?随着社会的快速发展和时代的进步,越来越多的青年人意识到加入团组织的重要性。作为新时代的青年,我们应当积极响应国家的号召,参与到团组织的建设中来。而想要成为共青团员,首先需要撰写一份规范的团…

新手一次过软考高级(系统架构设计师)秘笈,请收藏!

软考系统架构设计师是高级科目之一,也是比较有难度的科目,但是只要你把该掌握的知识掌握熟练,技能水平达到要求,那还是考可以拿下证书的。 系统架构设计师适合人群:适合熟悉开发过程与方法、数据库、信息安全的技术人员…

NLP: SBERT介绍及sentence-transformers库的使用

1. Sentence-BERT Sentence-BERT(简写SBERT)模型是BERT模型最有趣的变体之一,通过扩展预训练的BERT模型来获得固定长度的句子特征,主要用于句子对分类、计算两个句子之间的相似度任务。 1.1 计算句子特征 SBERT模型同样是将句子标记送入预训练的BERT模型…

OmniH2O——通用灵巧且可全身远程操作并学习的人形机器人(其前身H2O是HumanPlus的重要参考)

前言 由于我司一直在针对各个工厂、公司、客户特定的业务场景,做解决方案或定制开发,所以针对每一个场景,我们都会反复考虑用什么样的机器人做定制开发 于此,便不可避免的追踪国内外最前沿的机器人技术进展,本来准备…

数据库管理-第249期 23ai:全球分布式数据库-请求路由与查询过程(20241008)

数据库管理249期 2024-10-08 数据库管理-第249期 23ai:全球分布式数据库-请求路由与查询过程(20241008)1 客户端应用请求路由1.1 分片键1.2 Oracle连接驱动 2 查询过程和查询协调器2.1 指定一致性级别2.2 高可用与性能 总结 数据库管理-第249…

拍立淘API接口以图搜商品列表功能实现技术分享item_search_img|返回商品列表商品id商品价格url

开发背景 在电商平台的快速发展中,用户对于商品搜索的效率和准确性提出了越来越高的要求。传统的基于关键词的搜索方式,虽然在一定程度上满足了用户的需求,但在面对复杂的商品信息和多样化的用户搜索意图时,仍存在诸多局限性。为…

PyTorch搭建GNN(GCN、GraphSAGE和GAT)实现多节点、单节点内多变量输入多变量输出时空预测

目录 I. 前言II. 数据集说明III. 模型3.1 GCN3.2 GraphSAGE3.3 GAT IV. 训练与测试V. 实验结果 I. 前言 前面已经写了很多关于时间序列预测的文章: 深入理解PyTorch中LSTM的输入和输出(从input输入到Linear输出)PyTorch搭建LSTM实现时间序列…

IO相关,标准输入输出及错误提示

一、IO简介 1.1 IO的过程 操作系统的概念:向下统筹控制硬件,向上为用户提供接口。 操作系统的组成 内核 外壳(shell) linux的五大功能:进程管理、内存管理、文件管理、设备管理、网络管理。 最早接触的IO&#xf…

01背包,CF 1974E - Money Buys Happiness

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 1974E - Money Buys Happiness 二、解题报告 1、思路分析 问我们能够到达…

docker简述

1.安装dockers,配置docker软件仓库 安装,可能需要开代理,这里我提前使用了下好的包安装 启动docker systemctl enable --now docker查看是否安装成功 2.简单命令 拉取镜像,也可以提前下载使用以下命令上传 docker load -i imag…

深度学习笔记(持续更新)

注:本文所有深度学习内容都是基于PyTorch,PyTorch作为一个开源的深度学习框架,具有可以动态计算图、拥有简洁易用的API、支持GPU加速等特点,在计算机视觉、自然语言处理、强化学习等方面有广泛应用。 使用matplotlib绘图&#xff…

Linux 常用命令详解,线上问题排查必备

comm 比较文件行 comm 是 Linux 系统下的用于比较两个已排序文件的命令行工具。主要用于找出文件之间的差异或相同之处,例如两个文件中相同的行、仅在第一个文件中的行以及仅在第二个文件中的行。 基本语法 comm [OPTION] FILE1 FILE2可选参数OPTION如下&#xf…

图像分类-demo(Lenet),tensorflow和Alexnet

目录 demo(Lenet) 代码实现基本步骤: TensorFlow 一、核心概念 二、主要特点 三、简单实现 参数: 模型编译 模型训练 模型评估 Alexnet model.py train.py predict.py demo(Lenet) PyTorch提供了一个名为“torchvision”的附加库,其中包含…

芯课堂 | FatFs文件系统的移植及应用指南

1、FatFs文件系统简介 FatFs是用于小型嵌入式系统的通用FAT/exFAT文件系统模块。FatFs模块是按照ANSI C(C89)编写的,与磁盘控制层完全分离。因此,它独立于平台和存储设备,具有良好的硬件平台独立性。它可以集成到资源有…

这个问题做项目的时给某些客户普及过,这里再给你普及一下

有些因素不是地理概念,没错!但与地理有关!可以通过地理位置将他们链接起来,再结合其它业务数据,完成数据分析!例如百度地图会将:餐饮、文化、交通、住宿、甚至价格、天气与位置关联分析&#xf…

S7---基本介绍

目录 高通S7和S7 Pro Gen 1声音平台 音频性能的新层次 高通XPAN技术 卓越的听力增强 高通第四代ANC 特征 QualcommS7 Pro Gen 1附加功能 QualcommS7 Pro Gen 1框图 高通S7和S7 Pro Gen 1声音平台 声音被重新想象。QualcommS7声音平台旨在开启一个新的高级音频性能级别。…

Unity转Unreal5之从入门到精通 Spline(样条曲线)组件的使用

前言 Spline 组件 能编辑 样条曲线,定义一条路径,路径上的点可以通过距离起点的长度获取,因此可以实现 物体沿路径连续移动 的效果或者 物体沿路径分布 的效果。 今天我们就来实现一个简单的Spline样条曲线的Demo 实现一个沿路径运动的功能 1.新建一个基于 Actor 的蓝图…