数据库和缓存如何保持一致性

news2024/11/16 8:41:39

目录

 

前言

更新数据库+更新缓存:

1.在更新缓存前先加一个分布式锁

2.在更新完缓存时,给缓存加上较短的过期时间

Cache Aside策略

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

延迟双删

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

保证两个操作都能执行成功

重试机制

订阅MySQLbinlog,再操作缓存

 总结


前言

由于引入了缓存,那么在数据更新时,不仅要更新数据库,而且要更新缓存,这两个更新操作存在前后的问题:

  • 先更新数据库,再更新缓存;
  • 先更新缓存,再更新数据库;

但是会因为并发问题造成缓存和数据库的数据不一致的现象。

如果先更新缓存,在更新数据库:可能会出现下图

缓存和数据库中的数据依然可能不一致。

出现并发问题的时候,当两个请求并发更新同一条数据时,可能会出现缓存和数据库中的数据不一致的现象。 

更新数据库+更新缓存:

如果先更新数据库再更新缓存:可能会出现下图情况

此时,数据库中的数据是 2,而缓存中的数据却是 1,出现了缓存和数据库中的数据不一致的现象

如果业务对缓存命中率有很高的要求,我们也可以采用这个方案。

如何解决这个方式的数据不一致的问题:

1.在更新缓存前先加一个分布式锁

保证同一时间只运行一个请求更新缓存,就不会产生并发问题了,当然引入了锁后,对于写入的性能就会带来影响。

2.在更新完缓存时,给缓存加上较短的过期时间

这样出现缓存不一致的时候,缓存数据也能很快过期,对业务还是能接受的。

Cache Aside策略

不更新缓存,而是删除缓存中的数据,然后到读取数据时,发现缓存中没数据之后,再从数据库中读取数据并更新到缓存中。

写策略与读策略:

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

依然在读写并发时,还是会出现缓存和数据库的数据不一致的问题。

针对读写的并发请求而造成不一致的解决办法是

延迟双删

伪代码:

#删除缓存
redis.delKey(X)
#更新数据库
db.update(X)
#睡眠
Thread.sleep(N)
#再删除缓存
redis.delKey(X)

加了个睡眠时间,主要是为了确保请求 A 在睡眠的时候,请求 B 能够在这这一段时间完成「从数据库读取数据,再把缺失的缓存写入缓存」的操作,然后请求 A 睡眠完,再删除缓存。

所以,请求 A 的睡眠时间就需要大于请求 B 「从数据库读取数据 + 写入缓存」的时间。

但是具体睡眠多久其实是个玄学,很难评估出来,所以这个方案也只是尽可能保证一致性而已,极端情况下,依然也会出现缓存不一致的现象。

因此,还是比较建议用「先更新数据库,再删除缓存」的方案。

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

还是会出现不一致的问题,但这个概率不高,因为缓存的写入通常要远远快于数据库的写入,所以,先更新数据库再删除缓存是可以保证数据一致性的。

还可以给缓存数据加上过期时间,就算在这期间存在缓存数据不一致,经过过期时间,最终还是能达到一致。

但是这个依然会遇到问题:当删除缓存失败时,缓存中的数据依然是旧数据,没有更新。再给缓存加上过期时间后,还要等一段时间才能更新生效的现象。

保证两个操作都能执行成功

通过重试机制订阅MySQL binlog,再操作缓存的方式都可以保证两个操作都能成功。

重试机制

引入消息队列,将第二个操作(删除缓存)要操作的数据加入到消息队列,有消费者来操作数据。

如果应用删除缓存失败,可以从消息队列中重新读取数据,然后再次删除缓存。如果重试超过一定次数还没有成功,就需要向业务层发送报错信息。

如果删除缓存成功,就要把数据从消息队列中移除,避免重复操作。

订阅MySQLbinlog,再操作缓存

先更新数据库,再删缓存」的策略的第一步是更新数据库,那么更新数据库成功,就会产生一条变更日志,记录在 binlog 里。

于是我们就可以通过订阅 binlog 日志,拿到具体要操作的数据,然后再执行缓存删除,阿里巴巴开源的 Canal 中间件就是基于这个实现的。

Canal 模拟 MySQL 主从复制的交互协议,把自己伪装成一个 MySQL 的从节点,向 MySQL 主节点发送 dump 请求,MySQL 收到请求后,就会开始推送 Binlog 给 Canal,Canal 解析 Binlog 字节流之后,转换为便于读取的结构化数据,供下游程序订阅使用。

所以,如果要想保证「先更新数据库,再删缓存」策略第二个操作能执行成功,我们可以使用「消息队列来重试缓存的删除」,或者「订阅 MySQL binlog 再操作缓存」,这两种方法有一个共同的特点,都是采用异步操作缓存。

 总结

数据库和缓存保持一致性:

方式问题解决

先更新缓存,

再更新数据库

数据不一致(并发问题)暂无

先更新数据库,

再更新缓存

同上

1.在更新缓存前加上分布式锁;

2.给缓存加上较短的过期时间。

先删除缓存,

再更新数据库

数据不一致(读写并发)延迟双删

先更新数据库,

再删除缓存

同上

1.重试机制;

2.订阅MySQL binlog,再操作缓存。

 

啊,这个要记好多,回头再来背一遍。

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

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

相关文章

软考58-上午题-【数据库】-分布式数据库

一、四个透明 二、四种性质 三、真题 真题1: 真题2: 真题3: 真题4: 真题5:

信钰证券|沪指震荡涨0.26%,传媒等板块拉升,消费电子概念活跃

5日早盘,沪指盘中窄幅震荡上扬,创业板指、科创50指数走高,北证50指数跌超2%;北向资金小幅流入。 截至午间收盘,沪指涨0.26%报3047.2点,深成指微涨0.05%,创业板指涨0.42%,科创50指数…

mysql高可用架构设计

一、主从架构 主从架构一般如下所示 这里从节点一般设置成只读(readonly)模式。这样做,有以下几个考虑: 有时候一些运营类的查询语句会被放到备库上去查,设置为只读可以防止误操作; 防止切换逻辑有 bug&a…

uniapp制作--简单的tab切换

一、实现思路 在UniApp中&#xff0c;可以使用v-if来控制Tab栏并进行切换。 创建一个方法来控制点击时的效果。 二、实现步骤 ①view部分展示 <!-- tab选项 --><view class"select-area"><view class"select-top"><view clas…

软件测试行业最真实的写照,我后悔了。。。

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 软件测试是一个付出就有回报的工作&#xff0c;可能很多人会说软件测试就是吃青春饭&#xff0c;…

vs创建asp.net core webapi发布到ISS服务器

打开服务器创建test123文件夹&#xff0c;并设置共享。 ISS配置信息&#xff1a; 邮件网站&#xff0c;添加网站 webapi asp.net core发布到ISS服务器网页无法打开解决方法 点击ISS Express测试&#xff0c;可以成功打开网页。 点击生成&#xff0c;发布到服务器 找到服务器IP…

如何处理微服务之间的通信和数据一致性?

✨✨祝屏幕前的兄弟姐妹们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; 目录 引言 一、微服务通信 1、同步通信&#xff1a;HTTP 1.1.同步通信示例代码&#xf…

Q-Align Teaching LMMs for Visual Scoring via Discrete Text-Defined Levels

Q-Align: Teaching LMMs for Visual Scoring via Discrete Text-Defined Levels TL; DR&#xff1a;教会多模态大模型用文本等级评价词&#xff08;如 Good、Bad、Excellent 等&#xff09;来评估视觉质量分。 图像美学质量评估是一个小方向&#xff0c;但是实际业务中很有用&…

Jenkins 将shell脚本启动方式修改为bash

platform"arm x86" if [[ "$platform" ~ "arm" ]] thenecho "arm" fi最近在调试Jenkins实现的一些功能&#xff0c;发现在本地可以运行的脚本内容到了Jenkins里面就没办法运行了&#xff0c;不是提示unexpected operator就是提示[[ : …

鸿蒙Harmony应用开发—ArkTS声明式开发(通用属性:拖拽控制)

设置组件是否可以响应拖拽事件。 说明&#xff1a; 从API Version 10开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 ArkUI框架对以下组件实现了默认的拖拽能力&#xff0c;支持对数据的拖出或拖入响应&#xff0c;开发者只需要将这些组件…

商家转账到零钱申请时间要多久

商家转账到零钱是什么&#xff1f; 【商家转账到零钱】功能整合了微信支付之前的【企业付款到零钱】【批量转账到零钱】功能&#xff0c;支持批量对外转账&#xff0c;对有批量对用户付款需求的应用场景更友好&#xff0c;操作便捷。如果你的应用场景是单付款场景的话&#xf…

运维打工人,周末兼职送外卖的一天

运维打工人&#xff0c;周末兼职送外卖的一天 在那个不经意的周末&#xff0c;我决定尝试一份新的工作——为美团外卖做兼职配送员。这份工作对于一向规律生活的我来说&#xff0c;既是突破也是挑战。 早晨&#xff0c;空气中带着几分凉意和宁静。准备好出发时&#xff0c;线…

如何做代币分析:以 CRO 币为例

作者&#xff1a;lesleyfootprint.network 编译&#xff1a;Mingfootprint.network 数据源&#xff1a;CRO Token Dashboard &#xff08;仅包括以太坊数据&#xff09; 在加密货币和数字资产领域&#xff0c;代币分析起着至关重要的作用。代币分析指的是深入研究与代币相关…

Unity编辑器功能Inspector快捷自动填充数据

我们有时候可能需要在面板增加一些引用&#xff0c;可能添加脚本后要手动拖动&#xff0c;这样如果有大量的脚本拖动也是不小的工作量 实例 例如&#xff1a;我的脚本需要添加一个Bone的列表&#xff0c;一个个拖动很麻烦。 实现脚本 我们可以用这样的脚本来实现。 public…

鼠标右键没有git bash here,右键添加git bash here并增加图标

突然发现自己鼠标右键没有git bash here&#xff0c;或者安装之后就没有git bash here。后面那种情况多半是没有默认装在C盘。我们装在其他盘的时候就需要自己去配置。git gui目前用不上&#xff0c;这里只讲git bash here。网上一堆教程&#xff0c;说法不同大多不能用要么就很…

Vue router文件中本地路由配置使用i18n【解决tab名称出现undefined,导致i18n没有实现问题】

问题 点击按钮 跳转详情页后 tab名称出现错误&#xff0c;报 undefined ## 需求 点击工单详情按钮&#xff0c;跳转详情页面&#xff08;新页面&#xff09;&#xff0c;新页面tab栏名称 还是为 工单出库&#xff0c;但要求工单出库文字配置为多语言&#xff0c;使用i18n来配置…

SPC 之 I-MR 控制图

概述 1924 年&#xff0c;美国的休哈特博士应用统计数学理论将 3Sigma 原理运用于生产过程中&#xff0c;并发表了 著名的“控制图法”&#xff0c;对产品特性和过程变量进行控制&#xff0c;开启了统计过程控制新时代。 什么是控制图 控制图指示过程何时不受控制&#xff…

中医舌苔笔记

舌诊时按照舌尖-舌中-舌根-舌侧的顺序进行观察。 先看舌体再看舌苔&#xff0c;30秒左右。 如果一次望舌判断不清&#xff0c;可令病人休息3~5分钟后&#xff0c;重新观察一次 舌诊脏腑部位分属图 舌体 胖嫩而边有齿痕为气虚、阳虚。 薄白而润为风寒&#xff1b; 薄白而燥…

RabbitMQ如何实现延迟消息?

RabbitMQ中是可以实现延迟消息的&#xff0c;一般有两种方式&#xff0c;分别是通过死信队列以及通过延迟消息插件来实现。 扩展&#xff1a; 死信队列 当rabbitMQ中的一条正常的消息&#xff0c;因为过了存活时间&#xff08;TTL过期&#xff09;&#xff0c;队列长度超限&a…

【论文精读】Attention Bottlenecks for Multimodal Fusion 视频分类任务

本文并非逐句翻译&#xff0c;添加个人理解与疑惑&#xff0c;如有需要&#xff0c;请自行阅读原文。 Attention Bottlenecks for Multimodal Fusion 多模态融合的注意力瓶颈 会议&#xff1a;NIPS2021 Benchmark&#xff1a;Audioset、Epic Kitchens和VGGSound等 Backbone&…