当前位置: 首页 > news >正文

如何解决缓存和数据库的数据不一致问题

数据不一致问题是操作数据库和操作缓存值的过程中,其中一个操作失败的情况。实际上,即使这两个操作第一次执行时都没有失败,当有大量并发请求时,应用还是有可能读到不一致的数据。

如何更新缓存

更新缓存的步骤就两步,更新缓存和更新数据库。但是这两步会引申多种情况。

  1. 先更新数据库还是先更新缓存?更新缓存时先删除还是直接更新?
  2. 假设第一步成功了,第二步失败了怎么办?
  3. 假设 2 个线程同时更新同一个数据,A 线程先完成第一步,B 线程先完成第二步,此时该怎么办?
先更新缓存,再更新数据库

对于这个组合可能会出现一下两种情况:

  1. 假设第 2 步数据库更新失败了,要求回滚缓存的更新,这时该怎么办呢?我们知道 Redis 不支持事务回滚,除非我们采用手工回滚的方式,先保存原有数据,然后再将缓存更新回原来的数据,这种解决方案不考虑。
  2. 线程A先将缓存的数据更新为 1,然后在更新数据库前,线程B将缓存的数据更新为 2,由于网络等原因,线程B先一步把数据库更新为 2,然后线程A将数据库的数据更新为 1,此时,就出现了缓存和数据库中数据不一致的现象,如下图所示。

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

使用这种组合,即使数据库更新失败了也不需要回滚缓存。这种组合会出现两种问题。

假设某个商品数量是10个,线程A要更新商品的数量为20,所以它会删除缓存中的内容。这时,另一个线程 B 要读取这个商品的数量,它查询缓存发现未命中后,会从数据库中读取到商品数量为10,并且写入到缓存中,然后线程 A 继续更改数据库,将商品的数量更新为20。

为了解决一致性问题,可以让线程A给Key加锁,这种处理方法可能会使大量的读请求卡在锁中。

先更新数据库,再更新缓存
  1. 数据库更新成功,更新缓存失败怎么办?

数据库更新成功后,不会因为缓存是否成功而回滚。此时一般会采取重试机制来补偿,但是重试机制如果存在延时还是会出现数据不一致的问题,不好处理。

  1. 两个线程同时更新同一个数据,线程A先完成第一步,线程B先完成了第二步怎么办?

线程A 先将数据库的数据更新为 1,然后在更新缓存前,线程B 将数据库的数据更新为 2,然后把缓存更新为 2,然后 线程A更新缓存为 1。
此时,数据库中的数据是 2,而缓存中的数据却是 1,缓存和数据库中的数据不一致。

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

假如某个商品数据在缓存中不存在,线程 A 读取数据时从数据库中查询到商品数量为10,在未写入缓存中时另一个线程 B 更新数据。它更新数据库中的商品数量为20,并且清空缓存。这时线程 A 把从数据库中读到的商品数量10 的数据写入到缓存中。

最终,该商品数量在缓存中是 10(旧值),在数据库中是 20(新值),缓存和数据库数据不一致。

从理论上分析,先更新数据库,再删除缓存也是会出现数据不一致性的问题,但是在实际中,这个问题出现的概率并不高。

因为缓存的写入通常要远远快于数据库的写入,所以在实际中很难出现线程 B 已经更新了数据库并且删除了缓存,线程 A 才更新完缓存的情况。

而一旦线程 A 早于线程 B 删除缓存之前更新了缓存,那么接下来的请求就会因为缓存不命中而从数据库中重新读取数据,所以不会出现这种不一致的情况。

所以,「先更新数据库 + 再删除缓存」的方案,是可以保证数据一致性的。

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

在先删除缓存,更新数据库后,线程A更新完数据库值以后,让他睡眠一小段时间,再进行一次缓存删除操作。

加上睡眠时间,是为了让线程B能够先从数据库读取数据,再把缺失的数据写入缓存,然后,线程A再进行删除。线程A 睡眠的时间,就需要大于线程B读取数据再写入缓存的时间。这个时间是不好确定的,需要根据业务来估算。

其它线程读取数据时,会发现缓存缺失,所以会从数据库中读取最新值。因为这个方案会在第一次删除缓存值后,延迟一段时间再次进行删除,所以我们也把它叫做“延迟双删”。

没有一个组合是完美的,它们都有可能读到旧数据的可能,只不过概率不同,我建议更新缓存时,先更新数据库再删除缓存。

任何一种方案都不是完美的,但如果为了解决极小出现的概率要花好几倍的代价去解决(比如订阅 binlog 日志),从技术上来讲是得不偿失的,所以需要同业务方去协调一个适用的方法。

相关文章:

如何解决缓存和数据库的数据不一致问题

数据不一致问题是操作数据库和操作缓存值的过程中,其中一个操作失败的情况。实际上,即使这两个操作第一次执行时都没有失败,当有大量并发请求时,应用还是有可能读到不一致的数据。 如何更新缓存 更新缓存的步骤就两步&#xff0…...

linux系统下vscode portable版本的python环境搭建003:venv

这里写自定义目录标题 python安装方案一. 使用源码安装(有[构建工具](https://blog.csdn.net/ResumeProject/article/details/136095629)的情况下)方案二.使用系统包管理器 虚拟环境安装TESTCG 本文目的:希望在获得一个新的系统之后&#xff…...

使用TinyXML-2解析XML文件

一、XML介绍 当我们想要在不同的程序、系统或平台之间共享信息时,就需要一种统一的方式来组织和表示数据。XML(EXtensible Markup Language,即可扩展标记语言)是一种用于描述数据的标记语言,它让数据以一种结构化的方…...

Linux:docker在线仓库(docker hub 阿里云)基础操作

把镜像放到公网仓库,这样可以方便大家一起使用,当需要时直接在网上拉取镜像,并且你可以随时管理自己的镜像——删除添加或者修改。 1.docker hub仓库 2.阿里云加速 3.阿里云仓库 由于docker hub是国外的网站,国内的对数据的把控…...

C语言程序设计(第四版)—习题7程序设计题

目录 1.选择法排序。 2.求一批整数中出现最多的数字。 3.判断上三角矩阵。 4.求矩阵各行元素之和。 5.求鞍点。 6.统计大写辅音字母。 7.字符串替换。 8.字符串转换成十进制整数。 1.选择法排序。 输入一个正整数n(1<n≤10)&#xf…...

ZCC6982-同步升压充双节锂电池充电芯片

特性 ■高达 2A 的可调充电电流(受实际散热和输入功率限制) ■支持 8.4V、8.6V、8.7V、8.8V 的充满电压(限QFN) ■高达 28V 的输入耐压保护 ■高达 28V 的电池端耐压保护 ■宽输入工作电压范围:3.0V~6.5V ■峰值…...

定时器(基本定时器、通用定时器、高级定时器)

目录 一、基本定时器 二、通用定时器 三、高级定时器 一、基本定时器 1、作用:计时和计数。 二、通用定时器 1、除了有基本定时器的计时和计数功能外,主要有输入捕获和输出比较的功能,硬件主要由六大部分组成: ① 时钟源 ② 控…...

009集——磁盘详解——电脑数据如何存储在磁盘

很多人也知道数据能够保存是由于设备中有一个叫做「硬盘」的组件存在,但也有很多人不知道硬盘是怎样储存这些数据的。这里给大家讲讲其中的原理。 首先我们要明白的是,计算机中只有0和1,那么我们存入硬盘的数据,实际上也就是一堆0…...

鸿蒙开发-HarmonyOS UI架构

初步布局Index 当我们新建一个工程之后,首先会进入Index页。我们先简单的做一个文章列表的显示 class Article {title?: stringdesc?: stringlink?: string }Entry Component struct Index {State articles: Article[] []build() {Row() {Scroll() {Column() …...

Flutter 动画(显式动画、隐式动画、Hero动画、页面转场动画、交错动画)

前言 当前案例 Flutter SDK版本:3.13.2 显式动画 Tween({this.begin,this.end}) 两个构造参数,分别是 开始值 和 结束值,根据这两个值,提供了控制动画的方法,以下是常用的; controller.forward() : 向前…...

用HTML5 Canvas创造视觉盛宴——动态彩色线条效果

目录 一、程序代码 二、代码原理 三、运行效果 一、程序代码 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <!-- 声明文档类型为XHTML 1.0 Transitional -…...

云原生介绍与容器的基本概念

云原生介绍 1、云原生的定义 云原生为用户指定了一条低心智负担的、敏捷的、能够以可扩展、可复制的方式最大化地利用云的能力、发挥云的价值的最佳路径。 2、云原生思想两个理论 第一个理论基础是&#xff1a;不可变基础设施。 第二个理论基础是&#xff1a;云应用编排理…...

Flash存储

目录 一、MCU读写擦除Flash步骤 1、写flash步骤&#xff1a; 2、读flash步骤&#xff1a; 3、擦除flash步骤&#xff1a; 4、要注意的地方&#xff1a; 一、MCU读写擦除Flash步骤 1、写flash步骤&#xff1a; (1)解锁 2、读flash步骤&#xff1a; 3、擦除flash步骤&#x…...

Day 44 | 动态规划 完全背包、518. 零钱兑换 II 、 377. 组合总和 Ⅳ

完全背包 题目 文章讲解 视频讲解 完全背包和0-1背包的区别在于&#xff1a;物品是否可以重复使用 思路&#xff1a;对于完全背包问题&#xff0c;内层循环的遍历方式应该是从weight[i]开始一直遍历到V&#xff0c;而不是从V到weight[i]。这样可以确保每种物品可以被选择多次…...

使用PaddleNLP UIE模型提取上市公司PDF公告关键信息

项目地址&#xff1a;使用PaddleNLP UIE模型抽取PDF版上市公司公告 - 飞桨AI Studio星河社区 (baidu.com) 背景介绍 本项目将演示如何通过PDFPlumber库和PaddleNLP UIE模型&#xff0c;抽取公告中的相关信息。本次任务的PDF内容是破产清算的相关公告&#xff0c;目标是获取受理…...

软件工程师,OpenAI Sora驾到,快来围观

概述 近期&#xff0c;OpenAI在其官方网站上公布了Sora文生视频模型的详细信息&#xff0c;展示了其令人印象深刻的能力&#xff0c;包括根据文本输入快速生成长达一分钟的高清视频。Sora的强大之处在于其能够根据文本描述&#xff0c;生成长达60秒的视频&#xff0c;其中包含&…...

【Linux 04】编辑器 vim 详细介绍

文章目录 &#x1f308; Ⅰ 基本概念&#x1f308; Ⅱ 基本操作1. 进入 / 退出 vim2. vim 模式切换 &#x1f308; Ⅲ 命令模式1. 光标的移动2. 复制与粘贴3. 剪切与删除4. 撤销与恢复 &#x1f308; Ⅳ 底行模式1. 保存文件2. 查找字符3. 退出文件4. 替换内容5. 显示行号6. 外…...

KMP算法详解

1. 问题引入 链接&#xff1a;leetcode_28 题目&#xff1a;s1字符串是否包含s2字符串&#xff0c;如果包含返回s1中包含s2的最左开头位置&#xff0c;不包含返回-1 暴力方法就是s1的每个位置都做开头&#xff0c;然后去匹配s2整体&#xff0c;时间复杂度O(n*m) KMP算法可以…...

ubuntu22.04@laptop OpenCV Get Started: 013_contour_detection

ubuntu22.04laptop OpenCV Get Started: 013_contour_detection 1. 源由2. 应用Demo2.1 C应用Demo2.2 Python应用Demo 3. contour_approx应用3.1 读取图像并将其转换为灰度格式3.2 应用二进制阈值过滤算法3.3 查找对象轮廓3.4 绘制对象轮廓3.5 效果3.6 CHAIN_APPROX_SIMPLE v.s…...

[ai笔记5] 个人AI资讯助手实战

欢迎来到文思源想的ai空间&#xff0c;这是技术老兵重学ai以及成长思考的第5篇分享&#xff0c;也是把ai场景化应用的第一篇实操内容&#xff01; 既然要充分学习和了解ai&#xff0c;自然少不了要时常看看ai相关资讯&#xff0c;所以今天特地用字节的“扣子”做了一个ai的资讯…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

Java面试专项一-准备篇

一、企业简历筛选规则 一般企业的简历筛选流程&#xff1a;首先由HR先筛选一部分简历后&#xff0c;在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如&#xff1a;Boss直聘&#xff08;招聘方平台&#xff09; 直接按照条件进行筛选 例如&#xff1a…...

Angular微前端架构:Module Federation + ngx-build-plus (Webpack)

以下是一个完整的 Angular 微前端示例&#xff0c;其中使用的是 Module Federation 和 npx-build-plus 实现了主应用&#xff08;Shell&#xff09;与子应用&#xff08;Remote&#xff09;的集成。 &#x1f6e0;️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)

目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 ​编辑​编辑 UDP的特征 socke函数 bind函数 recvfrom函数&#xff08;接收函数&#xff09; sendto函数&#xff08;发送函数&#xff09; 五、网络编程之 UDP 用…...

软件工程 期末复习

瀑布模型&#xff1a;计划 螺旋模型&#xff1a;风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合&#xff1a;模块内部功能紧密 模块之间依赖程度小 高内聚&#xff1a;指的是一个模块内部的功能应该紧密相关。换句话说&#xff0c;一个模块应当只实现单一的功能…...

C++--string的模拟实现

一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现&#xff0c;其目的是加强对string的底层了解&#xff0c;以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量&#xff0c;…...

DeepSeek越强,Kimi越慌?

被DeepSeek吊打的Kimi&#xff0c;还有多少人在用&#xff1f; 去年&#xff0c;月之暗面创始人杨植麟别提有多风光了。90后清华学霸&#xff0c;国产大模型六小虎之一&#xff0c;手握十几亿美金的融资。旗下的AI助手Kimi烧钱如流水&#xff0c;单月光是投流就花费2个亿。 疯…...