【Redis】缓存和数据库一致性问题及解决方案

news2025/1/12 23:35:55

往期文章: 

【Redis】Redis 底层的数据结构(结合源码) 

【Redis】为什么选择 Redis 做缓存?

【Redis】缓存击穿、缓存穿透、缓存雪崩原理以及多种解决方案 


一、前言

在前面的文章中,我们探讨了为什么要使用 Redis 作为系统的缓存,今天我们接着来探讨加入缓存后可能遇到的一个问题:数据一致性问题

当我们在项目中接入缓存后,用户访问系统获取数据的大致流程如下:

加入缓存(Redis)后,请求先访问到缓存(Redis),而不是直接访问数据库。

在这种业务场景下,可能会出现缓存和数据库数据不一致性的问题。


在更新的时候,操作缓存和数据库无疑就是以下四种可能之一:

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

二、问题分析

1、先更新缓存,再更新数据库


如果缓存更新成功,但在执行更新数据库时,服务器突然宕机了,那么缓存中是最新的数据,而数据库中是旧的数据。

脏数据就因此诞生了,并且如果缓存的信息是单独某张表的,而且这张表也在其他表的关联查询中,那么其他表关联查询出来的数据也是脏数据,结果就是直接会产生一系列的问题。

 

2、先更新数据库,在更新缓存


只有等到缓存过期之后,才能访问到正确的信息,那么在缓存没过期的时间段内,所看到的都是脏数据。

以上两图中只要执行第二步时失败了,就必然会产生脏数据。

 

3、先删除缓存,在更新数据库 


这种方式在没有高并发的情况下,是可能保持数据一致性的。 

如果只有第一步执行成功,而第二步失败,那么只有缓存中的数据被删除了,但是数据库没有更新,那么在下一次进行查询的时候,查不到缓存,只能重新查询数据库,构建缓存,这样其实也是相对做到了数据一致性。

但如果是处于读写并发的情况下,还是会出现数据不一致的情况: 

执行完成后,明显可以看出,1号用户所构建的缓存,并不是最新的数据,还是存在问题的。 

 

4、先更新数据库,在删除缓存 


如果更新数据库成功了,而删除缓存失败了,那么数据库中就会是新数据,而缓存中是旧数据,数据就出现了不一致情况。 

和之前一样,如果两段代码都执行成功,在并发情况下会是什么样呢? 

还是会造成数据的不一致性。

但是此处达成这个数据不一致性的条件明显会比起其他的方式更为困难 :

  • 时刻1:读请求的时候,缓存正好过期
  • 时刻2:读请求在写请求更新数据库之前查询数据库,
  • 时刻3:写请求,在更新数据库之后,要在读请求成功写入缓存前,先执行删除缓存操作。

这通常是很难做到的,因为在真正的并发开发中,更新数据库是需要加锁的,不然没一点安全性~

一定程度上来讲,这种方式还是解决了一定程度上的数据不一致性问题的。

 

以上四种方式无论选择那种方式,如果实在多服务或时并发的情况下,其实都是有可能产生数据不一致性的。

 

 三、解决方案

1、延迟双删

先进行缓存清除,再执行 update,最后(延迟N秒)再执行缓存清除。

进行两次删除,且中间需要延迟一段时间。

// 延迟双删伪代码
public void write(String key,Object data){
    deleteRedisCache(key);       // 删除redis缓存
	updateMysqlSql(obj);         // 更新mysql
	Thread.sleep(100);           // 延迟一段时间
    deleteRedisCache(key);       // 再次删除该key的缓存
}

延迟双删的流程图: 

解决这样的问题,其实最好的方式就是在执行完更新数据库的操作后,先休眠一会儿,再进行一次缓存的删除,以确保数据一致性。

首先延迟删除的时间需要大于用户1执行流程的总时间,就是1号用户从数据库读取数据 写入缓存时间。

2、加入 MQ

无论是更新缓存还是删除缓存,在同时操作缓存和数据库时,都无法保证两者都能一次性操作成功,所以我们最好的办法就是重试,这个重试并不是立即重试,因为缓存和数据库可能因为网络或者其它原因停止服务了,立即重试成功率极低,而且重试会占用线程资源,显然不合理,所以需要采用异步重试机制。

异步重试可以使用消息队列来完成,因为消息队列可以保证消息的可靠性,消息不会丢失,也可以保证正确消费,当且仅当消息消费成功后才会将消息从消息队列中删除。

优点:

  • 可以大幅减少接口的延迟返回的问题
  • MQ 本身有重试机制,无需人工去写重试代码
  • 解耦,把查询 MySQL 和同步 Redis 完全分离,互不干扰

3、Canal 订阅日志

Canal 是一个阿里巴巴开源的项目,用于监听并捕获 MySQL 数据库的 binlog 日志变化,以便实时处理数据变更事件。 

修改数据时只需要更新数据库,无需修改缓存,那什么时候修改缓存呢?

在数据库一条记录发生变更时就会生成一条 binlog 日志,我们可以订阅这种消息,拿到具体的数据,然后根据日志消息更新缓存,订阅日志目前比较流行的就是阿里开源的 Canal,那么我们的架构就变为如下形式:

订阅数据库变更日志,当数据库发生变更时,就可以拿到具体操作的数据,然后再去根据具体的数据,去删除对应的缓存。

当然 Canal 也是要配合消息队列一起来使用的,因为其 Canal 本身是没有数据处理能力的。 

这个方式算的上彻底解耦了,应用程序代码无需再管消息队列方面发送失败问题,全交由 Canal来发送。 

 

参考文章:Redis数据一致性问题的三种解决方案-CSDN博客 

 

一  叶  知  秋,奥  妙  玄  心

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

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

相关文章

独居打工人,把超市当顶配食堂

文 | 螳螂观察 作者 | 如意 独自在大城市扎根的年轻人有着自己的小确幸,比如“周末可以睡到下午才起床,不会有任何人打扰”,“瘫在沙发上吃着零食享受一部自己想看很久的电影,也不会被唠叨。” 但生活并不总是尽如人意&#xf…

基于SpringBoot+Vue的学生宿舍水电信息管理系统

作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…

PDF在线编辑哪家强?2024年4款热门工具大比拼

如今是数字化的时代,PDF 文件对我们工作和学习来说特别重要。不过呢,遇到那些麻烦的 PDF 编辑和转换的事情时,你是不是常常觉得没招儿,甚至还得加班到半夜?别犯愁啦,今天我给你讲讲四款非常好用的 PDF 在线…

深入理解全连接层:从线性代数到 PyTorch 中的 nn.Linear 和 nn.Parameter

文章目录 数学概念(全连接层,线性层)nn.Linear()nn.Parameter()Q1. 为什么 self.weight 的权重矩阵 shape 使用 ( out_features , in_features ) (\text{out\_features}, \text{in\_features}) (out_features,in_features)而不是 ( in_featur…

【人工智能】OpenAI最新发布的GPT-o1模型,和GPT-4o到底哪个更强?最新分析结果就在这里!

在人工智能的快速发展中,OpenAI的每一次新模型发布都引发了广泛的关注与讨论。2023年9月13日,OpenAI正式推出了名为o1的新模型,这一模型不仅是其系列“推理”模型中的首个代表,更是朝着类人人工智能迈进的重要一步。本文将综合分析…

10款超好用的电脑文件加密软件推荐|2024文件加密软件排行榜

在数字时代,数据安全已成为个人和企业不可忽视的重要议题。加密软件作为守护数据安全的坚固防线,其重要性不言而喻。以下是2024年备受推荐的十款电脑文件加密软件。 1.安秉网盾 安秉网盾以其全面的数据保护和安全防护功能备受企业青睐。它支持多种加密…

C语言内存函数(21)

文章目录 前言一、memcpy的使用和模拟实现二、memmove的使用和模拟实现三、memset函数的使用四、memcmp函数的使用总结 前言 正文开始,发车! 一、memcpy的使用和模拟实现 函数模型:void* memcpy(void* destination, const void* source, size…

深入Redis:分布式锁

在一个分布式的系统中,会涉及到多个节点访问同一个公共资源的情况。此时就需要通过锁来做互斥控制,避免出现类似于“线程安全”的问题。 Java中的synchronize只能在当前线程中生效,在分布式的这种多个进程多个主机的场景下就无能为力了。此时…

原型模式详细介绍和代码实现

🎯 设计模式专栏,持续更新中, 欢迎订阅:JAVA实现设计模式 🛠️ 希望小伙伴们一键三连,有问题私信都会回复,或者在评论区直接发言 Java实现原型模式 介绍: 原型模式(Prototype Patte…

C4D2025来了!亮眼的新功能一览

C4D2025新功能亮点,同步上新的Redshift 2025.0.2。等我体验了再给大家讲详细的 成都渲染101云渲染支持对应软件渲染,3090等显卡,云渲码6666 渲染101云渲码6666 Mograph增强 引入线性域标签,用于精细控制对象参数。 为追踪器对象新…

安泰功率放大器有哪些特点呢

功率放大器是电子设备中的重要组成部分,其作用是将输入信号的电功率放大到足够的水平,以驱动负载,如扬声器或天线。功率放大器有一些独特的特点,这些特点对于各种应用至关重要。下面将详细介绍功率放大器的特点,以更好…

【Vue】移动端访问Vue项目页面无数据,但是PC访问有数据

问题: Vue项目,PC访问时下拉列表有数据,移动端访问时下拉列表没有数据; 思路: 首先打开了Fiddler抓包工具,把抓到的url复制到PC浏览器进行访问,结果发现PC上访问这个页面是有数据的&#xff…

利用Leaflet.js创建交互式地图:绘制固定尺寸的长方形

在现代Web开发中,交互式地图已成为展示地理位置数据的重要工具。Leaflet.js是一个轻量级、功能丰富的开源JavaScript库,用于构建移动友好的交互式地图。在本文中,我们将探讨如何利用Leaflet.js在地图上绘制一个固定尺寸的长方形,扩…

堆+堆排序+topK问题

目录 堆: 1、堆的概念 2、堆的结构 3、堆的实现 3.1、建堆 3.1.1、向上调整建堆(用于堆的插入) 3.1.2、向下调整建堆 3.2、堆的删除 3.3、堆的代码实现 3.3.1、Heap.h 3.3.2、Heap.c 堆排序:(O(N*log(N))) 1、排序如何…

接口测试用例的编写

🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 1、接口测试发现的典型问题 接口测试经常遇到的bug和问题,如下: 传入参数处理不当,导致程序crash类型溢出,导…

下载chromedriver驱动

首先进入关于ChromeDriver最新下载地址:Chrome for Testing availability 进入之后找到与自己所匹配的,在浏览器中查看版本号,下载版本号需要一致。 下载即可,解压,找到 直接放在pycharm下即可 因为在环境变量中早已配…

严重干扰的验证码识别系统源码分享

严重干扰的验证码识别检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Comp…

领夹麦克风哪个品牌好?大疆、西圣、博雅无线麦克风在线测评

​随着科技的不断进步,越来越多的专业音频设备出现在大家的视野中,无线领夹麦克风就是其中之一,并且很多人在视频创作、直播等场景中都会进行选购。但是近些年来无线领夹麦克风市场较为复杂,很多质量不佳的产品混杂其中&#xff0…

安全生产许可证的重要性

在现代社会,安全生产许可证对于企业来说,不仅仅是一种法律要求,更是一种社会责任和企业形象的体现。本文将深入探讨安全生产许可证的重要性,以及它如何影响企业的长期发展和社会责任。 一、法律合规性的重要性 安全生产许可证是企…

Windows上指定盘符-安装WSL虚拟机(机械硬盘)

参考来自于教程1:史上最全的WSL安装教程 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/386590591#%E4%B8%80%E3%80%81%E5%AE%89%E8%A3%85WSL2.0 教程2:Windows 10: 将 WSL Linux 实例安装到 D 盘,做成移动硬盘绿色版也不在话下 - 知乎 (z…