如何保证缓存与数据库双写时的数据一致性?

news2024/11/19 4:42:12

背景:使用到缓存,无论是本地内存做缓存还是使用 Redis 做缓存,那么就会存在数据同步的问题,因为配置信息缓存在内存中,而内存时无法感知到数据在数据库的修改。这样就会造成数据库中的数据与缓存中数据不一致的问题。

共有四种方案:

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

第一种和第二种方案,没有人使用的,因为第一种方案存在问题是:并发更新数据库场景下,会将脏数据刷到缓存。

第二种方案存在的问题是:如果先更新缓存成功,但是数据库更新失败,则肯定会造成数据不一致。

目前主要用第三和第四种方案。

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

该方案也会出问题,此时来了两个请求,请求 A(更新操作) 和请求 B(查询操作)

  1. 请求A进行写操作,删除缓存
  2. 请求B查询发现缓存不存在
  3. 请求B去数据库查询得到旧值
  4. 请求B将旧值写入缓存
  5. 请求A将新值写入数据库

上述情况就会导致不一致的情形出现。而且,如果不采用给缓存设置过期时间策略,该数据永远都是脏数据。

答案一:延时双删

最简单的解决办法延时双删

使用伪代码如下:
 

public void write(String key,Object data){
        Redis.delKey(key);
        db.updateData(data);
        Thread.sleep(1000);
        Redis.delKey(key);
    }

转化为中文描述就是 (1)先淘汰缓存 (2)再写数据库(这两步和原来一样) (3)休眠1秒,再次淘汰缓存,这么做,可以将1秒内所造成的缓存脏数据,再次删除。确保读请求结束,写请求可以删除读请求造成的缓存脏数据。自行评估自己的项目的读数据业务逻辑的耗时,写数据的休眠时间则在读数据业务逻辑的耗时基础上,加几百ms即可。

如果使用的是 Mysql 的读写分离的架构的话,那么其实主从同步之间也会有时间差。

主从同步时间差

此时来了两个请求,请求 A(更新操作) 和请求 B(查询操作)

  1. 请求 A 更新操作,删除了 Redis
  2. 请求主库进行更新操作,主库与从库进行同步数据的操作
  3. 请 B 查询操作,发现 Redis 中没有数据
  4. 去从库中拿去数据
  5. 此时同步数据还未完成,拿到的数据是旧数据

此时的解决办法就是如果是对 Redis 进行填充数据的查询数据库操作,那么就强制将其指向主库进行查询。

从主库中拿数据

答案二: 更新与读取操作进行异步串行化

异步串行化

我在系统内部维护n个内存队列,更新数据的时候,根据数据的唯一标识,将该操作路由之后,发送到其中一个jvm内部的内存队列中(对同一数据的请求发送到同一个队列)。读取数据的时候,如果发现数据不在缓存中,并且此时队列里有更新库存的操作,那么将重新读取数据+更新缓存的操作,根据唯一标识路由之后,也将发送到同一个jvm内部的内存队列中。然后每个队列对应一个工作线程,每个工作线程串行地拿到对应的操作,然后一条一条的执行。

这样的话,一个数据变更的操作,先执行删除缓存,然后再去更新数据库,但是还没完成更新的时候,如果此时一个读请求过来,读到了空的缓存,那么可以先将缓存更新的请求发送到队列中,此时会在队列中积压,排在刚才更新库的操作之后,然后同步等待缓存更新完成,再读库。

读操作去重

多个读库更新缓存的请求串在同一个队列中是没意义的,因此可以做过滤,如果发现队列中已经有了该数据的更新缓存的请求了,那么就不用再放进去了,直接等待前面的更新操作请求完成即可,待那个队列对应的工作线程完成了上一个操作(数据库的修改)之后,才会去执行下一个操作(读库更新缓存),此时会从数据库中读取最新的值,然后写入缓存中。

如果请求还在等待时间范围内,不断轮询发现可以取到值了,那么就直接返回;如果请求等待的时间超过一定时长,那么这一次直接从数据库中读取当前的旧值。(返回旧值不是又导致缓存和数据库不一致了么?那至少可以减少这个情况发生,因为等待超时也不是每次都是,几率很小吧。这里我想的是,如果超时了就直接读旧值,这时候仅仅是读库后返回而不放缓存)

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

这一种情况也会出现问题,比如更新数据库成功了,但是在删除缓存的阶段出错了没有删除成功,那么此时再读取缓存的时候每次都是错误的数据了。

此时解决方案就是利用消息队列进行删除的补偿。具体的业务逻辑用语言描述如下:

  1. 请求 A 先对数据库进行更新操作
  2. 在对 Redis 进行删除操作的时候发现报错,删除失败
  3. 此时将Redis 的 key 作为消息体发送到消息队列中
  4. 系统接收到消息队列发送的消息后再次对 Redis 进行删除操作

但是这个方案会有一个缺点就是会对业务代码造成大量的侵入,深深的耦合在一起,所以这时会有一个优化的方案,我们知道对 Mysql 数据库更新操作后再 binlog 日志中我们都能够找到相应的操作,那么我们可以订阅 Mysql 数据库的 binlog 日志对缓存进行操作。

利用订阅 binlog 删除缓存

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

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

相关文章

代码中遇到的问题2

目录 记录: 好处: 问题一: 解答: 问题二: 解答: 常见类型问题: 记录: string connStr ConfigurationManager.ConnectionStrings["connStr"].ConnectionString; 在代码中写上这段代码,将连接数据库的代码语句放到&#xff…

安全基础~通用漏洞2

文章目录 知识补充盲注Boolean盲注延时盲注报错注入二次注入 知识补充 盲注常用 if(条件,5,0) #条件成立 返回5 反之 返回0 left(database(),1),database() #left(a,b)从左侧截取a的前b位 盲注 盲注就是在注入过程中,获取的数据不能回显至前端页面。 …

Oracle Linux 7.9 安装图解

风险告知 本人及本篇博文不为任何人及任何行为的任何风险承担责任,图解仅供参考,请悉知!本次安装图解是在一个全新的演示环境下进行的,演示环境中没有任何有价值的数据,但这并不代表摆在你面前的环境也是如此。生产环境…

Ask for Power Apps 消失了?

Ask for Power Apps 消失了? 背景替换定义一个接收数组的参数1.我们新建一个Text接收参数取名为**Arrlist**.定义一个参数类型是Array 背景 今天才发现,我在flow中想向power apps索要一个参数,但是之前的Ask for Power Apps 这个触发器怎么也找不到了。…

【Vulnhub通关】Kioptrix:Level 1 (#1)

文章目录 准备工作靶机基本信息下载 渗透流程主机发现目标扫描服务探测HTTP(S)服务SMB文件共享服务 权限提升本次靶机测试到此结束 注意:本文转载自本人稀土掘金博客。博客地址:御坂19008号 的个人主页 - 动态 - 掘金 准备工作 靶机基本信息 靶机名称&a…

SUSE Linux 15 SP5 安装图解

风险告知 本人及本篇博文不为任何人及任何行为的任何风险承担责任,图解仅供参考,请悉知!本次安装图解是在一个全新的演示环境下进行的,演示环境中没有任何有价值的数据,但这并不代表摆在你面前的环境也是如此。生产环境…

寒假思维训练day12 E. Increasing Subsequences

适合喜欢算法、对算法感兴趣的朋友。 今天又来更新啦,断更一天,有点摆了,今天继续补上,献上一道1800的构造。 摘要: part1:关于一些构造题的总结 part2: 每日一题: Problem - E - Codeforces (链接在此…

【XR806开发板试用】通过MQTT实现手机远程实现PWM控灯

本文参与极术社区的《基于安谋科技STAR-MC1的XR806开发板试用》活动。 一、例程编译、烧录确认 首先按照全志在线文档平台的点灯教程确保能正常编译、烧录和点灯:https://xr806.docs.aw-ol.com/study/soft_led/ 确保例程没问题后,我们再改造例程&#xf…

Java入门高频考查基础知识5(扎实技术基础应变一切变化-45题4.2万字参考答案)

技术变革裁员影响的因素: 自动化替代简单重复性工作:随着技术的发展,一些简单、重复性的编码任务可能被自动化工具或者机器学习算法取代。这可能导致一些岗位的需求减少或者消失,从而可能导致部分人员裁员。 技能更新要求&#x…

怎么移除WordPress后台工具栏“新建”菜单?如何添加“新建文章”菜单?

默认情况下,WordPress后台顶部管理工具栏有左侧有一个“新建”菜单,而且还有下拉菜单文章、媒体、链接、页面和用户等,不过我们平时用得最多的就是“新建文章”,虽然可以直接点击“新建”,或点击“新建 – 文章”&…

Spring5系列学习文章分享---第三篇(AOP概念+原理+动态代理+术语+Aspect+操作案例(注解与配置方式))

目录 AOP概念AOP底层原理AOP(JDK动态代理)使用 JDK 动态代理,使用 Proxy 类里面的方法创建代理对象**编写** **JDK** 动态代理代码 AOP(术语)AOP操作(准备工作)**AOP** **操作(**AspectJ注解)**AOP** **操作(**AspectJ…

c++ 包管理工具vcpkg

微软包管理工具 一、下载 git clone https://github.com/microsoft/vcpkg二、初始化 ./vcpkg/bootstrap-vcpkg.sh三、查看帮助文档 ./vcpkg/vcpkg help四、安装包 vcpkg/vcpkg install fmt五、查看安装包 vcpkg/vcpkg list输出 包实际安装路径 ./vcpkg/packages/fmt_x…

LoadRunner从零开始之走近LoadRunner

3.1 LoadRunner 的运行原理 安装LoadRunner 后,在菜单“开始” 一“MercuryLoadRunner” 中,你会看 到这样一组程序,如图 3-1 所示。 • 其中Applications 下面的Analysis、Controller 和Virtual User Generator 是我们 做性能测试最常用的…

大学生求职遇到在线测评 需要结合实际做吗

每年毕业季,都有大量的大学生求职,企业在这个时候往往能够收到很多的求职简历,尤其是一些比较好的岗位,原本只是想要招收10个人,但是结果光是简历就收到上千个简历,一个个面试不实际,浪费时间和…

猫用空气净化器哪些好?五款宠物空气净化推荐!

如今,养宠物的家庭越来越多了!家里因此变得更加温馨,但同时也会带来一些问题,比如异味和空气中的毛发可能会对健康造成困扰。 为了避免家中弥漫着异味,特别是来自宠物便便的味道,一款能够处理家里异味的宠…

数字证书和数字证书认证机构和数字根证书,CA,RCA

文章目录 一、 数字证书1、什么是数字证书2、数字证书干什么的3、风险 二、数字证书认证机构(Certificate Authority,缩写为CA)参考文章 一、 数字证书 维基百科 公开密钥认证(英语:Public key certificate&#xff…

Unity Text超框 文字滚动循环显示

Unity Text超框 文字滚动循环显示 //container Text using System.Collections; using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; using UnityEngine.UI;public class AutoScrollText : MonoBehaviour {private Text[] _texts new Text[…

项目难点和优化

难点: 对于同一个位置百度地图定位的经纬度和腾讯地图定位的经纬度不一样? 解决:由于两者所用的算法不同,计算出来的经纬度也是不一样的,将百度地图的经纬度转换成腾讯地图的经纬度/腾讯的经纬度转化百度的经纬度 export functi…

【超简版,代码可用!】【0基础Python爬虫入门——下载歌曲/视频】

安装第三方模块— requests 完成图片操作后输入:pip install requests 科普: get:公开数据 post:加密 ,个人信息 进入某音乐网页,打开开发者工具F12 选择网络,再选择—>媒体——>获取URL【先完成刷新页面】 科…

环保投入超20亿,齐鲁制药集团争做绿色制药标杆

近期,由人民日报社指导、人民网主办的2023人民企业社会责任荣誉盛典暨第十八届人民企业社会责任奖颁奖活动在京举行,齐鲁制药集团荣获“人民企业社会责任奖绿色发展奖”。多年来,齐鲁制药深入践行“绿水青山就是金山银山”理念,积…