MySQL和Redis如何保证数据一致性

news2024/11/23 15:34:28

MySQL与Redis都是常用的数据存储和缓存系统。为了提高应用程序的性能和可伸缩性,很多应用程序将MySQL和Redis一起使用,其中MySQL作为主要的持久存储,而Redis作为主要的缓存。在这种情况下,应用程序需要确保MySQL和Redis中的数据是同步的,以确保数据的一致性。

什么是一致性

“数据一致”一般指的是:缓存中有数据,缓存的数据值=数据库中的值。但根据缓存中是有数据为依据,则“一致”可以包含两种情况:

1)缓存中有数据,缓存的数据值=数据库中的值。

2)缓存中本没有数据,数据库中的值=最新值(有请求查询数据库时,会将数据写入缓存,则变为上面的“一致”状态)。

“数据不一致”:缓存的数据值≠数据库中的值;缓存或者数据库中存在旧值,导致其他线程读到旧数据。

在这里插入图片描述
一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。

  • 强一致性:这种一致性级别是最符合用户直觉的,它要求系统写入什么,读出来的也会是什么,用户体验好,但实现起来往往对系统的性能影响大
  • 弱一致性:这种一致性级别约束了系统在写入成功后,不承诺立即可以读到写入的值,也不承诺多久之后数据能够达到一致,但会尽可能地保证到某个时间级别(比如秒级别)后,数据能够达到一致状态
  • 最终一致性:最终一致性是弱一致性的一个特例,系统会保证在一定时间内,能够达到一个数据一致的状态。这里之所以将最终一致性单独提出来,是因为它是弱一致性中非常推崇的一种一致性模型,也是业界在大型分布式系统的数据一致性上比较推崇的模型

导致数据不一致的原因?

1) 在高并发的业务场景下,数据库大多数情况都是用户并发访问最薄弱的环节。所以,就需要使用redis做一个缓冲操作,让请求先访问到redis,而不是直接访问MySQL等数据库;

2)读取缓存步骤一般没有什么问题,但是一旦涉及到数据更新,数据库和缓存更新,就容易出现缓存(Redis)和数据库(MySQL)间的数据一致性问题;

3)这个业务场景,主要是解决读数据从Redis缓存,一般都是按照下图的流程来进行业务操作。

在这里插入图片描述

应对策略

针对缓存更新问题,提出了一个旁路缓存的缓存更新套路,这个策略分为以下三种场景:

1)失效:应用程序先从缓存取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。

2)命中:应用程序从缓存中取数据,取到后返回。

3)更新:先把数据存到数据库中,成功后,再让缓存失效。

不管是先删缓存再更新数据库还是先更新数据库再删缓存,都会导致缓存跟数据不一致问题!

先写MySQL,再写Redis

在这里插入图片描述
先写Redis,再写MySQL
在这里插入图片描述
先删除Redis,再写MySQL
在这里插入图片描述
先写 MySQL,再删除 Redis
在这里插入图片描述
不管是先写数据库,再删除缓存;还是先删除缓存,再写库,都有可能出现数据不一致的情况。

现有的大部分业务场景下大多采用读写分离的操作来提升数据库吞吐量,但是并发读写访问的时候,对缓存和数据库相互交叉执行操作,则会出现数据不一致问题。

在进行数据更新时,就涉及到先更新缓存还是先更新数据库了,其实两种方式都有数据一致性问题:

举个例子:假如业务A为写请求,业务B为读请求

1.先更新数据库再更新缓存

步骤1:业务A先更新数据库,此时该业务线由于宕机或者其他原因延迟没有继续进行。

步骤2:业务B读取数据,读取的是缓存中的旧数据。

步骤3:业务A恢复过来,更新缓存

可以看到,由于写请求延迟,可能会读到旧的缓存数据。

2.先更新缓存再更新数据库

步骤1:业务A先删除缓存

步骤2:业务B进入,业务B发现缓存中没有数据,直接从数据库中进行读取,读到了数据库中的旧数据

步骤3:业务A更新数据库并返回。

可以看到,由于写请求延迟,可能读到旧的数据库数据。

因为写和读是并发的,没法保证顺序,就会出现缓存和数据库的数据不一致的问题。

解决方案

(1)读写请求串行化

最为简单的一种方法,写请求在更新之前需要先获得分布式锁,获取到锁才能去更新数据库,获取不到则进行等待,超时直接返回更新失败。更新完数据库后更新缓存,如果更新失败,放到内存队列中进行重新尝试。读请求则同样需要获得锁,然判断缓存中是否有数据,有则直接读缓存,没有则直接读数据库,并更新缓存。

这种方案可以保证数据的一致性。但是会降低系统吞吐量(等待时间长),这在需要数据强一致的情况下适用。(银行转账)

(2)删除缓存

  • 1.先删除缓存,后更新数据库
  • 2.先更新数据库,后删除缓存

先删除缓存,后更新数据库,第二步操作失败,数据库没有更新成功,那下次读缓存发现不存在则从数据库中读取,并重建缓存,此时数据库和缓存依日保持一致。

但如果是先更新数据库,后删除缓存,第二步操作失败,数据库是最新值,缓存中是旧值,发生不致。所以,这个方案依旧存在问题。

总之,和前面提到的问题类似,第二步失败依旧有不一致的风险

我们再来看[并发]问题,这个问题是我们需要关注的[重点]

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

依旧是 2 个线程并发[读写]数据

1.缓存中 X 不存在 (数据库 X = 1)
2.线程 A 读取数据库,得到目值 (X = 1)
3.线程 B 更新数据库 (X = 2)
4.线程 B 删除缓存
5.线程 A 将日值写入缓存 (X = 1)

最终 X的值在缓存中是 1 (日值) ,在数据库中是 2(新值),也发生不一致

这种情况[理论]来说是可能发生的,但实际真的有可能发生吗?

其实概率[很低],这是因为它必须满足 3 个条件

1.缓存刚好已失效
2.读请求 + 写请求并发
3.更新数据库 + 除缓存的时间 (步 3-4) ,要比读数据库 + 写缓存时间短(步 2 和5)

仔细想一下,条件 3 发生的概率其实是非常低的因为写数据库一般会先[加锁],所以写数据库,通常是要比读数据库的时间更长的这么来看,[先更新数据库 + 再删除缓存]的方案,是可以保证数据一致性的。

所以,我们应该采用这种方案,来操作数据库和缓存

如何保证两步都执行成功?

无论是更新缓存还是删除缓存,只要第二步发生失败,那么就会导致数据库和缓存不一致。
保证第二步成功执行,就是解决问题的关键.

程序在执行过程中发生异常,最简单的解决办法是什么?

答案是:异步重试

  • 如果是同步重试,立即重试很大概率还会失败,[重试次数]设置多少才合理?

  • 重试会一直[占用]这个线程资源,无法服务其它客户端请求

  • 异步其实就是把重试请求写到消息队列中,然后由专门的消费者来重试,直到成功。

为了避免第二步执行失败,我们可以把操作缓存这一步,直接放到消息队列中,由消费者来操作缓存

到这里你可能会问,写消息队列也有可能会失败啊? 而且,引入消息队列,这又增加了更多的维扩成本,这样做值得吗?

这个问题很好,但我们思考这样一个问题:如果在执行失败的线程中一直重试,还没等执行成功,此时如果项目[重启]了,那这次重试请求也就[丢失]了,那这条数据就一直不一致了

所以,这里我们必须把重试消息或第二步操作放到另一个[服务]中,这个服务用[消息队列]最为合适。

  • 消息队列保证可靠性: 写到队列中的消息,成功消费之前不会丢失(重启项目也不担心)
  • 消息队列保证消息成功投递: 下游从队列拉取消息,成功消费后才会删除消息,否则还会继续投递消息给消费者 (符合我们重试的需求)

至于写队列失败和消息队列的维护成本问题

  • 写队列失败: 操作缓存和写消息队列,[同时失败]的概率其实是很小的维护成本:

  • 我们项目中一般都会用到消息队列,维护成本并没有新增很多

参考资料:https://www.zhihu.com/question/319817091
https://www.jb51.net/database/285448xty.htm
https://baijiahao.baidu.com/s?id=1706150811910444110&wfr=spider&for=pc

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

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

相关文章

Bootstrap-fileinput 插件的使用

1.bootstrap-fileinput 下载地址 https://github.com/kartik-v/bootstrap-fileinput.git 2.bootstrap-fileinput 使用 input 标签 multiple"multiple" 表示可以多选文件 <div class"container-fluid"><div class"card border-0 shadow-sm…

深度学习在MRI运动校正中的应用综述

运动是MRI中的主要挑战之一。由于MR信号是在频率空间中获取的&#xff0c;因此除了其他MR成像伪影之外&#xff0c;成像对象的任何运动都会导致重建图像中产生伪影。深度学习被提出用于重建过程的几个阶段的运动校正。广泛的MR采集序列、感兴趣的解剖结构和病理学以及运动模式&…

这四种订货系统不能选(四):不能源码交付

订货系统在现代企业管理中具备着重要的地位和作用。通过订货系统&#xff0c;企业能够更好地掌握市场需求&#xff0c;提高订单的准确性和及时性&#xff0c;优化企业的供应链管理&#xff0c;并加强与供应商之间的合作与沟通。今天我们分享最后一个不能选的、也是最重要的一点…

ArcGIS Pro发布地图服务(影像、矢量)

本文示例使用&#xff08;因为portal的授权的版本只有10.5的&#xff0c;故使用10.5进行示例&#xff09;&#xff1a; 软件:ArcGIS Pro3.0.1&#xff08;破解版&#xff09;&#xff0c; ArcGIS Portal10.5 当ArcGIS Pro和Portal不在一个机器或者版本不一样的时候&#xff0…

日常问题——git推送代码被拒绝

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;日常BUG、BUG、问题分析☀️每日 一言 &#xff1a;存在错误说明你在进步&#xff01; 一、问题描述 Push to origin/master was rejected 提交代码时提示&#xff0c;被拒绝。 二、问题原因 …

(七)Unity VR项目升级至Vision Pro需要做的工作

Vision Pro 概述 定位为混合现实眼镜&#xff0c;对AR支持更友好 无手柄&#xff0c;支持手&#xff08;手势&#xff09;、眼&#xff08;注视&#xff09;、语音交互 支持空间音频&#xff0c;相比立体声、环绕声更有沉浸感和空间感 支持VR/AR应用&#xff0c;支持多种应用模…

零基础官网下载jdk

Oracle 官网总是隔一段时间一改版&#xff0c;时间长了博客可能不适用&#xff0c;望注意&#xff0c;但是精髓不变。 Oracle官网 官网地址百度搜索&#xff0c;其他任何官网都一个套路&#xff0c;但要识别下一些广告网站会模仿官方网站。 官网地址&#xff1a;https://www.…

k8s服务注册发现

Service 是 将运行在一个或一组pod上的网络应用程序公开为网络服务的方法。 定义service前端为service名称、ip、端口等不变的部分&#xff0c;后端为符合标签选择的pod集合 注册 通过api server提交注册service请求到DNSservice随后得到clusterIP&#xff08;虚拟ip地址&am…

UHPC的疲劳计算——兼论ModelCode2010的适用性

文章目录 0. 背景1、结论及概述2、MC10对于SN曲线的调整&#xff08;囊括NC、HPC、UHPC&#xff09;2.1 疲劳失效曲面的构建2.2 新模型的验证 3、MC10对于疲劳设计强度的调整及其背后的原因4. 结语 0. 背景 今年年初&#xff0c;有一位用UHPC做混凝土塔筒的同行告诉我&#xf…

多平台1688、淘宝、京东搜索商品聚合接口,示例返回值说明

多平台根据关键词取商品列表 API 返回值说明 item_search-根据关键词取商品列表 公共参数 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;Taobao。拼多多。京东&#xff0c;1688API 接口测试secretString是调用密钥api_nameString是A…

Deep Learning With Pytorch - 最基本的感知机、贯序模型/分类、拟合

文章目录 如何利用pytorch创建一个简单的网络模型&#xff1f;Step1. 感知机&#xff0c;多层感知机&#xff08;MLP&#xff09;的基本结构Step2. 超平面 ω T ⋅ x b 0 \omega^{T}xb0 ωT⋅xb0 or ω T ⋅ x b \omega^{T}xb ωT⋅xb感知机函数 Step3. 利用感知机进行决策…

我们为什么需要API管理系统?

我们为什么需要API管理系统&#xff1f; 随着web技术的发展&#xff0c;前后端分离成为越来越多互联网公司构建应用的方式。前后端分离的优势是一套Api可被多个客户端复用&#xff0c;分工和协作被细化&#xff0c;大大提高了编码效率&#xff0c;但同时也带来一些“副作用”:…

stm32g070的PD0/PD2 PA8和PB15

目前在用STM32G070做项目&#xff0c;其中PD2TIMER3去模拟PWM&#xff0c;PD0用作按键检测&#xff0c;测试发现PD0低电平检测没有问题&#xff0c;高电平检测不到&#xff0c;电路图如下图所示&#xff1a; 用万用表测试电平&#xff0c;高电平1.0V左右&#xff0c;首先怀疑硬…

高级SQL分析函数-窗口函数

摘要&#xff1a;本文由葡萄城技术团队于CSDN原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 前言 SQL语句中&#xff0c;聚合函数在统计业务数据结果时起到了重要作用&#xff0c;…

nginx keepalived 本地二进制部署

文章目录 安装 nginx安装 keepalived卸载 nginx卸载 keepalived 安装 nginx wget http://nginx.org/download/nginx-1.24.0.tar.gz tar -xf nginx-1.24.0.tar.gz cd nginx-1.24.0/ ./configure --with-stream --prefix/usr/local/nginx make && make install修改nginx…

关于ChatGPT抽样调查:78%的人用于搜索,30%的人担心因它失业

人工智能早已不再被视为未来科技&#xff0c;而是越来越多地应用在时下人们的生活之中。根据DECO PROTESTE的调查&#xff0c;大约72%的葡萄牙人认为人工智能已经活跃于他们的日常。[1] 随着ChatGPT对各个行业的影响&#xff0c;也引发了人们关于这种人工智能模型潜力的争论&a…

【LeetCode】543.二叉树的直径

题目 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5]…

pwm接喇叭搞整点报时[keyestudio的8002模块]

虽然现在查看时间很方便&#xff0c;但是其实好像我的时间观念却越来越差。于是决定搞一个整点报时&#xff0c;时常提醒自己时光飞逝&#xff0c;不要老是瞎墨迹。 这篇主要讲一下拼装方式和配置&#xff0c;就差不多了。不涉及什么代码。3针的元器件&#xff0c;去掉正负接线…

地毯(暴力+差分两种方法)

题目描述 在 nx n 的格子上有 m 个地毯。 给出这些地毯的信息&#xff0c;问每个点被多少个地毯覆盖。 输入格式 第一行&#xff0c;两个正整数 n,m。意义如题所述。 接下来 m 行&#xff0c;每行两个坐标 (x_1,y_1) 和 (x_2,y_2)&#xff0c;代表一块地毯&#xff0c;左上…

SQL-每日一题【1341. 电影评分】

题目 表&#xff1a;Movies 表&#xff1a;Users 请你编写一个解决方案&#xff1a; 查找评论电影数量最多的用户名。如果出现平局&#xff0c;返回字典序较小的用户名。查找在 February 2020 平均评分最高 的电影名称。如果出现平局&#xff0c;返回字典序较小的电影名称。 …