Redis--13--缓存一致性问题

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 缓存一致性问题
    • 1、先更新缓存,再更新DB
    • 方案二:先更新DB,再更新缓存
    • 方案三:先删缓存,再写数据库
      • 推荐1:==延迟双删==
    • 方案四:先写数据库,再删缓存
  • 删除缓存失败怎么办?
    • 方案一:设置过期时间
    • 方案二:同步重试
    • 方案三:消息队列
    • 方案四:订阅mysql的binlog
    • 总结


缓存一致性问题

通常情况下,我们使用缓存的主要目的是为了提升查询的性能。大多数情况下,是这样使用缓存的:

在这里插入图片描述

当数据库有数据更新时,在很长的一段时间内(决定于缓存的过期时间),用户请求从缓存中获取到的都可能是旧值,而非数据库的最新值。那么,该如何更新缓存呢?目前有以下四种解决方案:

  1. 先更新缓存,再更新数据库(差)

  2. 先更新数据库,再更新缓存(差)

  3. 先删除缓存,后更新数据库(一般)

  4. 先更新数据库,后删除缓存(推荐)

讨论四种方案前先统一两个认知,以便更好理解四种方案:

  • 缓存一致性问题没有绝对可靠的方案,我们只能让两者尽量接近,但无论如何也不能百分百达到一致性效果。
  • 缓存和数据库,无论先处理谁,只要后者有延迟/失败,都会导致不一致的情况,这也正是缓存不一致的根本原因所在。所有解决方案和讨论都是围绕这一点来进行的。

1、先更新缓存,再更新DB

在这里插入图片描述

缺点:如果刚写完缓存,突然网络出现了异常,导致写数据库失败了。这样缓存中的数据就变成脏数据,这个问题非常严重,也是最差的一种解决

方案二:先更新DB,再更新缓存

在这里插入图片描述
缺点一:问题又来了,写数据库成功,但写缓存失败了,依然会造成缓存脏数据的问题。但写缓存失败比写数据库失败的概率要小很多了(因为数据库可能有加锁、外键约束、超时等机制限制),所以此方案要比第一种方案好一点。

  • 如果对接口性能要求不高,还可以把写数据库和写缓存放到一个事务中,写缓存失败就回滚数据库。

缺点二:然而高并发场景下,还会有个棘手问题:
在这里插入图片描述

  • 请求a先过来,刚写完了数据库。但由于网络原因,卡顿了一下,还没来得及写缓存。
  • 这时候请求b过来了,先写了数据库。
  • 接下来,请求b顺利写了缓存。
  • 此时,请求a卡顿结束,也写了缓存。

很显然,在这个过程当中,请求b在缓存中的新数据,被请求a的旧数据覆盖了。

也就是说:在高并发场景中,如果多个线程同时执行先写数据库,再写缓存的操作,可能会出现数据库是新值,而缓存中是旧值,两边数据不一致的情况。

缺点三:浪费系统资源

  • 写的缓存的内容,并不是简单的数据,而是要经过非常复杂的计算或者查询筛选得出的结果,这样每写一次缓存都要计算一次,这是非常浪费系统资源的,尤其对那些写多读少的业务场景,更是雪上加霜。

删除缓存类

方案三:先删缓存,再写数据库

方案一:
在这里插入图片描述

嗯,看起来还不错。即使写数据库失败了,下个请求也会重新触发写缓存操作,基本上避免更新缓存的所有弊端,然而也不是十全十美。

缺点:

在这里插入图片描述

  1. 请求d先过来,把缓存删除了。但由于网络原因,卡顿了一下,还没来得及写数据库。
  2. 这时请求c过来了,先查缓存发现没数据,再查数据库,有数据,但是旧值。
  3. 请求c将数据库中的旧值,更新到缓存中。
  4. 此时,请求d卡顿结束,把新值写入数据库。

这种极端情况下依然会导致写入的缓存为旧值

推荐1:延迟双删

在这里插入图片描述
为了避免方案1的避免,写完数据库后,再删除一次。

该方案有个非常关键的地方是:第二次删除缓存,并非立马就删,而是要在一定的时间间隔之后

sleep的时间要对业务读写缓存的时间做出评估,sleep时间大于读写缓存的时间即可。

那么,为什么一定要间隔一段时间之后,才能删除缓存呢?

请求d卡顿结束,把新值写入数据库后,请求c将数据库中的旧值,更新到缓存中。此时,如果请求d删除太快,在请求c将数据库中的旧值更新到缓存之前,就已经把缓存删除了,这次删除就没任何意义。必须要在请求c更新缓存之后,再删除缓存,才能把旧值及时删除了。

还是mysql读写分离的问题
在这里插入图片描述
在这里插入图片描述

方案四:先写数据库,再删缓存

在这里插入图片描述
就怕一种情况:缓存失效。

  1. 缓存自动失效。
  2. 请求f查询缓存,发缓存中没有数据,查询数据库的旧值,但由于网络原因卡顿了,没有来得及更新缓存。
  3. 请求e先写数据库,接着删除了缓存。
  4. 请求f更新旧值到缓存中。

这时,缓存和数据库的数据同样出现不一致的情况了。但这种情况还是比较少的,需要同时满足以下条件:

  1. 缓存刚好自动失效。
  2. 请求f从数据库查出旧值,更新缓存的耗时,比请求e写数据库,并且删除缓存的耗时还长。

缓存失效。只有可能是查询比删除慢的情况,而这种情况相对来说会少很多。同时结合延时双删的处理,可以有效的避免缓存不一致的情况。

删除缓存失败怎么办?

其实先写数据库,再删缓存的方案,跟缓存双删的方案一样,有一个共同的风险点,即:如果缓存删除失败了怎么办?

方案一:设置过期时间

缓存设置一个过期时间,比如5分钟。当然这种方案只适合数据更新不是太频繁的业务。

方案二:同步重试

在接口中判断是否删除成功,如果失败就重试,直到成功或超过最大重试次数为止,返回数据。当然,这种方案的缺点就是可能影响接口性能。

方案三:消息队列

将删除缓存任务写入mq等消息中间件中,在mq的consumer中处理。但问题也很多:

引入消息中间件之后,问题更复杂了,对业务代码有一定侵入性、消息丢失怎么办
消息本身的延迟也会带来短暂的不一致性,不过这个延迟相对来说还是可以接受的

比如基于 RocketMQ 的可靠性消息通信,来实现最终一致性
在这里插入图片描述

方案四:订阅mysql的binlog

当一条数据发生修改时,MySQL 就会产生一条变更日志(binlog),我们可以订阅这个日志,拿到具体操作的数据,然后再根据这条数据,去删除对应的缓存。
在这里插入图片描述
意思就是我们的业务应用在修改数据时,「只需」修改数据库,无需操作缓存。步骤如下:

  1. 在业务接口中写数据库之后,直接返回成功。
  2. mysql服务器会自动把变更的数据写入binlog中。
  3. binlog订阅者(消费者)获取变更的数据,然后删除缓存
    在这里插入图片描述

在这里插入图片描述

总结

缓存删除比更新效果更好

举个例子:如果数据库1小时内更新了1000次,那么缓存也要更新1000次,但是这个缓存可能只在最后一次更新后被读取了1次,那么前999次的更新有必要吗?

反过来,如果是删除的话,就算数据库更新了1000次,那么也只是做了1次缓存删除(删除前判断key是否存在),只有当缓存真正被读取的时候才去数据库加载

删除缓存有两种方式

  1. 先删除缓存,再更新数据库。解决方案是使用延迟双删。
  2. 先更新数据库,再删除缓存。解决方案是消息队列或者监听binlog同步,引入消息队列会带来更多的问题,对业务代码有一定侵入性,并不推荐直接使用。

针对缓存一致性要求不是很高的场景,那么只通过设置超时时间就可以了。

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

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

相关文章

【c】杨辉三角

下面介绍两种方法 1.利用上面性质的第五条&#xff0c;我们可以求各行各列的组合数 2.利用上面性质的第7条&#xff0c;我们可以用数组完成 下面附上代码 1. #include<stdio.h> void fact(int n ,int m )//求组合数 {long long int sum11;long long int sum21;int a…

C#中GDI+图形图像技术(Graphics类、Pen类、Brush类)

目录 一、创建Graphics对象 1.创建Pen对象 2.创建Brush对象 &#xff08;1&#xff09;SolidBrush类 &#xff08;2&#xff09;HatchBrush类 ​​​​​​​&#xff08;3&#xff09;LinerGradientBrush类 用户界面上的窗体和控件非常有用&#xff0c;且引人注目&#…

家政小程序源码,师傅竞价接单

家政预约上门服务小程序开发方案&#xff0c;php开发语言&#xff0c;前端是uniapp&#xff0c;有成品源码&#xff0c;可以二开&#xff0c;可以定制。 一家政小程序用户端功能&#xff1a;服务分类、在线预约、在线下单。 师傅端&#xff1a;在线接单&#xff0c;竞价&…

zabbix分布式监控平台从IPV4切换到IPV6之监控主机切换

现在有一套监控了海量服务器的zabbix分布式监控平台需整体在线从IPV4切换到IPV6&#xff0c;不能影响其原有的定制监控及视图。本文讲解了切换的第一步--监控主机切换。 一、zabbix分布式监控平台平台架构 本套zabbix分布式监控平台是一个多代理服务器分布式部署的典型传统架构…

rocketMQ介绍

作用 流量削峰系统解耦 功能 普通消息 同步消息异步消息事务消息顺序消息延迟消息订阅与发布消息过滤消息消费重试死信队列...... 架构设计 1个broker是1台实例每个broker都有从节点&#xff0c;便于做故障转移每个broker对应一个文件&#xff0c;存储数据&#xff1f;还是…

基于单片机设计的自动门控制系统

一、前言 自动门控制系统是一种智能化的应用&#xff0c;能够根据人体接近信号自动完成门的打开和关闭操作。在传统的门控系统中&#xff0c;通常需要人手动进行门的开启和关闭&#xff0c;不仅费时费力&#xff0c;还不够智能高效。 本项目采用了STC89C52作为主控芯片&#…

【高数:1 映射与函数】

【高数&#xff1a;1 映射与函数】 例2.1 绝对值函数例2.2 符号函数例2.3 反函数表示例2.4 双曲正弦sinh&#xff0c;双曲余弦cosh&#xff0c;双曲正切tanh 参考书籍&#xff1a;毕文斌, 毛悦悦. Python漫游数学王国[M]. 北京&#xff1a;清华大学出版社&#xff0c;2022. 例2…

1.1美术理论基础

一、光影 物体呈现在人们眼前的时候&#xff0c;不同的受光面其明暗变化以及物体的影子。 1.什么是黑白灰 在美术中黑白灰指亮面、灰面、暗面&#xff0c;属于素描的三大面&#xff0c;主要体验一个物体的整体寿光过程。普遍存在于各种艺术和设计领域。黑白灰作品的出现&#x…

一文搞懂系列——你真的了解如何生成动态库了吗?

引言 动态库的编译&#xff0c;这有什么难度&#xff0c;这不是手到擒来的事情吗&#xff1f;无非不就是&#xff1a; gcc -FPIC -shared -o libxxx.so *.o *.c 我若是提出这些需求场景&#xff0c;阁下又如何应对呢&#xff1f; 动态库A依赖其他部分提供的能力。但是却不…

LinkedList详解

LinkedList详解 LinkedList是List接口的一个主要的实现类之一&#xff0c;基于链表的实现。以java8为例来了解一下LinkedList的源码实现 继承关系 public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>,…

第十五届蓝桥杯模拟赛B组(第二期)C++

前言&#xff1a; 第一次做蓝桥模拟赛的博客记录&#xff0c;可能有很多不足的地方&#xff0c;现在将第十五届蓝桥杯模拟赛B组&#xff08;第二期&#xff09;的题目与代码与大家进行分享&#xff0c;我是用C做的&#xff0c;有好几道算法题当时自己做的也是一脸懵&#xff0c…

DELL EMC unity 存储系统日志收集方法

对于一些非简单的硬件故障&#xff0c;解决故障最有效、最快速的方法就是收集日志&#xff0c;而不是瞎搞。常见的乱搞方法就是 1. reimage系统‘ 2. 更换控制器&#xff1b;3&#xff0c; 重启。 本文详细介绍了图形界面GUI和命令行CLI下如何收集DELL EMC Unity日志的方法和常…

WPS导出的PDF比较糊,和原始的不太一样,将带有SVG的文档输出为PDF

一、在WPS的PPT中 你直接输出PDF可能会导致一些问题&#xff08;比如照片比原来糊&#xff09;/ 或者你复制PPT中的图片到AI中类似的操作&#xff0c;得到的照片比原来糊&#xff0c;所以应该选择打印-->高级打印 然后再另存为PDF 最后再使用AI打开PDF文件再复制到你想用…

挑选数据可视化工具:图表类型、交互功能与数据安全

作为一名数据分析师&#xff0c;我经常需要使用各种数据可视化工具来将数据以直观、清晰的方式呈现出来&#xff0c;以便更好地理解和分析。在市面上的众多可视化工具中&#xff0c;我根据实际需求和项目特点进行选择。本文将从以下几个角度对市面上的数据可视化工具进行对比&a…

flutter开发实战-轮播Swiper更改Custom_layout样式中Widget层级

flutter开发实战-轮播Swiper更改Custom_layout样式中Widget层级 在之前的开发过程中&#xff0c;需要实现卡片轮播效果&#xff0c;但是卡片轮播需要中间大、两边小一些的效果&#xff0c;这里就使用到了Swiper。具体效果如视频所示 添加链接描述 这里需要的效果是中间大、两边…

安装postgresql驱动及python使用pyodbc指定postgresql驱动调用postgresql

注&#xff1a;Python解释器版本(32位/64位)和postgresql驱动版本(32位/64位)需一致。 一、安装postgresql驱动 https://www.postgresql.org/ftp/odbc/versions/msi/ &#xff08;1&#xff09;32位&#xff1a; &#xff08;2&#xff09;64位&#xff1a; 双击安装。全程默…

高精度加法,减法,乘法,除法(上)(C语言)

前言 加&#xff0c;减&#xff0c;乘&#xff0c;除这些运算我们自然信手捏来&#xff0c;就拿加法来说&#xff0c;我们要用c语言编程算ab的和&#xff0c;只需让sum ab即可&#xff0c;可是这是局限的&#xff0c;我们都知道int的表示的最大值为2147483647&#xff08;32位…

2023.12.3 分布式SQL查询引擎-Presto

目录 目录 1.Prosto简介 Apache Hadoop-MapReduce Apache Hive 2.Presto的优缺点 3.个人自用启动服务 个人自用启动服务 3.Presto的架构 4.presto和hive的区别 5.presto优化 6.Presto-内存调优 1.Prosto简介 Apache Hadoop-MapReduce 优点&#xff1a;统一、通用、简…

C - 语言->内存函数

目录 系列文章目录 前言 1. memcpy使⽤和模拟实现 1.2 memcpy函数的模拟实现: 2. memmove 使⽤和模拟实现 2.1memmove的模拟实现&#xff1a; 3. memset 函数的使⽤ 4. memcmp 函数的使⽤ 系列文章目录 ✅作者简介&#xff1a;大家好&#xff0c;我是橘橙黄又青&#xff…