Redis与DataBase保持数据一致性

news2024/9/21 1:37:21

文章目录

    • 1. 读取数据
    • 2. 写数据
      • 2.1 先操作缓存
      • 2.2 先操作数据库

在我们系统中缓存最常用的策略是:服务端需要同时维系DB和Cache,并且是以DB的结果为准, Cache-Aside Pattern(缓存分离模式、旁路缓存)。

1. 读取数据

image-20240808112851354

当用户查询数据时,首先先到Redis中进行查询,如果没有命中,则到数据库中进行查询。在数据库中查询到数据后,将数据返回,并且存入到Redis中,设置过期时间,设置过期时间是为了避免长时间未使用的数据一直存放在Redis中,占用内存空间。

2. 写数据

用户在写入数据时,要操作Redis和DataBase。有以下几种操作:

先操作Redis缓存

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

先操作DataBase数据库

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

对于是更新Redis中的数据,还是删除Redis中的数据,其实更推荐是删除Redis中的数据。

因为,如果是选择修改Redis数据,而不是删除,那么如果再修改Redis数据成功后,再去更新数据库中的数据时失败,这样就会导致Redis和DataBase中的数据不一致。再或者如果先成功更新了数据库中的数据,再去更新Redis中的数据时失败了,那么下次查询到Redis中的数据虽然存在,但是为错误数据。

在更新缓存与删除缓存之中,我们确定了是使用删除Redis缓存中的数据。那么剩下的一个问题就是,先更新数据库,还是先删除Redis缓存?

对于这两种情况,我们分别进行讨论。

2.1 先操作缓存

image-20240808140156405

先操作缓存,也就是先将Redis中的数据删除,然后再更新DataBase中的数据。

数据不一致

考虑这种情况下会出现的问题:

  1. Redis中的数据成功被删除了。
  2. 在数据被写入数据库之前,此时有线程2存在,线程2读取DataBase中的数据,读取成功后存入了Redis中。
  3. 线程2从DataBase中读取数据后,线程1的新数据写入DataBase。
  4. 此时再去读取数据时,因为Redis中数据存在,所以会直接读取Redis中的数据,但是Redis中的数据是旧数据,此时就出现了数据不一致问题。

image-20240808141454554

出现上述这种情况时,该如何解决呢?

双删)有一种解决方法:

  1. Redis中的数据成功被删除了
  2. 在数据被写入数据库之前,此时有线程2存在,线程2读取DataBase中的数据,读取成功后存入了Redis中。
  3. 线程2从DataBase中读取数据后,线程1的新数据写入DataBase。
  4. 线程1的新数据写入DataBase后,再次执行将Redis中的数据删除。
  5. 此时再去读取数据时,因为Redis中数据不存在,所以会到DataBase中查询数据,再将查询到的数据存入Redis中,这样保证了数据的一致性。

image-20240808142513505

但是,这种处理方法,还是存在一种缺陷。如以下情况:

  1. 线程1更新数据,Redis中的数据成功被删除了。
  2. 线程2读取数据,先查缓存,缓存中数据不存在,查询数据库,查询到旧数据。
  3. 线程1删除Redis数据成功后,将新数据写入数据库。
  4. 线程1将新数据写入数据库后,删除Redis中的缓存数据。
  5. 线程2将查询到的旧数据返回,并将数据写入到Redis中。
  6. 此时,Redis中的数据为旧数据,又导致了Redis与数据库中数据不一致的问题。

image-20240808142952938

为了解决上述问题,我们可以将第5步操作进行延迟。在上诉例子中,也就是让线程2先将旧数据存入到Redis中,线程1再去删除Redis中的数据。这种处理方法称为延迟双删

对于先操作缓存,再操作数据库的情况中,处理数据不一致性问题,我们一般采用延迟双删的方法进行处理。

2.2 先操作数据库

image-20240808144242932

先操作数据库,也就是先更新DataBase中的数据,然后再将Redis中的数据删除。

此时,线程2存在,如果数据再Redis中存在,那么不会出现数据不一致问题。

线程2查询数据,首先查询缓存,数据存在直接返回,只是线程2可能查到的为旧数据,但是这并不影响Redis与DataBase的数据一致性。

image-20240808144818490

除了这种情况外,还有一种情况存在。Redis中不存在被更新的该数据。那么就会出现以下情况:

  1. 线程2查询数据,先查询Redis中的数据,数据不存在,到数据库中查询数据,查询到旧数据。
  2. 线程1更新数据库中的数据。
  3. 线程1更新完数据库中的数据后,执行对Redis中数据的删除操作。
  4. 线程2将查询到的旧数据存储到Redis中。此时就出现了数据不一致行问题。

image-20240808145124373

对于这种情况,我们对于图中第4步操作还是采用延迟删除进行处理。

但是这种情况下,也会存在一种问题,就是第3步操作,删除缓存失败。针对这种删除缓存失败的情况,我们可以采用删除重试机制进行解决。我们可以引入MQ消息队列。

image-20240808150609362

但是,加入了删除重试后,非业务代码和业务代码耦合度太高了。

我们想解耦的话,就可以采用另一个组件,叫做canal

canal是阿里的一款开源框架,主要用途是基于MySQL数据库增量日志解析,提供增量数据订阅和消费。

Canal提供了各种语言的客户端,当Canal监听到binlog变化时,会通知Canal的客户端。

image-20240808151104257

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

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

相关文章

高质量翻译对中国移动应用在国外市场推广的影响

在移动应用的竞争格局中,打入国外市场对中国开发商来说既是机遇也是挑战。决定中国移动应用程序在海外成功的最关键因素之一是其翻译质量。高质量的翻译在有效推广该应用程序、确保其与当地用户产生共鸣,并最终推动新市场的采用和增长方面发挥着关键作用…

嵌入式全栈开发学习笔记---数据结构(广度优先算法)

目录 过程分析 代码实现 queue_maze.c 上节我们讲完了队列,本节开始学习广度优先算法! 之前我们用深度优先算法找出从迷宫出来的所有路径,本次我们要用广度优先算法找出最短路径。 过程分析 广度优先算法也叫广度优先搜索。 这种算法就相当…

Qwen2-Math 开源 AI 模型发布;阿里云推出首个域名 AI 大模型应用丨 RTE 开发者日报

开发者朋友们大家好: 这里是 「RTE 开发者日报」 ,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE(Real-Time Engagement) 领域内「有话题的 新闻 」、「有态度的 观点 」、「有意思的 数据 」、「有思…

vscode用快捷键一键生成vue模板

项目中有些代码模块是固定的,如下面的代码所示,为了不重复写这些相同的代码,我们可以使用快键键一键生成模板。 流程: 中文:首选项-> 用户代码片段 -> 输入框中输入vue,找到vue.json文件(没有vue.j…

如意AI健康管理开放域自然对话算法分析报告

一、算法全周期行为分析 1. 算法安全 信息内容安全:通过意图识别和实体抽取技术,确保生成的内容不涉及违法或不当信息。 信息源安全:数据采集环节采用安全措施,保护数据不被非法访问。 2. 算法监测 信息安全监测:实施实…

国产芯上运行TinyMaxi轻量级的神经网络推理库-米尔基于芯驰D9国产商显板

本篇测评由优秀测评者“短笛君”提供。 本文将介绍基于米尔电子MYD-YD9360商显板(米尔基于芯驰D9360国产开发板)的TinyMaxi轻量级的神经网络推理库方案测试。 算力测试 TinyMaix 是面向单片机的超轻量级的神经网络推理库,即 TinyML 推理库&a…

重头开始嵌入式第十六天(结构体,共用体)

目录 结构体 结构体定义 结构体初始化 结构体赋值 结构体调用 结构体大小 在 C 语言中,结构体内存对齐规则主要遵循以下原则: 共用体 共用体定义 共同体赋值,初始化 共用体大小 结构体与共用体的区别 结构体 在 C 语言中,…

vuex properties of undefined (reading ‘getters‘)

前言: 最近打算用vue 写个音乐播放器,在搞 vuex 的时候遇到一个很神奇报错;vuex 姿势练了千百次了,刚开始的时候我一直以为是代码问题,反复检查了带了,依旧报错。 Error in mounted hook: "TypeError:…

PHP全方位多功能投票小程序系统源码

🌟【全民参与,决策更精彩】全方位多功能投票小程序大揭秘!🎉 🚀 开篇引入:投票新风尚,尽在指尖 Hey小伙伴们,你是否厌倦了传统的投票方式,觉得它们既繁琐又不够灵活&am…

历代文学-技术生态-总体介绍

1. 历代文学简介 历代文学(https://literature.sinhy.com/#/literature?__c1000,微信小程序可直接搜索“历代文学”)是一个由两个人(一个后端和一个前端)开发的文学网站,是一个收录从古到今、以及古今中外…

C++之运算符重载系列深入学习:从入门到精通!

为什么需要对运算符进行重载 C预定义中的运算符的操作对象只局限于基本的内置数据类型,但是对于我们自定义的类型是没有办法操作的。但是大多时候我们需要对我们定义的类型进行类似的运算,这个时候就需要我们对这么运算符进行重新定义,赋予其…

智能家居中高性能联网通信方案,乐鑫ESP32-S3/C3无线Wi-Fi蓝牙应用

随着科技的飞速发展,智能家居已经不再是科幻小说中的概念,而是走进了千家万户的现实生活。 智能家居是广泛的系统性产品概念,以住宅为载体,运用物联网、网络通信和人工智能等技术,接收信号并判断,提供更加…

美股全线反弹,市场情绪回暖

一、市场概况 昨夜美股三大股指全线收涨,市场情绪明显回暖。道琼斯工业平均指数上涨1.76%,纳斯达克综合指数上涨2.87%,标普500指数则上涨2.3%。美债市场方面,美国十年期国债收益率上涨1.141%,报3.99%,两年…

[ Python ]使用Charles对Python程序发出的Get与Post请求抓包-解决Python程序报错问题

目录 一、前言 二、Charles 三、抓取Python请求 3.1 正常运行 3.2 程序报错 3.2.1 报错信息 3.2.2 解决方法 3.3 取消警告信息 四、总结 一、前言 在Python开发中,网络请求是常见的操作之一。无论是使用内置的urllib库还是第三方库requests,都可…

Java小白入门到实战应用教程-Scanner类及IO流讲解

Java小白入门到实战应用教程-Scanner类及IO流讲解 Scanner类 我们前面写的很多例子都是程序独自执行的,但是我们做编程写代码的目的是要实现能人和代码去交互的。 现在我们就来了解一个知识点,去实现最简单的人和程序的交互。 在java中通过Scanner类…

MySQL第3讲--数据类型和表的修改和删除

文章目录 前言数据类型数值类型整数类型浮点数和定点数 字符串类型字符类型:文本类型:二进制数据类型 日期和时间类型实例分析 表的操作添加字段修改字段删除字段修改表名删除表 DDL总结DDL数据库操作DDL表操作 前言 上一节在MySQL第2讲–关系型数据库以…

kubernetes 管理平台 Pod管理多容器 与嵌入式脚本

资源清单文件 模板与帮助信息 管理资源对象 多容器 Pod 管理多容器 Pod 自定义任务 容器保护策略 宽限期策略 Pod调度策略

CUDA编程从零到壹

如今,当我们谈论深度学习时,为了提高性能,我们通常会将其实现与使用 GPU 联系起来。 GPU(图形处理单元)最初设计用于加速图像、2D 和 3D 图形的渲染。然而,由于它们能够执行许多并行操作,它们的…

linux 源码部署polardb-x 错误汇总

前言 在linux 源码部署polardb-x 遇到不少错误,特在此做个汇总。 问题列表 CN 启动报错 Failed to init new TCP 详细错误如下 Caused by: Failed to init new TCP. XClientPool to my_polarx#267b21d8127.0.0.1:33660 now 0 TCP(0 aging), 0 sessions(0 runni…

【汇总】测开高频面试题

加油 !!! 🔥 谈谈对测试的理解 我认为测试是发现并及时解决问题:包括功能、性能、用户体验❤️等方面的验证 … 通过提前定位并修复缺陷,可以减少未来维护成本、保障软件质量、提升用户满意度❤️ … 我了…