如何保证数据库和缓存双写一致性?

数据库和缓存(redis)双写数据一致性问题再高并发的场景下,是一个很严重的问题,无论在工作中,还是面试,遇到的概率非常大,这里就聊一聊目前的常见解决方案以及最优方案。

常见方案

缓存的主要目的是为了提升查询的效率,下面是缓存使用的大概流程:

首先用户请求到达服务端,先查缓存有没有数据,如果能查到则直接返回

如果缓存中没数据,则去查询数据库

如果数据库中有数据,则将该数据放入缓存中,然后返回

如果数据库中没数据,则直接返回

乍一看,这种用法没什么问题,但是如果数据库中的某条数据,放入缓存后,立马被更新了,那么此时该如何更新缓存呢?

如果不更新缓存,那么在缓存的过期时间内,用户请求从缓存中获取的数据都是旧值,和数据库中的不一致,这就是个问题。

目前有如下几种缓存的更新方案:

  1. 先写缓存,再写数据库
  2. 先写数据库,再写缓存
  3. 先删缓存,再写数据库
  4. 先写数据库,再删缓存

接下来,针对这四种方案,详细分析一下。

先写缓存,再写数据库

这种方案有个不能接受的弊端:当某个请求的写操作访问时,刚写完缓存,突然网络出现异常,导致写数据库失败了

结果就是缓存更新了,而数据库没有,这样缓存中的数据就变成了脏数据。如果此时该用户的查询请求刚好读到了该数据,就会出现问题,因为数据在数据库中不存在。由于缓存的主要目的是为了把数据库中的临时数据保存在内存中,便于后续查询,但是如果某条数据在数据库中都不存在,那么缓存这条数据的意义就没有了。

所以这种方案是不可取的,实际工作用的不多。

先写数据库,再写缓存

用户的写操作,先写数据库,再写缓存,可以避免上面讲到的假数据问题,但是却带来了新的问题

写缓存失效了

如果把写数据库和写缓存操作放在同一个事务中,当写缓存失败,可以把写数据库操作回滚。并发量较小且对接口性能要求不高的系统可以这样操作,但是如果在高并发的场景下,为了防止出现大事务造成的死锁问题,通常写数据库操作和写缓存操作不要放在同一个事务中。也就是说在该方案中,如果写数据库成功了,但写缓存失败了,数据库中已写入的数据不会回滚。这样就会出现:数据库是新数据,缓存时旧数据,两边数据不一致的情况。

高并发下的问题

假设现在是高并发的场景,针对同一个用户的同一条数据,有两个写请求: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订阅者获取变更的数据,并删除缓存。

这种方案在业务接口中只需要关心数据库操作,但是重删缓存还是会失败,所以在这里就可以使用上述两个方案了。既不会对业务场景造成侵入,也使得功能有一定的健壮性。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java基础知识(四) -- 面向对象(上)

1.概述 Java语言是一种面向对象的程序设计语言,而面向对象思想(OOP)是一种程序设计思想,在面向对象思想的指引下,使用Java语言去设计、开发计算机程序。这里的对象泛指现实中一切事物,每种事物都具备自己的属性和行为。 面向对象思…

【数据结构与算法】Java描述:学数据结构与算法你需要预备的知识点!!!

这篇文章主要介绍 什么是数据结构,算法的时间复杂度,空间复杂度计算,包装类的装箱拆箱, 泛型语法,以及擦除机制。 目录 一、什么是数据结构 二、时间复杂度,空间复杂度 2.1 时间复杂度,空间…

传输层7——TCP拥塞控制(重点!!!)

目录 一、认识拥塞控制 1、什么叫做拥塞? 2、拥塞的特点 3、流量控制 VS 拥塞控制 二、TCP如何防止拥塞? 1、慢开始 2、拥塞避免 3、3重复确认 和 快重传算法 4、快恢复算法 5、总结 三、主动队列管理AQM 1、技术背景 2、AQM思 想和实现策略…

[64]最小路径和⭐

[64]最小路径和⭐ 题目描述 给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 **说明:**每次只能向下或者向右移动一步。 示例输入 示例 1: 输入:grid …

AI技术架构:从基础设施到应用

人工智能(AI)的发展,正以前所未有的速度重塑我们的世界。了解AI技术架构,不仅能帮助我们看懂 AI 的底层逻辑,还能掌握其对各行业变革的潜力与方向。 一、基础设施层:AI 技术的坚实地基 基础设施层是 AI 技…

每日计划-1213

1. 完成 SQL2 查询多列 https://www.nowcoder.com/exam/oj?page1tabSQL%E7%AF%87topicId199 2. 八股部分 1) C 中面向对象编程如何实现数据隐藏? 在c中,可以将数据成员声明为私有或受保护,并提供公有的成员函数来访问和修改这些数据成员&am…

【ADS射频电路设计教程】1. ADS基本操作

下面介绍ADS中主要仿真器的使用 1. 直流仿真 直流仿真器在控制面板的simulator-dc 直流仿真器 但是ADS自带有很多仿真器,可以直接来调用 选用晶体管电流扫描的模板 就可以输出模板 然后调入晶体管模型 然后要设置扫描的电压,选择dc仿真器对vds进行扫描…

EasyGBS点对点穿透P2P远程访问技术在安防视频监控中的应用

随着信息技术的快速发展,安防视频监控系统在公共安全领域的应用变得越来越广泛。传统的视频监控系统多依赖于中心服务器进行视频流的集中处理和分发,这不仅增加了网络带宽的负担,还可能成为系统性能瓶颈。为了解决这些问题,P2P&am…

CTFHub 命令注入-综合练习(学习记录)

综合过滤练习 命令分隔符的绕过姿势 ; %0a %0d & 那我们使用%0a试试,发现ls命令被成功执行 /?ip127.0.0.1%0als 发现一个名为flag_is_here的文件夹和index.php的文件,那么我们还是使用cd命令进入到文件夹下 http://challenge-438c1c1fb670566b.sa…

美团2024年秋招第一场笔试【前端移动端】

美团2024年秋招第一场笔试【前端&移动端】 2024/12/12 1.在一个长度为28的数组中删除第5个元素时(元素序号:1~28),需要向前移动(23)个元素。 2.如下图一个树型结构,其结点E在树的中序遍历…

2025周易算命网站搭建详细方法+源码选择php环境的配置

以下是一个详细的搭建教程,包括网站分类、环境配置、程序设计和功能实现。 1. 环境准备 1.1 服务器选择 操作系统: Linux(推荐使用Ubuntu或CentOS)Web服务器: Nginx数据库: MySQLPHP版本: 7.4.x(确保小于8.0) 1.2 安…

【ABAP SAP】开发-BUG修补记录_采购申请打印时品名规格品牌为空

项目场景: TCODE:自开发程序ZMMF004 采购申请打印 问题描述 ZMMF004打印的时候,有的采购申请的品名、规格、品牌为空 原因分析: 1、首先我通过写SQL语句查底表来看这几条采购申请本身有无品名、规格、品牌 SQL语句如下,只需修…

人员离岗监测摄像机智能人员睡岗、逃岗监测 Python 语言结合 OpenCV

在安全生产领域,人员的在岗状态直接关系到生产流程的顺利进行和工作环境的安全稳定。人员离岗监测摄像机的出现,为智能人员睡岗、逃岗监测提供了高效精准的解决方案,而其中的核心技术如AI识别睡岗脱岗以及相关的算法盒子和常见的安全生产AI算…

Linux之条件变量,信号量,生产者消费者模型

Linux之条件变量,信号量,生产消费者模型,日志以及线程池 一.条件变量1.1条件变量的概念1.2条件变量的接口 二.信号量2.1信号量的重新认识2.2信号量的接口 三.生产者消费者模型3.1生产者消费者模型的概念3.2基于阻塞队列的生产者消费者模型3.3…

Leecode刷题C语言之K次乘法运算后的最终数组①

执行结果:通过 执行用时和内存消耗如下: 代码如下: int* getFinalState(int* nums, int numsSize, int k, int multiplier, int* returnSize) {int *ret (int *)malloc(sizeof(int) * numsSize);memcpy(ret, nums, sizeof(int) * numsSize);while (k…

配置中心 选型 : Apollo Vs. Nacos Vs. spring cloud config

为什么我们需要一个微服务配置中心? 首先,我们可以想象下,如果没有配置中心,我们的项目可能是这样的:不同环境的配置文件都放在项目里面,部署时可以通过启动参数来指定使用哪个环境的配置。 这种方式有两…

LAVE——基于大语言模型的新型代理辅助视频编辑工具允许用户根据自己的编辑风格进行调整

概述 论文地址:https://arxiv.org/abs/2402.10294 视频是一种非常强大的交流和讲述故事的媒介。随着社交媒体和视频共享平台的出现,视频的受欢迎程度直线上升,许多人都在制作和分享自己的内容。然而,对于初学者来说,视…

【Vulkan入门】09-CreateFrameBuffer

目录 先叨叨git信息关键代码VulkanEnv::FindHostVisitbaleMemoryTypeIndex()TestPipeLine::CreateFramebuffers() 与网上大多数文章不同,其他文章基本上都使用窗口框架(X11、GLFW、WSL等)提供的surface来显示Vulkan渲染出的图像。我认为那样会…

题目 1688: 数据结构-字符串插入

第一种方式字符串 #include<iostream> #include<cstring> #include<algorithm> using namespace std; int main(){string s1,s2;int n;cin>>s1>>s2>>n;s1.insert(n-1,s2);cout<<s1<<endl;return 0; } 第二种方式字符数组 …

【KodExplorer】可道云KodExplorer-个人网盘安装使用

说明&#xff1a;安装kodExplorer &#xff08;不是Kodbox&#xff09;&#xff1b;Kodbox需求服务器至少2核4G内存&#xff0c;要求环境具备php/redis/mysql/。安装kodExplorer 就是比较方便简单部署&#xff0c;个人版免费。 一、安装环境需求 服务器: Windows&#xff0c;…