深入解析缓存与数据库数据不一致问题

缓存层是提高系统响应速度和扩展性的关键组件。然而,缓存层的引入也带来了数据一致性的挑战。

当数据库中的数据发生变化时,如何确保这些变化能够及时且准确地反映到缓存中,是确保用户体验和系统可靠性的重要问题。

1. 数据一致性

首先,我们需要清楚什么情况符合【数据一致性】

  • 缓存中有数据,缓存中的值需要和数据库中的值保持一致;
  • 缓存中没有数据,数据库中的值必须是最新值。

不属于这两种情况的,则就是缓存和数据库数据不一致问题了。

根据是否接收写请求,我们可以把缓存分成读写缓存只读缓存

1.1 读写缓存

对于读写缓存来说,如果要对数据进行增删改,就需要在缓存中进行,同时还要根据采取的写回策略,决定是否同步写回到数据库中。

  • 同步直写策略:写缓存时,也同步写数据库,缓存和数据库中的数据一致;
  • 异步写回策略:写缓存时不同步写数据库,等到数据从缓存中淘汰时,再写回数据库。使用这种策略时,如果数据还没有写回数据库,缓存就发生了故障,那么,此时,数据库就没有最新的数据了。

所以,对于读写缓存来说,要想保证缓存和数据库中的数据一致,就要采用同步直写策略

不过,需要注意的是,如果采用这种策略,就需要同时更新缓存和数据库

所以,我们要在业务应用中使用事务机制,来保证缓存和数据库的更新具有原子性,也就是说,两者要不一起更新,要不都不更新,返回错误信息,进行重试。否则,我们就无法实现同步直写。

当然,在有些场景下,我们对数据一致性的要求可能不是那么高,比如说缓存的是电商商品的非关键属性或者短视频的创建或修改时间等,那么,我们可以使用异步写回策略。

1.2 只读缓存

对于只读缓存来说,当有数据新增时,会直接写入数据库;

当有数据删改时,需要把缓存中的数据标记为无效,当应用后续再访问这些增删改的数据时,因为发生缓存缺失,就会读取数据库,把数据放入缓存中了。

在这里插入图片描述
那么在上面的步骤中,“数据不一致”的情况会不会发生呢?

1.2.1 新增操作

如果是新增数据,则直接保存到数据库中,缓存中是没有值的。符合一致性的第2种情况,此时缓存和数据库中数据是一致的。

1.2.2 删改数据

如果发生删改操作,既要更新数据库,也要删除缓存,这两个操作如果无法保证原子性,就会发生数据不一致的情况。

那么到底是先更新数据库,再删除缓存?还是先删除缓存,再更新数据库呢?我们分别来讨论下。

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

假如删除缓存成功,更新数据库失败,那么当应用再次访问数据时,发生缓存缺失,就去访问数据库,而数据库中的值为旧值。

在这里插入图片描述

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

如果更新数据库成功,删除缓存失败了,那数据库中的值是最新值,缓存中的值是旧值,这肯定是不一致的。
在这里插入图片描述

重试机制

从上面的流程可以看出,无论是哪种情况,都会发生数据不一致的情况,那如何解决呢?就是重试机制。

比如说,当应用删除缓存失败或更新数据库失败时,可以把要删除的缓存值或要更新的数据库值保存到消息队列中,然后从消息队列中重新读取这些值,再次的进行删除或更新。

在这里插入图片描述

并发情况

在上面的讨论中,说的是在更新数据库和删除缓存中,有一个失败的情况下,导致的“数据不一致”。

实际上,即使这两个操作都成功,当有大量并发请求时,应用还是有可能读到不一致的数据。

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

比如,线程 A 删除缓存成功,再还没来得及更新数据库时,线程 B 开始读取数据,它发现缓存缺失,然后去数据库读取数据,此时有两个问题:

  • 线程 B 读取到了旧值;
  • 线程 B 会把读取到的旧值,写入到缓存中,这会导致其他线程从缓存中读到旧值(会一直脏下去,直到缓存过期)

等到线程 B 执行完成后,线程 A 才开始更新数据库,此时数据库数据是最新值,缓存中是旧值。

在这里插入图片描述
针对这种情况,我们可以在线程 A 更新数据库后,sleep 一小段时间,再执行一次删除缓存操作。

线程 A sleep 的时间,就需要大于线程 B 读取数据再写入缓存的时间。

这个时间怎么确定呢?可以在业务程序运行的时候,统计下线程读数据和写缓存的操作时间,以此为基础来进行估算。

当其它线程读取数据时,会发现缓存缺失,所以会从数据库中读取最新值。

在这里插入图片描述

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

当线程 A 更新数据库后,还没来得及删除缓存,此时线程 B开始读取数据,发生缓存命中,读取到旧数据。

但是,线程 A很快就执行删除缓存,让缓存失效了,后续的查询请求会发生缓存缺失,然后去查询数据库最新值了。

所以,这种情况对业务的影响较小。

但是,有一种极端情况
在这里插入图片描述
但是这种情况,理论上会出现,实际上概率特别低。

它需要满足,在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大。

总结

下面我们来总结下,发生数据不一致的情况有哪些

  • 删除缓存或更新数据库某一步失败而导致的数据不一致,可以使用重试机制确保删除或更新操作成功。
  • 在删除缓存值、更新数据库的这两步操作中,有其他线程的并发读操作,导致其他线程读取到旧值,应对方案是延迟双删。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

排序算法详解

稳定性 在排序算法中,稳定性是一个重要的概念,指的是在排序过程中,如果两个元素的值相等,它们在排序后的相对位置与排序前的相对位置保持不变的特性。 稳定排序与不稳定排序 稳定排序:在排序时,相等的元素…

【智能大数据分析 | 实验三】Storm实验:实时WordCountTopology

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈智能大数据分析 ⌋ ⌋ ⌋ 智能大数据分析是指利用先进的技术和算法对大规模数据进行深入分析和挖掘,以提取有价值的信息和洞察。它结合了大数据技术、人工智能(AI)、机器学习(ML&a…

nginx中的HTTP 负载均衡

HTTP 负载均衡:如何实现多台服务器的高效分发 为了让流量均匀分配到两台或多台 HTTP 服务器上,我们可以通过 NGINX 的 upstream 代码块实现负载均衡。 方法 在 NGINX 的 HTTP 模块内使用 upstream 代码块对 HTTP 服务器实施负载均衡: upstr…

小新学习Docker之Ansible 的脚本 --- playbook 剧本

一、playbook 剧本简介 playbooks 本身由以下各部分组成: (1)Tasks:任务,即通过 task 调用 ansible 的模板将多个操作组织在一个 playbook 中运行 (2)Variables:变量 (3…

【Java后端】之 ThreadLocal 详解

想象一下,你有一个工具箱,里面放着各种工具。在多人共用这个工具箱的时候,很容易出现混乱,比如有人拿走了你的锤子,或者你找不到合适的螺丝刀。为了避免这种情况,最好的办法就是每个人都有自己独立的工具箱…

商业智能(BI)及其常见技术

简介 商业智能(Business Intelligence, BI)是一系列技术和方法的集合,旨在帮助企业从大量数据中提取有用的信息,支持决策制定和业务优化。商业智能系统通常包括数据收集、数据存储、数据处理、数据分析和数据可视化等多个环节&am…

数据结构-顺序栈

栈:是一种特殊的线性表,其只允许在表尾进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。 压栈:栈的插入操作叫…

nvm实现node多版本管理

在项目中是否遇到vue2和vue3项目同时要启动,而又得频繁去安装卸载node版本的困扰? Node Version Manager (nvm) 是一个用于管理多个 Node.js 版本的工具,它允许你在同一台机器上安装和切换不同版本的 Node.js,非常适合开发者在不…

Ubuntu22.04中安装英伟达驱动并部署Pytorch深度学习环境

安装英伟达驱动 本文基于windows10ubuntu22.04双系统,给ubuntu22.04安装英伟达驱动。 安装依赖。 sudo apt update # 获取最新的软件包信息 sudo apt upgrade # 升级软件包 sudo apt install g sudo apt install gcc sudo apt install make禁用ubuntu默认驱动Nouv…

Crawler4j在多线程网页抓取中的应用

网页爬虫作为获取网络数据的重要工具,其效率和性能直接影响到数据获取的速度和质量。Crawler4j作为一个强大的Java库,专门用于网页爬取,提供了丰富的功能来帮助开发者高效地抓取网页内容。本文将探讨如何利用Crawler4j进行多线程网页抓取&…

RHCE的学习(3)

第三章 远程登录服务 简介 概念 远程连接服务器通过文字或图形接口方式来远程登录系统,让你在远程终端前登录linux主机以取得可操作主机接口(shell),而登录后的操作感觉就像是坐在系统前面一样 功能: 分享主机的运算能力 服务…

京存助力北京某电力研究所数据采集

北京某电力研究所已建成了一套以光纤为主,卫星、载波、微波等多种通信方式共存,分层级的电力专用的网络通信架构体系。随着用电、配电对网络的要求提高,以及终端通信入网的迅速发展,迫切地需要高效的通信管理系统来应对大规模、复…

Java项目-基于springboot框架的校园在线拍卖系统项目实战(附源码+文档)

作者:计算机学长阿伟 开发技术:SpringBoot、SSM、Vue、MySQL、ElementUI等,“文末源码”。 开发运行环境 开发语言:Java数据库:MySQL技术:SpringBoot、Vue、Mybaits Plus、ELementUI工具:IDEA/…

ubuntu 虚拟机将linux文件夹映射为windows网络位置

在使用虚拟机时可以选择将windows的文件夹设置为共享文件夹方便在虚拟机中访问windows中的文件,同理,也可以将linux的文件夹共享为一个网络文件夹,通过windows的添加一个网络位置功能,将linux的文件夹映射到windows本地,方便windows访问使用linux的文件夹 参照如下:https://blo…

ThingsBoard 规则链节点:Create Alarm节点详解

引言 用法和含义 主要功能 配置步骤 使用场景 实际项目中的应用案例 案例1:智能温室管理系统 案例2:工厂设备监控系统 总结 引言 ThingsBoard 是一个开源的物联网平台,它提供了设备管理、数据收集、处理和可视化等功能。规则链是 Thi…

私域电商新纪元:消费增值模式引领业绩飞跃

朋友们,你们好!我是吴军,热衷于引领各位深入发掘私域电商领域的独特魅力及其隐藏的机遇。 今日,我想讲述一个激励人心的真实案例。就在过去的一个月里,我们的合作伙伴取得了惊人的业绩突破,销售额一举跨越…

雷池WAF自动化实现安全运营实操案例终极篇

免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停…

队列(数据结构)——C语言

目录 1.概念与结构 2.队列的实现 初始化QueueInit 申请新节点BuyNode 入队QueuePush 判断队为空QueueEmpty 出队QueuePop 读取队头数据QueueFront 读取队尾数据QueueBack 元素个数QueueSize 销毁队列QueueDestroy 3.整体代码 (文章中结点和节点是同一个意思) 1.概…

15. 软件接口

文章目录 第15章 软件接口15.1接口的概念多个接口操作、事件和属性接口演进 15.2 设计接口接口的范围交互方式交换数据的表示形式和结构可扩展标记语言(XML)JavaScript 对象表示法(JSON)Protocol Buffers 错误处理 15.3 接口文档…

200元运动蓝牙耳机有哪些?爆款测评PK力荐!

在运动场景下,传统的入耳式和半入耳式耳机虽然占据了大部分市场,但并不适合所有人,尤其是在长时间运动中佩戴时,耳道的压迫感往往会导致不适。而骨传导耳机虽然通过不塞入耳道的方式改善了佩戴舒适度,但在音质方面与入…