当前位置: 首页 > 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 基础知识<...

2026廊坊硅酸铝柔性包裹,防火专业厂家这样选

最近在跑几个建筑机电工程&#xff0c;跟不少项目经理、施工队负责人聊了聊&#xff0c;发现大家不约而同遇到了同一个坎儿——管道防火验收。尤其是湿式报警阀间、排烟管道这些“硬骨头”&#xff0c;防火包裹的材质、阻燃等级、贴合度&#xff0c;直接决定了消防验收能不能一…...

Godot MCP服务器:AI助手与游戏开发工作流的高效集成方案

1. 项目概述&#xff1a;为什么我们需要一个更好的Godot MCP&#xff1f;如果你是一个Godot引擎的开发者&#xff0c;尤其是当你尝试将AI能力集成到你的游戏开发工作流中时&#xff0c;你很可能听说过或者用过MCP&#xff08;Model Context Protocol&#xff09;。简单来说&…...

GLM-ASR开源语音识别引擎:基于GLM架构的端到端实践指南

1. 项目概述&#xff1a;一个开源的、基于GLM架构的语音识别引擎最近在语音识别&#xff08;ASR&#xff09;这个圈子里&#xff0c;一个名为“GLM-ASR”的开源项目引起了我的注意。它来自zai-org组织&#xff0c;顾名思义&#xff0c;其核心是将自然语言处理领域大放异彩的GLM…...

构建安全代码执行沙箱:基于容器与系统调用的多层隔离实践

1. 项目概述&#xff1a;安全代码执行的挑战与机遇 在软件开发、在线教育、自动化测试乃至安全研究领域&#xff0c;我们常常面临一个共同的难题&#xff1a;如何在一个受控、隔离的环境中&#xff0c;安全地执行一段来源未知或不可信的代码&#xff1f;无论是处理用户提交的在…...

CANN/ops-nn动态量化RMS归一化融合算子

aclnnAddRmsNormDynamicQuantV2 【免费下载链接】ops-nn 本项目是CANN提供的神经网络类计算算子库&#xff0c;实现网络在NPU上加速计算。 项目地址: https://gitcode.com/cann/ops-nn &#x1f4c4; 查看源码 产品支持情况 产品是否支持Ascend 950PR/Ascend 950DT√A…...

工程师必读:六大情感触发器,破解技术产品市场转化难题

1. 项目概述&#xff1a;当工程师遇上商业&#xff0c;一场关于“情感”的必修课最近有个工程师朋友跟我抱怨&#xff0c;说他团队花了两年心血打磨的产品&#xff0c;技术指标全面领先&#xff0c;结果推向市场后反响平平&#xff0c;远不如隔壁一个技术平平但“会讲故事”的竞…...

【AI原生版本控制终极指南】:2026奇点大会Git for AI官方认证实践白皮书首次解禁

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;AI原生版本控制&#xff1a;2026奇点智能技术大会Git for AI最佳实践 在2026奇点智能技术大会上&#xff0c;Git for AI正式成为AI工程化基础设施的核心组件。它不再仅追踪文本变更&#xff0c;而是原生…...

从零到一:基于iSYSTEM winIDEA与IC5000的嵌入式程序烧写与调试实战指南

1. 环境准备&#xff1a;搭建你的嵌入式开发工作台 第一次接触iSYSTEM工具链时&#xff0c;我完全被各种专业术语搞懵了。后来才发现&#xff0c;只要把环境搭好&#xff0c;后面的操作就像拼乐高一样简单。这里我会手把手带你配置好winIDEA和IC5000调试器&#xff0c;避开那些…...

第四部分-Docker网络与存储——18. 自定义网络

18. 自定义网络 1. 自定义网络概述 自定义网络允许用户根据需求创建具有特定配置的网络&#xff0c;相比默认的 bridge 网络&#xff0c;提供了更好的隔离性、DNS 解析和灵活性。 ┌────────────────────────────────────────────…...

为Claude Code配置Taotoken解决密钥被封与Token不足痛点

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 为Claude Code配置Taotoken解决密钥被封与Token不足痛点 对于经常使用Claude Code进行开发的工程师来说&#xff0c;直接使用官方服…...