如何保证数据库和缓存双写一致性?
数据库和缓存(redis)双写数据一致性问题再高并发的场景下,是一个很严重的问题,无论在工作中,还是面试,遇到的概率非常大,这里就聊一聊目前的常见解决方案以及最优方案。
常见方案
缓存的主要目的是为了提升查询的效率,下面是缓存使用的大概流程:
首先用户请求到达服务端,先查缓存有没有数据,如果能查到则直接返回
如果缓存中没数据,则去查询数据库
如果数据库中有数据,则将该数据放入缓存中,然后返回
如果数据库中没数据,则直接返回
乍一看,这种用法没什么问题,但是如果数据库中的某条数据,放入缓存后,立马被更新了,那么此时该如何更新缓存呢?
如果不更新缓存,那么在缓存的过期时间内,用户请求从缓存中获取的数据都是旧值,和数据库中的不一致,这就是个问题。
目前有如下几种缓存的更新方案:
- 先写缓存,再写数据库
- 先写数据库,再写缓存
- 先删缓存,再写数据库
- 先写数据库,再删缓存
接下来,针对这四种方案,详细分析一下。
先写缓存,再写数据库
这种方案有个不能接受的弊端:当某个请求的写操作访问时,刚写完缓存,突然网络出现异常,导致写数据库失败了
结果就是缓存更新了,而数据库没有,这样缓存中的数据就变成了脏数据。如果此时该用户的查询请求刚好读到了该数据,就会出现问题,因为数据在数据库中不存在。由于缓存的主要目的是为了把数据库中的临时数据保存在内存中,便于后续查询,但是如果某条数据在数据库中都不存在,那么缓存这条数据的意义就没有了。
所以这种方案是不可取的,实际工作用的不多。
先写数据库,再写缓存
用户的写操作,先写数据库,再写缓存,可以避免上面讲到的假数据问题,但是却带来了新的问题
写缓存失效了
如果把写数据库和写缓存操作放在同一个事务中,当写缓存失败,可以把写数据库操作回滚。并发量较小且对接口性能要求不高的系统可以这样操作,但是如果在高并发的场景下,为了防止出现大事务造成的死锁问题,通常写数据库操作和写缓存操作不要放在同一个事务中。也就是说在该方案中,如果写数据库成功了,但写缓存失败了,数据库中已写入的数据不会回滚。这样就会出现:数据库是新数据,缓存时旧数据,两边数据不一致的情况。
高并发下的问题
假设现在是高并发的场景,针对同一个用户的同一条数据,有两个写请求:a和b,它们同时请求服务器。其中请求a获取的是旧数据,而请求b获取的是新数据:
请求a先到,刚写完数据库,由于网络问题,卡了一下,还没来得及写缓存,此时请求b到了,先写了数据库,然后写了缓存,此时a卡顿结束,也写了缓存。
在这个过程中,请求b在缓存中的新数据,被请求a给覆盖了,且数据库存的是请求b的值,而缓存时请求a的值,两边又不一致了。
浪费系统资源
每个写操作都是写完数据库接着写缓存,如果写的缓存并不是简单的数据内容,而是要经过非常复杂的计算得出的最终结果,这样每写一次缓存都要经过一次非常复杂的计算,就很浪费系统资源。
还有一些特殊业务场景,写多读少。这类场景中,每个写操作都要写一遍缓存,有点得不偿失。
先删缓存,再写数据库
通过上面的内容可知,直接更新缓存的问题有很多。那么换一种思路,不去更新缓存,而是直接删除呢?
高并发下的问题
假设在高并发的场景中,同一个用户的同一条数据,有一个读数据请求c,还有另一个写数据请求d,同时请求到业务系统,如图所示:
请求d先到,把缓存删除了,由于网络原因,卡了一下,还没来得及写数据库,这时请求c也到了,先查缓存发现没数据,再查数据库,有数据,但是旧值。请求c将数据更新到缓存中,此时,请求d卡顿结束,把新值写入了数据库中。
在上面的过程中,请求d的新值并没有被请求c写入缓存,同样会导致缓存和数据库的数据都不一致。那么针对此类情况该如何处理?
缓存双删
在写数据库之前删除缓存一次,写完数据库后再删一次。该方案有个关键的地方是,第二次删除时需要间隔一段时间。那么流程就变成了:请求d先到,把缓存删了,但是卡住了,然后请求c到了,把数据库中的值更新到了缓存中,然后请求d卡顿结束,把新值写到了数据库,一段时间后,请求d将缓存中的数据删除。
为什么要隔一段时间呢?
如果请求d立即删除缓存,而请求c还没来得及将旧值更新到缓存中,就会导致删除没有意义。
先写数据库,再删缓存
依旧是高并发的场景,有一个读请求f和一个写请求e,更新过程:请求e先写数据库,但是卡住了,没来得及删,请求f查询缓存,然后返回。请求e删除缓存。这样看,没啥问题,那如果请求f先到呢?
请求f查询缓存,直接返回,请求e写数据库,再写缓存。这样看也没啥问题。
有一种特殊情况:缓存恰好失效。
缓存过期时间到了,自动失效。请求f查询缓存,没查到数据,于是去查数据库,更新缓存时卡住了,请求e写数据库,接着删除了缓存,此时请求f卡顿结束,更新了缓存。这样又出现两边不一致的情况了。
不过这种场景较少,且条件苛刻,相较于其他方案,该方案的业务影响是最小的。
缓存删除失败怎么办?
先写数据库再删缓存和缓存双删方案一样,都有个共同的风险点:缓存删除失败了,也会导致两边数据不一致。
所以需要添加重试机制。
在接口中如果更新数据库成功了,但是缓存更新失败了,可以立刻重试几次,如果其中有任何一次成功,那么就返回成功,如果都失败了,需要记录下来,后续处理。
下面介绍一下重试方案。
定时任务
当删除缓存失败后,将数据写入重试表中,使用定时任务异步读取表中的用户数据,重试表需要记录一个重试次数字段,当重试次数到达阈值时,需要记录重试失败,后续进一步处理。
这种方式有个缺点,就是实时性不高,对于实时性要求较高的业务场景,该方案并不适用。
消息队列
在高并发的场景中,消息队列是不可或缺的中间件之一。
Mq的生产者,生产了消息之后,通过指定的topic发送到了mq服务器,然后mq的消费者订阅该topic,读取消息数据之后,做业务逻辑的处理。
那么添加mq的处理方案为:
当用户操作写完数据库后,删除缓存失败了,产生一条mq消息,发送给mq服务器,消费者读取到这条消息后,开始重试,任意一次成功就返回成功,否则将该消息添加到死信队列中。
Mysql的binlog
无论是mq还是定时任务,对业务都有一定的侵入性,定时任务的方案中需要在业务代码中增加额外逻辑,mq的方案中需要在业务代码中发送mq消息,除了这两个方案以外,我们还可以使用canal中间件监听binlog日志。
在业务接口中写数据库之后,就直接返回成功,MySQL会将变更的数据写到binlog中,然后binlog订阅者获取变更的数据,并删除缓存。
这种方案在业务接口中只需要关心数据库操作,但是重删缓存还是会失败,所以在这里就可以使用上述两个方案了。既不会对业务场景造成侵入,也使得功能有一定的健壮性。
相关文章:
如何保证数据库和缓存双写一致性?
数据库和缓存(redis)双写数据一致性问题再高并发的场景下,是一个很严重的问题,无论在工作中,还是面试,遇到的概率非常大,这里就聊一聊目前的常见解决方案以及最优方案。 常见方案 缓存的主要目…...
QT 多级嵌套结构体,遍历成员--半自动。<模板+宏定义>QTreeWidget树结构显示
Qt的QTreeWidget来显示嵌套结构体的成员,并以树形结构展示。 #include <QApplication> #include <QTreeWidget> #include <QTreeWidgetItem> #include <QString> #include <cstdint>// 假设这些是你的结构体定义 struct BaseMeterPa…...
《深入浅出HTTPS》读书笔记(17):公开密钥算法
公开密钥算法(Public Key Cryptography),也称为非对称加密算法(Asymmetrical Cryptography)。 公开密钥算法的功能比较多,可以进行加密解密、密钥协商、数字签名。 【密钥是一对】 公开密钥算法的密钥是一对…...
React 中为什么不直接使用 requestIdleCallback?
首先看下 requestIdleCallback是什么? 简介 requestIdleCallback 是一个在浏览器空闲时执行低优先级任务的 API。 定义与用途 requestIdleCallback 方法允许开发者在浏览器的空闲时段内调度函数的执行。这些函数通常用于执行非关键性的、低优先级的任务,…...
工作:SolidWorks从3D文件导出2D的DWG或DXF类型文件方法
工作:SolidWorks从3D文件导出2D的DWG或DXF类型文件方法 SolidWorks从3D文件导出2D的DWG或2D DXF类型文件方法(一)打开3D文件(二)从装配体到工程图(三)拖出想要的角度的图型(四&#…...
element-ui radio和checkbox禁用时不置灰还是原来不禁用时的样式
把要紧用的内容加上一个class"notEdit-page" z注意要在style里面写不能加上scoped /*//checkBox自定义禁用样式*//*//checkBox自定义禁用样式*/ .notEdit-page.el-checkbox__input.is-disabled.is-checked.el-checkbox__inner::after {border-color: #fff; } .notEdi…...
MySQL 8.0 安装与配置技术文档(Ubuntu22.04)
MySQL 8.0 安装与配置技术文档 目录 环境准备下载 MySQL 安装包检查是否已安装 MySQL彻底卸载 MySQL安装 MySQL配置 MySQL创建用户并允许外网访问修改 root 用户密码参考链接 1. 环境准备 确保系统为 Ubuntu 22.04,并安装了以下基础工具: sudo apt-ge…...
【Linux】Ubuntu中安装多个版本的gcc、g++编译器,并自由切换
1、安装 1.1 命令安装 使用命令直接安装: sudo apt install gcc-[版本号] sudo apt install g++-[版本号]例如: sudo apt install gcc-10 sudo apt install g++-10 sudo apt install gcc-9 sudo apt install g++-9 sudo apt install gcc-8 sudo apt install g++-81.2 源码…...
uni-app登录界面样式
非常简洁的登录、注册界面模板,使用uni-app编写,直接复制粘贴即可,无任何引用,全部公开。 废话不多说,代码如下: login.vue文件 <template><view class"screen"><view class"…...
如何在小米平板5上运行 deepin 23 ?
deepin 23 加入了 ARM64 支持,这里尝试将 deepin 系统刷入平板中,平常使用中,带个笔记本电脑有时候也会嫌比较麻烦,把 Linux 系统刷入平板中既满足了使用需要,又满足了轻便的需求。为什么不使用 Termux ?虽…...
Linux上的C语言编程实践
说明: 这是个人对该在Linux平台上的C语言学习网站笨办法学C上的每一个练习章节附加题的解析和回答 ex1: 在你的文本编辑器中打开ex1文件,随机修改或删除一部分,之后运行它看看发生了什么。 vim ex1.c打开 ex1.c 文件。假如我们删除 return 0…...
ubuntu中使用ffmpeg库进行api调用开发
一般情况下,熟悉了ffmpeg的命令行操作,把他当成一个工具来进行编解码啥的问题不大,不过如果要把功能集成进自己的软件中,还是要调用ffmpeg的api才行。 ffmpeg的源码和外带的模块有点太多了,直接用官网别人编译好的库就…...
基于yolov8的SAR影像目标检测系统,支持图像、视频和摄像实时检测【pytorch框架、python源码】
更多目标检测、图像分类识别、目标追踪等项目可看我主页其他文章 功能演示: 基于yolov8的SAR影像目标检测系统,支持图像、视频和摄像实时检测【pytorch框架、python源码】_哔哩哔哩_bilibili (一)简介 基于yolov8的SAR影像目标…...
【源码】Sharding-JDBC源码分析之SQL中读写分离动态策略、数据库发现规则及DatabaseDiscoverySQLRouter路由的原理
Sharding-JDBC系列 1、Sharding-JDBC分库分表的基本使用 2、Sharding-JDBC分库分表之SpringBoot分片策略 3、Sharding-JDBC分库分表之SpringBoot主从配置 4、SpringBoot集成Sharding-JDBC-5.3.0分库分表 5、SpringBoot集成Sharding-JDBC-5.3.0实现按月动态建表分表 6、【…...
服务器卸载安装的 Node.js
卸载安装的 Node.js 版本,具体步骤取决于你是通过包管理器(如 yum 或 dnf)安装的,还是通过 nvm (Node Version Manager) 安装的。以下是针对这两种情况的指南。 通过包管理器卸载 Node.js 如果你是通过 yum 或 dnf 安装的 Node.…...
一键图片提取表格导出为Excel文档工具体验
在日常工作中,我们经常会遇到需要将图片中的表格数据转换为可编辑的Excel文件的情况。这不仅能够提高工作效率,还能减少手动输入数据的错误。本文将介绍一款实用的工具,它能够帮助我们快速实现图片到Excel的转换,同时保持操作的简…...
SpringBoot异常处理
SpringBoot异常处理 一、认识异常 异常分类: Error: 代表编译和系统错误,不允许捕获Exception: 标准Java库的方法所激发的异常,包含运行异常Runtime_Exception和非运行异常 Non_RuntimeException 的子类Runtime_Exception: 运行时异常。No…...
在 OAuth 2.0 中,refreshToken(刷新令牌)存在的意义
在 OAuth 2.0 中,refreshToken(刷新令牌) 的主要目的是为了提升用户体验和安全性,同时确保访问令牌的有效性。以下是需要使用 refreshToken 的原因: 1. 访问令牌的有限生命周期 访问令牌(accessToken&…...
【Redis】壹 —— Redis 介绍
文章目录: 前言 一、认识Redis 1. Redis 用途 作为数据库 作为流引擎 二、服务端高并发分布式结构演变 1. 单机架构 2. 应用数据分离架构 3. 应用服务集群架构 4. 读写分离 / 主从分离架构 5. 冷热分离 —— 引入缓存 6. 分库分表 7. 微服务架构 8. …...
【html网页页面010】html+css制作茶品牌文创网页制作含视频元素(7页面附效果及源码)
茶主题品牌文创网页制作 🥤1、写在前面🍧2、涉及知识🌳3、网页效果完整效果(7页):代码目录结构:page1、主页page2、精品包装page3、茶园一角page4、品牌地带page5、衍生品page6、联X我们page7、视频详情页 ἰ…...
【项目实战】基于python+爬虫的电影数据分析及可视化系统
注意:该项目只展示部分功能,如需了解,文末咨询即可。 本文目录 1.开发环境2 系统设计 2.1 设计背景2.2 设计内容 3 系统页面展示 3.1 用户页面3.2 后台页面3.3 功能展示视频 4 更多推荐5 部分功能代码 5.1 爬虫代码5.2 电影信息代码 1.开发环…...
K8S命令部署后端(流水线全自动化部署)
前言 本文为链接: 云效流水线k8s半自动部署java(保姆级)的补充,本文起初的目的是为了补充完善k8s流水线的全自动化部署,但是也适用于k8s的一键重启,因为使用k8s的web页面容易出现漏点的情况,因此也可以把代码保存为shell脚本,同样可以实现一键重启。关于…...
GPS北斗卫星授时服务器功能是什么?应用是什么?
GPS北斗卫星授时服务器功能是什么?应用是什么? GPS北斗卫星授时服务器功能是什么?应用是什么? 摘 要:首先对计算机网络时间同步相关技术进行了介绍,然后阐述了时间同步技术在现代计算机网络中的应用与发展,最后指出时间同步网络…...
学习笔记064——如何手动将jar包导入到maven本地库
文章目录 1、背景:2、方法 1、背景: 有时网络慢的情况, 本地maven库需要导入外部下载的jar包。 以便于在项目的pom文件中,直接写dependency写导入依赖。 2、方法 在Windows终端中,输入: mvn install:in…...
未来趋势系列 篇二:HBM题材解析和股票梳理
文章目录 系列文章HBM题材解析环氧塑封电镀液PSPI(光敏性聚酰亚胺)前驱体封装基板其他材料TSV技术封装测试股票梳理系列文章 未来趋势系列 篇一:AI题材解析和股票梳理 HBM HBM(High Bandwidth Memory,高带宽内存)是一种专为高效能运算设计的新兴高速内存接口技术。它通…...
网卡驱动测试
以下是网卡驱动不同测试类型的具体方法和命令: 1. 功能性测试 驱动加载/卸载测试: 方法:加载/卸载网卡驱动,观察日志是否报错。命令: modprobe <driver_name> # 加载驱动 rmmod <driver_name> # 卸载驱动…...
DDR的跨4K问题
参考视频:【深入理解FPGA底层逻辑】、4k边界和outsdanding_哔哩哔哩_bilibili 1、AXI4_FULL突发写一个字节是一个地址, 2、协议规定,把AXI4从机的地址区间从0进行到了4095....每4K进行一次分配 所以突发长度的计算如下: 另外AX…...
数据结构---栈(Stack)
1. 简介 栈(Stack)是计算机科学中的一种抽象数据类型,它遵循特定的操作顺序,即后进先出(Last In First Out,LIFO)。这意味着最后添加到栈中的元素将是第一个被移除的。栈的基本操作通常包括&am…...
【JavaWeb后端学习笔记】Java上传文件到阿里云对象存储服务
阿里云对象存储 1、创建阿里云对象存储节点2、上传文件2.1 修改项目配置文件2.2 定义一个Properties类获取配置信息2.3 准备一个alioss工具类2.4 创建注册类,将AliOssUtil 注册成Bean2.5 使用AliOssUtil 工具类上传文件2.6 注意事项 使用阿里云对象存储服务分为以下…...
Unity3D RPG战斗系统详解
前言 设计一个RPG(角色扮演游戏)的战斗系统是游戏开发中的关键环节,它决定了游戏的乐趣和挑战性。在Unity3D中,可以通过多种技术和工具来实现一个功能完善的战斗系统。以下是对RPG战斗系统的技术详解以及代码实现。 对惹&#x…...
Spark架构及运行流程
Spark架构图 Driver: 解析用户的应用程序代码,转化为作业(job)。创建SparkContext上下文对象,其负责与资源管理器(ClusterManager)通信,进行资源的申请、任务的分配和监控等。跟踪Executor的执行情况。可通过UI界面查询运行情况。…...
SpringBoot3整合MyBatis
一、MyBatis整合步骤: (1).导入依赖:在Spring Boot项目的构建文件(如pom.xml)中添加MyBatis和数据库驱动的相关依赖。例如,如果使用MySQL数据库,您需要添加MyBatis和MySQL驱动的依赖。 (2).配置数据源:在application.properties或application.yml中配置…...
【计网笔记】习题
物理层 不属于物理层接口规范定义范畴的是(C) A. 接口形状 B. 引脚功能 C. 物理地址 D. 信号电平 【2023-912】光网络只能通过导向型介质传播。() 【2017-408】若信道在无噪声情况下的极限数据传输速率不小于信噪比为30dB条件下的…...
力扣56.合并区间
题目描述 题目链接56. 合并区间 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间,并返回 一个不重叠的区间数组,该数组需恰好覆盖输入中的所有区间 。 示例 1: …...
【oracle】大数据删除插入
文章目录 引言本文目标 Oracle大数据插入操作插入操作的场景和需求使用并行查询进行数据插入示例代码:创建新表并插入数据解释代码中的关键点 性能优化建议 Oracle大数据删除操作删除操作的场景和需求使用游标和批量处理进行数据删除示例代码:批量删除数…...
mysql 双1设置
MySQL 的"双1"设置通常指的是两个配置参数:innodb_flush_log_at_trx_commit 和 sync_binlog。这两个参数都与 MySQL 的数据安全和性能有关。 innodb_flush_log_at_trx_commit:这个参数控制了 InnoDB 引擎中事务日志的刷新频率。它有三个可能的…...
《C++ 赋能 K-Means 聚类算法:开启智能数据分类之旅》
在当今数字化浪潮汹涌澎湃的时代,人工智能无疑是引领科技变革的核心驱动力之一。而在人工智能的广袤天地中,数据分类与聚类作为挖掘数据内在价值、揭示数据潜在规律的关键技术手段,正发挥着前所未有的重要作用。K-Means 聚类算法,…...
用Python开发一个经典贪吃蛇小游戏
Python 是开发小游戏的绝佳工具,借助第三方库,如 pygame,我们可以快速开发一个经典的贪吃蛇游戏。本篇将介绍如何用 Python 实现一个完整的贪吃蛇小游戏。 一、游戏设计 1.1 游戏规则 玩家通过方向键控制贪吃蛇移动。贪吃蛇吃到食物后会变长,同时得分增加。如果贪吃蛇撞到…...
《大宋豪侠传》客户端源码 + 服务端源码 + 工具源码 + 资源,大小16.3G
《大宋豪侠传》客户端源码 服务端源码 工具源码 资源,大小16.3G 下载地址: 通过网盘分享的文件:【源码】《大宋豪侠传》客户端源码 服务端源码 工具源码 资源,大小16.3G 链接: https://pan.baidu.com/s/1lUf84LzXKB3iM7L-1P…...
使用vue-seamless-scroll实现echarts图表大屏滚动,出现空白间隔的解决方案
一、背景介绍 最近的业务开发需求,想要实现echarts图表大屏滚动,小编首先采用vue-seamless-scroll进行实现,结果发现第二屏出现空白间隔,尝试了多种解决方案均不生效,最终选择换一个方案。 二、封装的ScrollList组件…...
zsh配置
zsh配置 https://zhuanlan.zhihu.com/p/58073103 $ cat .zshrc If you come from bash you might have to change your $PATH. export PATH H O M E / b i n : / u s r / l o c a l / b i n : HOME/bin:/usr/local/bin: HOME/bin:/usr/local/bin:PATH Path to your oh-my-zs…...
Brain.js(八):RNNTimeStep 实战教程 - 股票价格预测 - 实操需警慎
前置声明,个人浅度炒股,但计划将基金转入股市。然后 股市有风险,不是技术可以完全预测的,但是在无头绪的时候,用技术指标做个参考也不错。 本文涉及到的股票预测,只是代码简单示例,实操需警慎&a…...
Python+requests实现接口自动化测试
🍅 点击文末小卡片,免费获取软件测试全套资料,资料在手,涨薪更快 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传…...
java------------常用API preiod duration 计算时间差
1,preiod 如果末天数比初天数小,需要进一位 package API;import java.time.LocalDate; import java.time.Period;public class preiod {public static void main(String[] args) {// 计算时间差// LocalDate获取对象其中的一个方法LocalDate d1 LocalD…...
Android水波纹效果
Android水波纹效果 需要到水波纹效果的场景还蛮少的。万一刚好你需要呢 一、思路: 自定义组件SpreadView 二、效果图: 看视频更直观点: Android轮子分享-水波纹效果 三、关键代码: public class SpreadView extends View {pr…...
yolov8 转华为昇腾om脚本
目录 yolov8 转华为昇腾 om脚本 测试ok 推理demo: yolov8 转华为昇腾 om脚本 测试ok import sys import osos.chdir(os.path.dirname(os.path.abspath(__file__)))import torchcurrent_dir = os.path.dirname(os.path.abspath(__file__))paths = [os.path.abspath(__file__)…...
【人工智能】从基础到实践:用Python和PyTorch实现深度学习图像分割模型
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 图像分割是计算机视觉中的核心任务之一,旨在将图像划分为具有语义意义的区域,在自动驾驶、医疗影像分析等领域有广泛应用。本篇文章将从图像分割的基础知识出发,详细讲解分割任务的目标、评价指标以及常…...
AI绘画设计实战-Day2
Stable Diffusion 提示词前缀 FF,(masterpiece:1.2),best quality,highres,extremely detailed CG,perfect lighting,8k wallpaper,anime,comic,game CG, FF,(杰作:1.2),最高质量,高分辨率,极其…...
详解LeetCode地下城游戏(动态规划)——区分两种状态表示形式
地下城游戏 题目链接:174. 地下城游戏 状态表示: 按照以往题的表示,dp[i][j]表示:从起点(0,0)位置到达(i,j)位置时,所需的最小初始健康值。但是…...
CV(3)--噪声滤波和特征
前言 仅记录学习过程,有问题欢迎讨论 图像噪声(需要主动干扰的场景): 添加高斯噪声:概率密度函数服从高斯分布的一类噪声 通过设置sigma和mean生成符合高斯分布的随机数,然后计算输出像素,放缩…...