当前位置: 首页 > news >正文

【Redis】如何保证Redis缓存与数据库的一致性?

文章目录

  • 1、四种同步策略
  • 2、更新缓存还是删除缓存
    • 2.1 更新缓存
    • 2.2 删除缓存
  • 3、先操作数据库还是缓存
    • 3.1 先删除缓存再更新数据库
    • 3.2 先更新数据库再删除缓存
  • 4、延时双删
    • 4.1 采用读写分离的架构怎么办?
  • 5、利用消息队列进行删除的补偿

1、四种同步策略

想要保证缓存与数据库的双写一致,一共有4种方式,即4种同步策略:

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

从这4种同步策略中,我们需要作出比较的是:

更新缓存与删除缓存哪种方式更合适?应该先操作数据库还是先操作缓存?

2、更新缓存还是删除缓存

下面,我们来分析一下,应该采用更新缓存还是删除缓存的方式。

2.1 更新缓存

  • 优点:每次数据变化都及时更新缓存,所以查询时不容易出现未命中的情况。

  • 缺点:更新缓存的消耗比较大。如果数据需要经过复杂的计算再写入缓存,那么频繁的更新缓存,就会影响服务器的性能。如果是写入数据频繁的业务场景,那么可能频繁的更新缓存时,却没有业务读取该数据。

2.2 删除缓存

  • 优点:操作简单,无论更新操作是否复杂,都是将缓存中的数据直接删除。

  • 缺点:删除缓存后,下一次查询缓存会出现未命中,这时需要重新读取一次数据库。从上面的比较来看,一般情况下,删除缓存是更优的方案。

3、先操作数据库还是缓存

下面,我们再来分析一下,应该先操作数据库还是先操作缓存。
首先,我们将先删除缓存与先更新数据库,在出现失败时进行一个对比:

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

在这里插入图片描述

如上图,是先删除缓存再更新数据库,在出现失败时可能出现的问题:

  • 线程A删除缓存成功,线程A更新数据库失败;
  • 线程B从缓存中读取数据;由于缓存被删,进程B无法从缓存中得到数据,进而从数据库读取数据;此时数据库中的数据更新失败,线程B从数据库成功获取旧的数据,然后将数据更新到了缓存。
  • 最终,缓存和数据库的数据是一致的,但仍然是旧的数据

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

在这里插入图片描述

如上图,是先更新数据库再删除缓存,在出现失败时可能出现的问题:

  • 线程A更新数据库成功,线程A删除缓存失败;
  • 线程B读取缓存成功,由于缓存删除失败,所以线程B读取到的是缓存中旧的数据。
  • 最后线程A删除缓存成功,有别的线程访问缓存同样的数据,与数据库中的数据是一样。
  • 最终,缓存和数据库的数据是一致的,但是会有一些线程读到旧的数据。

经过上面的比较,我们发现在出现失败的时候,是无法明确分辨出先删缓存和先更新数据库哪个方式更好,以为它们都存在问题。后面我们会进一步对这两种方式进行比较,但是在这里我们先探讨一下,上述场景出现的问题,应该如何解决呢?

实际上,无论上面我们采用哪种方式去同步缓存与数据库,在第二步出现失败的时候,都建议采用重试机制解决,上面两幅图中已经画了。

下面我们再将先删缓存与先更新数据库,在没有出现失败时进行对比:
在这里插入图片描述

如上图,是先删除缓存再更新数据库,在没有出现失败时可能出现的问题:

  • 线程A删除缓存成功;
  • 线程B读取缓存失败;
  • 线程B读取数据库成功,得到旧的数据;
  • 线程B将旧的数据成功地更新到了缓存;
  • 线程A将新的数据成功地更新到数据库。

可见,进程A的两步操作均成功,但由于存在并发,在这两步之间,进程B访问了缓存。最终结果是,缓存中存储了旧的数据,而数据库中存储了新的数据,二者数据不一致。

在这里插入图片描述

如上图,是先更新数据库再删除缓存,在没有出现失败时可能出现的问题:

  • 线程A更新数据库成功;
  • 线程B读取缓存成功;
  • 线程A删除缓存成功。

可见,最终缓存与数据库的数据是一致的,并且都是最新的数据。但线程B在这个过程里读到了旧的数据,可能还有其他线程也像线程B一样,在这两步之间读到了缓存中旧的数据,但因为这两步的执行速度会比较快,所以影响不大。对于这两步之后,其他进程再读取缓存数据的时候,就不会出现类似于进程B的问题了。

最终结论:

经过对比你会发现,先更新数据库、再删除缓存是影响更小的方案。如果第二步出现失败的情况,则可以采用重试机制解决问题。

4、延时双删

上面我们提到,如果是先删缓存、再更新数据库,在没有出现失败时可能会导致数据的不一致。如果在实际的应用中,出于某些考虑我们需要选择这种方式,那有办法解决这个问题吗?答案是有的,那就是采用延时双删的策略,延时双删的基本思路如下:

  • 删除缓存;
  • 更新数据库;
  • sleep N毫秒;
  • 再次删除缓存。
public void write(String key, Object data) {Redis.delKey(key);db.updateData(data);Thread.sleep(1000);Redis.delKey(key);
}

阻塞一段时间之后,再次删除缓存,就可以把这个过程中缓存中不一致的数据删除掉。而具体的时间,要评估你这项业务的大致时间,按照这个时间来设定即可。

4.1 采用读写分离的架构怎么办?

如果数据库采用的是读写分离的架构,那么又会出现新的问题,如下图:
在这里插入图片描述

此时来了两个请求,请求 A(更新操作) 和请求 B(查询操作)

  • 请求 A 更新操作,删除了 Redis;
  • 请求主库进⾏更新操作,主库与从库进行同步数据的操作;
  • 请 B 查询操作,发现 Redis 中没有数据;
  • 去从库中拿去数据;
  • 此时同步数据还未完成,拿到的数据是旧数据;

此时的解决办法就是如果是对 Redis 进行填充数据的查询数据库操作,那么就强制将其指向主库进⾏查询。

删除失败了怎么办?

如果删除依然失败,则可以增加重试的次数,但是这个次数要有限制,当超出一定的次数时,要采取报错、记日志、发邮件提醒等措施。

5、利用消息队列进行删除的补偿

先更新数据库,后删除缓存这⼀种情况也会出现问题,比如更新数据库成功了,但是在删除缓存的阶段出错了没有删除成功,那么此时再读取缓存的时候每次都是错误的数据了。
在这里插入图片描述

此时解决方案就是利用消息队列进行删除的补偿。具体的业务逻辑⽤语⾔描述如下:

  • 请求 线程A 先对数据库进行更新操作;
  • 在对 Redis 进行删除操作的时候发现报错,删除失败;
  • 此时将Redis 的 key 作为消息体发送到消息队列中;
  • 系统接收到消息队列发送的消息后再次对 Redis 进行删除操作;

但是这个方案会有⼀个缺点就是会对业务代码造成大量的侵入,深深的耦合在⼀起,所以这时会有⼀个优化的方法,我们知道对 Mysql 数据库更新操作后再 binlog 日志中我们都能够找到相应的操作,那么我们可以订阅 Mysql 数据库的 binlog 日志对缓存进行操作。

在这里插入图片描述

相关文章:

【Redis】如何保证Redis缓存与数据库的一致性?

文章目录 1、四种同步策略2、更新缓存还是删除缓存2.1 更新缓存2.2 删除缓存 3、先操作数据库还是缓存3.1 先删除缓存再更新数据库3.2 先更新数据库再删除缓存 4、延时双删4.1 采用读写分离的架构怎么办? 5、利用消息队列进行删除的补偿 1、四种同步策略 想要保证缓…...

MATLAB中ischange函数用法

目录 语法 说明 示例 均值的变化 线性区的变化 矩阵数据 ischange函数的功能是查找数据中的突然变化。 语法 TF ischange(A) TF ischange(A,method) TF ischange(___,dim) TF ischange(___,Name,Value) [TF,S1] ischange(___) [TF,S1,S2] ischange(___) 说明 ​…...

【React + Ant Design】表单如何在前置项未填写时禁止后置项交互并提示

在 react antd 中,对表单做在前置项未填写时禁用后置项交互并提示的效果。 情景 最近有这么个需求,某个业务中,要填写一张表单,其中有这样两项:选择数据连接和选择数据表,数据表是数据连接下所拥有的表。…...

Linux学习之MySQL建表

MySQL查询1 MySQL查询2 表管理 #1. 建库#1)库名命名规则仅可以使用数字、字母、下划线、不能纯数字,区分字母大小写,具有唯一性,不可使用MySQL命令或特殊字符#创建数据表时可以查看一下默认的字符集,8.0后创建数据库…...

Redis哨兵集群的介绍及搭建

Redis 是一款开源的、内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。然而,作为一个单点服务,Redis 在面临硬件故障或者网络问题时可能会导致服务不可用。为了解决这个问题,Redis 提供了哨兵模式,一个…...

【zookeeper】zookeeper日常运维

本文将分享一些zookeeper在日常使用中一些维护经验。 zookeeper清理快照 脚本或者命令清理 zookeeper长时间运行,快照逐渐增多可能造成服务器磁盘被占满的情况,但我们不能贸然用rm命令删除快照文件,如果直接删完会导致丢失好多数据&#x…...

【工作记录】MQTT介绍、安装部署及springboot集成@20230912

背景 近期公司可能会有物联网设备相关项目内容,提前对用到的mqtt协议做预研和初步使用。 最初接触到mqtt协议应该是早些年的即时通讯吧,现在已经是物联网设备最热门的协议了。 作为记录,也希望能帮助到需要的朋友。 MQTT介绍 《MQTT 协议规…...

Flask 使用 JWT(一)

下面是一些 JWT 的使用场景: 1、 授权:这是 JWT 最常的使用场景。一旦用户登录,后续的每个请求都必须携带 JWT ,允许用户携带 Token 访问所有的路由、服务器和资源。单点登录时目前使用最广泛的一个场景,因为它开销小并且能够轻易的实现跨域访问。 2、信息交换:JWT Token…...

Oracle(1):Oracle简介

1 什么是 ORACLE ORACLE 数据库系统是美国 ORACLE 公司(甲骨文)提供的以分布式数据库为核心的一组软件产品,是目前最流行的客户/服务器(CLIENT/SERVER)或B/S 体系结构的数据库之一。 ORACLE 通常应用于大型系统的数据库产品。 ORACLE 数据…...

计算机网络篇之IP地址

计算机网络篇之IP地址 文章目录 计算机网络篇之IP地址概括IPv4地址IPv6地址分配总结 概括 IP地址是计算机网络中用于标识和定位设备的一组数字,IP地址分为IPv4和IPv6两种格式 IPv4地址 IPv4地址是32位的二进制数,通常表示为四个用点分隔的十进制数&am…...

webrtc-m79-测试peerconnectionserver的webclient-p2p-demo

1 背景 webrtc的代码中有peerconnectionclient和peerconnectionserver的例子,但是没有对应的web端的例子,这里简单的写了一个测试例子,具体如下: 2 具体操作 2.1 操作流程 2.2 测试效果 使用webclient与peerconnectionclient的…...

C#,《小白学程序》第十五课:随机数(Random)第二,统计学初步,数据统计的计算方法与代码

1 文本格式 /// <summary> /// 《小白学程序》第十五课&#xff1a;随机数&#xff08;Random&#xff09;第二&#xff0c;统计学初步&#xff0c;数据统计的计算方法与代码 /// 用随机数做简单的统计并用图形显示统计结果。 /// </summary> /// <param name&q…...

C# 子类如何访问子类的方法(同一父类)

在继承关系中&#xff0c;子类可以通过创建另一个子类的对象来访问其方法。下面是一个示例&#xff0c;展示了子类如何访问另一个子类的方法&#xff1a; public class Animal {public virtual void Speak(){Console.WriteLine("我是动物。");} }public class Cat :…...

《Docker 容器化的艺术:深入理解容器技术》

&#x1f337;&#x1f341; 博主猫头虎&#xff08;&#x1f405;&#x1f43e;&#xff09;带您 Go to New World✨&#x1f341; &#x1f405;&#x1f43e;猫头虎建议程序员必备技术栈一览表&#x1f4d6;&#xff1a; &#x1f6e0;️ 全栈技术 Full Stack: &#x1f4da…...

gitlab配置hook,commit message的时候校验提交的信息

在 GitLab 中配置 Webhook 来调用 Java 接口以校验 commit 信息&#xff0c;是很多公司的一些要求&#xff0c;因为提交信息的规范化是必要的 不阻止commit的版本 在 GitLab 项目中进入设置页面。 在左侧导航栏中选择 “Webhooks”&#xff08;Web钩子&#xff09;。 在 We…...

ssh远程管理服务

ssh远程管理服务是什么 SSH是一个安全协议&#xff0c;在进行数据传输时&#xff0c;会对数据包进行加密处理&#xff0c;加密后在进行数据传输。确保了数据传输安全, 那SSH服务主要功能有哪些呢&#xff1f; 1.提供远程连接服务器的服务 1&#xff09;linux远程连接协议&…...

C语言顺序表

文章目录 前言线性表顺序表静态顺序表动态顺序表 接口实现 前言 我们先补一下上篇博客落下的知识点&#xff1a; 首先说一下斐波那契的时间复杂度和空间复杂度&#xff1a; long long Fac(size_t N) {if(0 N)return 1;return Fac(N-1)*N; }还是说一下size_t代表的类型是unsi…...

滑动窗口详解

滑动窗口本质其实也是一种双指针算法&#xff0c;只是因为它维护的区间随着遍历的进行在不停变化&#xff0c;所以形象地称为“滑动窗口” 一、⻓度最⼩的⼦数组 题目要求找到满足条件的长度最小的子数组&#xff0c;我们先来想想暴力的做法&#xff0c;再来想想能不能优化&am…...

JAVA -华为真题-分奖金

需求: 公司老板做了一笔大生意&#xff0c;想要给每位员工分配一些奖金&#xff0c;想通过游戏的方式来决定每个人分多少钱。按照员工的工号顺序&#xff0c;每个人随机抽取一个数字。按照工号的顺序往后排列&#xff0c;遇到第一个数字比自己数字大的&#xff0c;那么&#xf…...

第二章:25+ Python 数据操作教程(第十八节如何使用 Matplotlib 库在 python 中执行绘图和数据可视化)持续更新中

本教程概述了如何使用 Matplotlib 库在 python 中执行绘图和数据可视化。这篇文章的目的是让您熟悉该库的基础知识和高级绘图功能。它包含几个示例,将为您提供使用 Python 生成绘图的实践经验。 目录 什么是 Matplotlib? Matplotlib 基础知识<...

AtCoder 第409​场初级竞赛 A~E题解

A Conflict 【题目链接】 原题链接&#xff1a;A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串&#xff0c;只有在同时为 o 时输出 Yes 并结束程序&#xff0c;否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

图表类系列各种样式PPT模版分享

图标图表系列PPT模版&#xff0c;柱状图PPT模版&#xff0c;线状图PPT模版&#xff0c;折线图PPT模版&#xff0c;饼状图PPT模版&#xff0c;雷达图PPT模版&#xff0c;树状图PPT模版 图表类系列各种样式PPT模版分享&#xff1a;图表系列PPT模板https://pan.quark.cn/s/20d40aa…...

逻辑回归暴力训练预测金融欺诈

简述 「使用逻辑回归暴力预测金融欺诈&#xff0c;并不断增加特征维度持续测试」的做法&#xff0c;体现了一种逐步建模与迭代验证的实验思路&#xff0c;在金融欺诈检测中非常有价值&#xff0c;本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...

elementUI点击浏览table所选行数据查看文档

项目场景&#xff1a; table按照要求特定的数据变成按钮可以点击 解决方案&#xff1a; <el-table-columnprop"mlname"label"名称"align"center"width"180"><template slot-scope"scope"><el-buttonv-if&qu…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...

Appium下载安装配置保姆教程(图文详解)

目录 一、Appium软件介绍 1.特点 2.工作原理 3.应用场景 二、环境准备 安装 Node.js 安装 Appium 安装 JDK 安装 Android SDK 安装Python及依赖包 三、安装教程 1.Node.js安装 1.1.下载Node 1.2.安装程序 1.3.配置npm仓储和缓存 1.4. 配置环境 1.5.测试Node.j…...

JavaScript 标签加载

目录 JavaScript 标签加载script 标签的 async 和 defer 属性&#xff0c;分别代表什么&#xff0c;有什么区别1. 普通 script 标签2. async 属性3. defer 属性4. type"module"5. 各种加载方式的对比6. 使用建议 JavaScript 标签加载 script 标签的 async 和 defer …...

EEG-fNIRS联合成像在跨频率耦合研究中的创新应用

摘要 神经影像技术对医学科学产生了深远的影响&#xff0c;推动了许多神经系统疾病研究的进展并改善了其诊断方法。在此背景下&#xff0c;基于神经血管耦合现象的多模态神经影像方法&#xff0c;通过融合各自优势来提供有关大脑皮层神经活动的互补信息。在这里&#xff0c;本研…...