Redis作为缓存的数据一致性问题

背景

使用Reids作为缓存的原因:
在高并发场景下,传统关系型数据库的并发能力相对比较薄弱(QPS不能太大);
使用Redis做一个缓存。让用户请求先打到Redis上而不是直接打到数据库上。
但是如果出现数据更新操作:数据库与缓存更新,就会出现缓存(Redis)和数据库(MySQL)之间的数据一致性问题。

非读写分离架构:延时双删

先更新数据库,再更新缓存,为什么不可行?

线程安全角度

同时有请求A、B进行更新操作

执行顺序如下:
线程A更新了数据库
线程B更新了数据库
线程B更新了缓存
线程A更新了缓存

缓存和数据库出现了不一致

业务角度

如果某个业务场景是写多读少,就会导致缓存并未被读取就会被频繁的更新,极大的浪费了服务器的性能。
因为数据库的值,并不是直接刷入缓存,有的业务需要经过一系列复杂的计算再写入缓存。

先删除缓存,再更新数据库,为什么不可行?

线程安全角度

请求A进行写操作,先删除缓存
请求B查询发现缓存不存在
请求B去数据库查询得到旧值
请求B将旧值写入缓存
请求A更新数据库

此时数据库中的值是新值,缓存的值是旧值,就发生了数据不一致问题

延时双删

线程 A:
当应用程序需要更新数据时,首先将数据更新到数据库
A 线程向 Redis 发送删除缓存的指令,将缓存标记为过期
A 线程等待一定的时间窗口(通常是几十ms~几百ms),让 B 线程有足够的时间去访问缓存

线程 B:
在时间窗口内,当有请求访问过期的缓存数据时,B 线程发现缓存已过期,并触发缓存更新的操作
B 线程从数据库中获取到最新数据,并将其存储到缓存中
B 线程返回更新后的缓存数据

线程 A(续):
在时间窗口结束后,A 线程再次向 Redis 发送删除缓存的指令,彻底删除缓存数据
如果在时间窗口内没有请求访问到过期的缓存数据,A 线程会删除已标记为过期的缓存数据

通过上述流程
A 线程负责标记缓存过期并等待一段时间,给 B 线程足够的时间去访问缓存并更新
B 线程则负责处理实际的缓存更新操作
这样即使在缓存更新期间有请求访问过期的缓存数据,也能获取到最新的数据,避免脏读

注意
具体的时间窗口大小和线程的实现方式可以根据实际需求和系统性能进行调整。
同时,对于高并发环境,还需要考虑线程安全和并发控制的实现,以确保操作的正确性和性能。

读写分离架构(有专门的读服务、专门的写服务,写主,读从主)

可以采用先更新数据库,再删除缓存,配合上重试机制

问题

两个请求:请求A进行更新操作,请求B进行查询操作
请求A进行写操作,删除缓存
请求A将数据写入数据库,
请求B查询缓存发现,缓存没有值
请求B去从库查询,这时,还没有完成主从同步,因此查询到的是旧值
请求B将旧值写入缓存
数据库完成主从同步,从库变为新值
仍然会出现缓存与数据库数据不一致问题

延时双删的问题

延时时间需要在主从同步的延时时间基础上,加几百ms

双删失败
如果第二次删除缓存失败,仍然会出现缓存与数据库数据不一致的问题

同样还是有两个请求请求A进行更新操作,请求B进行查询操作(单库)
请求A进行写操作,删除缓存
请求B查询发现缓存不存在
请求B去数据库查询得到旧值
请求B将旧值写入缓存
请求A将新值写入数据库
请求A试图去删除请求B写入的缓存值,结果失败了

删除失败的重试补偿机制

先更新数据库,再删除缓存
同样存在并发问题,但是发生几率很低

两个请求:请求A进行更新操作,请求B进行查询操作(单库)
缓存刚好失效
请求A查询数据库,得一个旧值
请求B将新值写入数据库
请求B删除缓存
请求A将查到的旧值写入缓存

该情况发生的必要条件就是请求B写数据库的操作比请求A读数据库的操作耗时更短,才能使请求B先删除缓存
但是通常来说数据库的读操作是远远快于写操作的,所以这种并发问题很难发生。

如果在极端情况下,这种并发问题仍然发生了
给缓存设置一定的有效时间

异步延时双删策略

另起一个线程,异步删除,保证读请求完成以后,再进行删除操作

重试机制
与先删除缓存,再更新数据一样,如果删除缓存失败,那么仍然会出现数据不一致问题

选择靠谱的重试机制,比如利用消息队列进行删除的补偿

方案一:

更新数据库数据;
缓存因为种种问题删除失败
将需要删除的key发送至消息队列
自己消费消息,获得需要删除的key
继续重试删除操作,直到成功

在这里插入图片描述
缺点
对业务线代码造成大量的侵入,需要在业务代码中额外添加生成消息和消费消息的功能
业务代码变得不再专注于业务需求。

改进:
启动一个订阅程序去订阅数据库的binlog,获得需要操作的数据
在应用程序中,另起一段程序(避免业务侵入),获得这个订阅程序传来的信息,进行删除缓存操作

方案二

更新数据库数据
数据库会将操作信息写入binlog日志当中
订阅程序提取出所需要的数据以及key
另起一段非业务代码,获得该信息
尝试删除缓存操作,发现删除失败
将这些信息发送至消息队列

在这里插入图片描述
重新从消息队列中获得该数据,重试操作订阅binlog程序在mysql中有现成的中间件叫canal
可以完成订阅binlog日志的功能

附录

MySQL的查询QPS主要取决于硬件性能以及应用的查询优化,通常在千到万的范围
但在特定的配置下可以达到几万到十几万。

通常来说,对于大多数应用场景而言,MySQL的QPS在2000-3000就已经比较高了。

过高的QPS可能会对服务器性能产生负面影响,如CPU和I/O压力过大。
因此,最佳的QPS应根据实际的硬件配置和应用需求来定。

通过对查询的优化,如合理的索引设计、合理的查询设计,也能够有效提高QPS
通过使用缓存、读写分离、分库分表等方式,也能显著提高系统的并发处理能力,从而提高QPS

词汇

写入失败重试直到成功,称之为:删除补偿

学习文档

https://mp.weixin.qq.com/s?__biz=Mzg5MjE0MjE3Mw==&mid=2247488095&idx=1&sn=c4f50e3dbfd381c3f9c6948b973f8063&chksm=cfc3c56df8b44c7be1a7284a0f6b9255274fb50e3cefcf98e7e6897b1df0f8080223012a312b#rd

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

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

相关文章

【C/C++ 学习笔记】运算符

【C/C 学习笔记】运算符 视频地址: Bilibili 算术运算符 运算符含义备注 加号 − - −减号 ∗ * ∗乘号 / / /除号整数相除结果依然是整数(直接舍去小数部分),小数相除还是小数 % 取模小数无法进行取模运算;对 0 取模会报错 …

Windows下同一电脑配置多个Git公钥访问不同的账号

前言 产生这个问题的原因是我在Gitee码云上有两个账号,为了方便每次不用使用http模式推拉代码,于是我就使用了ssh的模式,起初呢我用两台电脑分别连接两个账号,用起来也相安无事,近段时时间台式机在家里,我在外地出差了,就想着把ssh公钥同时添加到不同的账号里,结果却发现不能用…

超网、IP 聚合、IP 汇总分别是什么?三者有啥区别和联系?

一、超网 超网(Supernet)是一种网络地址聚合技术,它可以将多个连续的网络地址合并成一个更大的网络地址,从而减少路由表的数量和大小。超网技术可以将多个相邻的网络地址归并成一个更大的网络地址,这个更大的网络地址…

【Vue3 组合式 API: reactive 和 ref 函数】

文章目录 1. 什么是组合式 API?2. reactive 函数3. ref 函数4. reactive vs ref 1. 什么是组合式 API? 组合式 API 是 Vue 3 中的一种新特性,它允许我们通过函数来组织组件的逻辑,而不是依赖于选项式 API 中的选项对象。这使得代…

Git使用教程:入门到精通

Git使用教程:入门到精通 一、Git安装根据需求选择电脑位数安装;20231023210945建议这里先新建一个文件夹如:D:/Git;专门来存放Git安装包和后续Git代码,方便管理; 二、Git使用前的配置需要先创建自己的Gitee…

贪心算法(蓝桥杯 C++ 题目 代表 注解)

介绍: 贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最好或最优(即最有利)的选择,从而希望最终能够得到全局最好或最优的结果的算法。它通常用来解决一些最优化问题,如最小生…

❤ Vue3项目搭建系统篇(二)

❤ Vue3项目搭建系统篇(二) 1、安装和配置 Element Plus(完整导入) yarn add element-plus --savemain.ts中引入: // 引入组件 import ElementPlus from element-plus import element-plus/dist/index.css const ap…

剑指offer经典题目整理(二)

一、斐波那契数列(fib) 1.链接 斐波那契数列_牛客题霸_牛客网 (nowcoder.com) 2.描述 斐波那契数列就是数列中任意一项数字,都会等于前两项之和,满足f(n) f(n-1) f(n-2) 的一个数列,例如:1 1 2 3 5 8…

CPU卡学习

外部认证主要流程: 1. 选卡 2. 发送RATS指令(0xe0 0x51 0x35 0xb4) 3. 成功后返回ATS :0x10 0x78 0x80 0xa0 0x2 0x20 0x90 0x0 0x0 0x0 0x0 0x0 0x38 0xc1 0x5f 0x3e 4. 发送选择MF指令(0xa 0x1 0x0 0xa4 0x0 0x0 0x2 0x3f 0x0 0xbf 0xeb) 5. 成功…

VMware虚拟机安装Ubuntu kylin22.04系统教程(附截图详细步骤)

一、版本信息 虚拟机产品:VMware Workstation 17 Pro 虚拟机版本:17.0.0 build-20800274 ISO映像文件:ubuntukylin-22.04-pro-amd64.iso 二、安装步骤 打开虚拟机,点击创建新的虚拟机: 选择自定义: 硬…

HarmonyOS NEXT应用开发之MpChart图表实现案例

介绍 MpChart是一个包含各种类型图表的图表库,主要用于业务数据汇总,例如销售数据走势图,股价走势图等场景中使用,方便开发者快速实现图表UI。本示例主要介绍如何使用三方库MpChart实现柱状图UI效果。如堆叠数据类型显示&#xf…

嘉绩咨询:搭建品牌招商桥梁,提供卓越讲师与会议服务

当下,品牌成功的关键在于强大的渠道支撑和高效的招商能力,在这一背景下,嘉绩咨询,这一专注于渠道招商全案系统孵化的知名平台型企业,今日宣布,将进一步加强其在品牌招商桥梁搭建上的服务功能,通…

HTML 学习笔记——标签创建小技巧

<!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Emmrt工具</title></head><body>&…

未来 AI:引领研发技术的关键

2024 年 AI 辅助研发趋势 2024年&#xff0c;随着人工智能技术的不断发展&#xff0c;AI辅助研发正成为科学研究和创新的重要驱动力。在这一年&#xff0c;我们预见到以下几个重要的AI辅助研发趋势。 首先&#xff0c;自动化实验将成为研发领域的主流。AI将在实验室中扮演越来…

XS2185:八通道PSE控制器产品

八通道PSE控制器产品-XS2185 芯片特性 八通道PSE 支持标准PD供电 支持非标PD供电 每个端口功率最大30W 12位端口电流监测 12位电源电压监测 支持直流负载断开检测 支持LED供电状态指示 支持过流保护 支持短路保护 Sifos基本测试通过 32-PIN…

L1-030 一帮一

“一帮一学习小组”是中小学中常见的学习组织方式&#xff0c;老师把学习成绩靠前的学生跟学习成绩靠后的学生排在一组。本题就请你编写程序帮助老师自动完成这个分配工作&#xff0c;即在得到全班学生的排名后&#xff0c;在当前尚未分组的学生中&#xff0c;将名次最靠前的学…

每日一问之Java中的类加载机制

Java中的类加载过程分为 加载、链接、初始化 三个步骤 加载&#xff1a;将类的字节码文件加载到内存中 链接可以细化分成 验证、准备、解析 验证&#xff1a;检查类的字节码是否符合Java的语法规范 准备&#xff1a;为类的静态变量分配内存并设置初始值 解析&#xff1a;将类…

自动备份数据到异地服务器(另一台电脑)

我们经常遇到需要将数据备份到另一台服务器&#xff0c;但是没有免费的软件&#xff0c;所以很苦恼&#xff0c;去网上找&#xff0c;找来找去都要收费&#xff0c;关键还是月租或年租。。。 其实没必要去买别人的&#xff1b; 下面我给大家写了一个批处理&#xff0c;即可以搞…

【Linux】CentOS网络故障排查大揭秘: 实战攻略解读

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Linux ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 检查网络连接状态&#xff1a; 检查网络配置&#xff1a; 重启网络服务&#xff1a; 检查防火墙设置&#xff1a; 查看日志文…

VBA技术资料MF128:批量创建文件夹及子文件夹

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…