【实践篇】Redis缓存和数据库一致性问题

Redis缓存和数据库一致性问题

文章目录

  • Redis缓存和数据库一致性问题
  • 0. 前言
    • 参考资料
  • 1. 缓存和数据库的数据不一致是如何发生的?
    • 1. 删除数据的情况:
    • 2. 修改数据的情况:

在这里插入图片描述

0. 前言

确保缓存和数据库之间的数据一致性是一项挑战,这需要考虑的因素包括更新策略、操作顺序以及是否引入消息队列等等。

  1. 更新缓存还是删缓存?
    一般来说,推荐的做法是删除缓存,而不是更新缓存。原因是更新缓存会引入更复杂的一致性问题。例如,如果在更新缓存之前,数据库的数据已经发生了变化,那么更新后的缓存数据将是过时的。另一方面,如果删除了缓存数据,那么下次请求时,如果缓存中没有数据,就会去数据库中查找,并将查找的结果存入缓存,这样可以确保缓存中的数据是最新的。

  2. 先更新数据库,再删除缓存,还是先删除缓存,再更新数据库?
    常见的推荐做法是先更新数据库,再删除缓存。原因是如果先删除缓存,那么在缓存删除后到数据库更新这段时间里,新的请求可能会将旧的数据重新加载到缓存中,导致数据不一致。

  3. 为什么要引入消息队列保证一致性?
    引入消息队列的目的是异步处理数据更新和缓存删除操作,减少这两个操作之间的时间差,从而降低数据不一致的风险。另外,消息队列还可以提供重试机制,以应对因为临时的故障导致的操作失败。

  4. 延迟双删会有什么问题?要不要用?
    延迟双删是指先删除缓存,再更新数据库,然后延迟一段时间后再次删除缓存。这种策略的目的是防止在更新数据库后新的请求将旧的数据加载到缓存中。但是,这种策略有一个问题,就是需要确定合适的延迟时间,如果延迟时间过短,可能无法阻止旧数据被加载到缓存中;如果延迟时间过长,可能会导致缓存的数据过期。因此,是否使用这种策略取决于具体的应用场景和需求。

题。

参考资料

  1. Redis官方文档:https://redis.io/
  2. Redis实战(书籍)
  3. Redis设计与实现(书籍)

1. 缓存和数据库的数据不一致是如何发生的?

首先,我们需要明确"数据的一致性"的具体含义。在这里,一致性包括两种情况:

当缓存中存在数据时,缓存的数值应与数据库中的值相同;
当缓存中没有数据时,数据库中的值必须是最新的。

不符合这两种情况的情况就属于缓存和数据库之间的数据不一致问题。

然而,缓存数据不一致的发生情况以及相应的应对方法会根据缓存的读写模式而异。根据是否接受写请求,我们可以将缓存分为读写缓存和只读缓存。

对于读写缓存而言,如果需要对数据进行增删改操作,就需要在缓存中进行,并根据所采取的写回策略决定是否同步写回数据库。同步直写策略要求在写入缓存时同时将数据同步写回数据库,以保持缓存和数据库中的数据一致。异步写回策略则表示在写入缓存时不立即同步写回数据库,而是等到数据从缓存中淘汰时再写回数据库。然而,如果在数据还未写回数据库时缓存发生故障,那么数据库将无法获取最新的数据。

因此,对于读写缓存来说,要保证缓存和数据库的数据一致性,需要采用同步直写策略。但需要注意的是,采用该策略时需要同时更新缓存和数据库。因此,在业务应用中需要使用事务机制,确保缓存和数据库的更新具有原子性,即要么同时更新,要么都不更新并返回错误信息以进行重试。否则,无法实现同步直写。

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

接下来,我们来讨论只读缓存。对于只读缓存,如果有数据新增,会直接写入数据库;而对于有数据修改或删除的情况,需要将只读缓存中的数据标记为无效。这样的处理方式会导致缓存缺失,当应用再次访问这些增删改的数据时,由于缓存中没有相应的数据,就会从数据库中读取数据并将其存入缓存。这样,后续再次访问数据时就可以直接从缓存中读取。

以java向MySQL中写入和修改数据为例,下图展示了数据的增删改操作具体过程:

java-> MySQLInsert          数据X直接在数据库中新增Update          数据X直接在数据库中修改Delete          数据X直接在数据库中删除如果执行的是修改或删除操作,还会删除缓存中的数据X。

通过图中可以看出,无论是新增、修改还是删除数据X,java上运行的应用都会直接在数据库中进行相应的操作。当然,如果应用执行的是修改或删除操作,还会从缓存中删除数据X。这个过程可以确保缓存和数据库的数据一致性。

1. 删除数据的情况:

当应用执行删除操作时,需要在数据库中删除相应的数据,并且还需要在缓存中删除对应的数据。如果删除数据库操作成功,但在删除缓存时发生错误,就会导致数据不一致的问题。此时,数据库中的数据已经被删除,而缓存中的数据仍然存在,从而导致缓存和数据库的数据不一致。

举个例子来说明这种情况:假设应用要删除数据X的值,首先成功地从数据库中删除了X,但在删除缓存中的X时遇到了错误。这种情况下,数据库中的X已经被删除,但缓存中仍然存在X的数据。如果此时另一个并发请求访问X,根据正常的缓存访问流程,它会首先在缓存中查询X,但由于缓存中仍然存在,它会读取到已经被删除的数据X,导致数据不一致。

2. 修改数据的情况:

在执行数据修改操作时,应用需要先更新数据库中的数据,然后再删除缓存中的数据。如果这两个操作无法保证原子性,就可能出现数据不一致的问题。假设应用先更新数据库,然后再删除缓存,如果在删除缓存时遇到错误,就会导致数据库中的数据是新值,而缓存中的数据是旧值,从而导致数据不一致。

举个例子来说明这种情况:应用要将数据X的值从10更新为3,首先成功地更新了数据库中的值,然后在删除缓存中的X时遇到错误。这种情况下,数据库中X的新值为3,但缓存中X的值仍然是旧值10,导致数据不一致。如果此时有其他并发请求来访问X,按照正常的缓存访问流程,它会先在缓存中查询,但由于缓存中仍然存在,它会读取到旧值10,进一步导致数据不一致。

当我们考虑缓存和数据库的一致性问题时,除了操作失败的情况,还需要考虑并发问题。并发问题可能会导致缓存和数据库之间的数据不一致。让我们来看一下这两种情况:

  1. 并发写: 如果有两个并发的写操作,一个写操作先更新数据库,然后更新缓存,另一个写操作在第一个写操作更新数据库后和更新缓存前更新了数据库和缓存,那么最后缓存中的数据将是第一个写操作的数据,而数据库中的数据是第二个写操作的数据,这就导致了数据不一致。

  2. 写后读: 如果一个写操作先更新了数据库,然后在更新缓存之前,一个读操作读取了数据库并将数据加载到了缓存,那么即使写操作后来更新了缓存,缓存中的数据也是旧的数据,这也会导致数据不一致。

为了解决这些问题,我们可以采用以下策略:

  1. 使用分布式锁: 我们可以在更新数据库和缓存时使用分布式锁,确保每次只有一个写操作可以执行。这样可以避免并发写的问题,但是会降低系统的并发性能。

  2. 先删除缓存,再更新数据库: 这是一种常见的策略,它可以避免写后读的问题。具体来说,我们先删除缓存,然后更新数据库。这样,如果在更新数据库后有一个读操作,由于缓存中没有数据,读操作会从数据库中读取最新的数据并加载到缓存中。

  3. 使用消息队列: 我们可以将更新缓存的操作放入消息队列中,确保更新数据库和更新缓存的操作按照正确的顺序执行。这样可以避免并发写和写后读的问题,但是会增加系统的复杂性。

以上只是一些基本的策略,具体的实现可能会根据业务需求和系统环境的不同而有所不同。总的来说,维护缓存和数据库的一致性需要综合考虑多种因素,包括但不限于如何更新数据、操作的顺序、是否引入消息队列以及如何处理并发等问题。

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

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

相关文章

2023年05月 C/C++(五级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C++编程(1~8级)全部真题・点这里 第1题:问题求解 给定一个正整数N,求最小的M满足比N大且M与N的二进制表示中有相同数目的1。 举个例子,假如给定N为78,二进制表示为1001110,包含4个1,那么最小的比N大的并且二进制表示中只包含4个1的数是83,其二进制是1010011,因此83…

长短期记忆网络(LSTM)

概念 三个门:遗忘门、输入门、输出门 候选记忆单元 记忆单元 隐状态 ot 控制是否让输出,是否要进行重置。 总结 代码实现 import torch from torch import nn from d2l import torch as d2lbatch_size,num_steps 32,35 train_iter,vocab d2l.load_…

【python爬虫】3.爬虫初体验(BeautifulSoup解析)

文章目录 前言BeautifulSoup是什么BeautifulSoup怎么用解析数据提取数据 对象的变化过程总结 前言 上一关,我们学习了HTML基础知识,知道了HTML是一种用来描述网页的语言,又了解了HTML的基本结构。 认识了HTML中的常见标签和常见属性&#x…

Java学习笔记31——字符流

字符流 字符流为什么出现字符流编码表字符串中的编码解码问题字符流写数据的5中方式字符流读数据的两种方式字符流复制Java文件 字符流 为什么出现字符流 汉字的存储如果是GBK编码占用2个字节,如果是UTF-8占用三个字节 用字节流复制文本文件时,文本文…

关于linux openssl的自签证书认证与nginx配置

自签文档链接 重点注意这块,不能写一样的,要是一样的话登录界面锁会报不安全 域名这块跟最后发布的一致 nginx配置的话 server {listen 443 ssl; //ssl 说明为https 默认端口为443server_name www.skyys.com; //跟openssl设置的域名保持一致s…

C# void 关键字学习

C#中void关键字是System.Void的别名; 可以将 void 用作方法(或本地函数)的返回类型来指定该方法不返回值; 如果C#方法中没有参数,则不能将void用作参数;这是与C语言不同的,C语言有…

Android之 Canvas绘制

一 Canvas介绍 1.1 Canvas 是绘制图形的重要类之一,它可以在 View 或 SurfaceView 上绘制各种图形和文本. 1.2 要创建 Canvas,首先需要有一个 View 或 SurfaceView 对象,在 View 或 SurfaceView 的绘制方法中,可以通过 Canvas 的…

基于Python+DenseNet121算法模型实现一个图像分类识别系统案例

目录 介绍在TensorFlow中的应用实战案例最后 一、介绍 DenseNet(Densely Connected Convolutional Networks)是一种卷积神经网络(CNN)架构,2017年由Gao Huang等人提出。该网络的核心思想是密集连接,即每…

Python 3.x标准数据类型

文章目录 一、数字1.1 基本的数字类型有:1.2 整数数字表示方式二、字符串2.1 操作符2.2 格式化百分号format 格式化输出位置匹配2.3 字符串 索引和切片2.4 切片:字符串 [ 起始索引:终止索引:步长 ]字符串连接操作三、列表3.1 列表的定义3.2 常用方法(功能:增删改查)增删除…

uniapp 项目实践总结(二)从零开始搭建一个项目

导语:本篇文章主要是项目方面的技术开发总结,新建一个项目可以选择使用可视化界面,也可以使用命令行搭建。 目录 可视化界面命令行搭建安卓开发环境苹果开发环境可视化界面 安装软件 使用官方推荐的 HbuilderX 软件,开发方式比较简单,内置相关环境以及终端,无需配置 no…

【STM32】锁存器

问题背景 在学习FSMC控制外部NOR存储器时,看到在NOR复用接口模式下,AD信号[15:0]是复用的。也就是说,若不使用锁存器:当NADV为低时,ADx(x0…15)上出现地址信号Ax,当NADV变高时,ADx上出现数据信号Dx。若使用…

【C语言】——调试技巧

目录 ​编辑 ①前言 1.什么是Bug? 2.什么是调试? 2.1调试的基本步骤 2.2Release与Debug 3.常用快捷键 4.如何写出好的代码 4.1常见的coding技巧 👉assert() 👉const() const修饰指针: ①前言 调试是每个程序员都…