如何保证Redis的缓存和数据库中的数据的一致性?

Redis的缓存如何和数据库中的数据保持一致性?

我们都知道,Redis是一个基于内存的键值存储系统,数据完全存放在内存中,这使得它的读写速度远超传统的硬盘存储数据库。对于高访问频率、低修改率的数据,通过将它们缓存在Redis中,应用可以快速地从内存中获取数据,能够大量减少数据库的查询次数,特别是在高并发场景下,有效避免了数据库可能成为整个系统的瓶颈问题。

🍭 但是在实际开发情景中,我们如何才能保证Redis缓存中的数据和数据库中数据的一致性呢?

什么情况会出现数据的不一致?

MySQL和Redis的操作包括读操作和写操作两种,那么有哪些情况是会导致二者数据不一致的?我们先从读和写的操作流程看起~

使用Redis读取数据的场景

在这里插入图片描述
🔆使用Redis读取数据的场景

  • 客户端发起一个查询数据的接口;
  • 向Redis中查询是否存在该条数据
    • 如果存在则直接返回;
    • 不存在则向数据库中查询,再将数据库中的数据保存在Redis中,然后设置一个过期时间,之后再返回客户端。

💡 设置过期时间的主要作用就是为了保证一些冷数据过期自动淘汰,不至于一直存在Redis中占用空间

使用Redis写数据的场景

🔆对数据进行修改是分为两个步骤:

  • 修改数据库数据
  • 修改/删除Redis缓存

为什么不推荐更新缓存而是直接删除?

删除逻辑非常简单,只需要在下一个线程的时候查询Redis,查询不到再从数据库中加载即可,其副作用只是增加了一次chache miss;而更新缓存的成本更高,因为我们写入数据库的值,很多情况下并不是直接写入缓存的,而是要经过一系列复杂的计算再写入缓存。那么每次写入数据库后,都要再次计算写入缓存的值,无疑是浪费性能,显然直接删除缓存更为合适,推荐直接删除。

🍭那么接下来的问题就是先操作缓存还是先操作数据库了

先操作缓存

在这里插入图片描述

🔆先操作缓存即先将缓存清空,再对数据库数据进行修改

  • 线程一进行删除Redis缓存和修改数据库数据操作,线程二进行查询数据操作
  • 线程一将Redis缓存清空之后发生了网络延迟,此时线程二进行缓存读取
  • 线程二在Redis缓存中查询不到数据,向数据库中查询数据并将查询到的数据加载到Redis缓存中,此时Redis缓存中存储的还是原先的老数据
  • 线程一完成数据的修改操作
  • 此时数据库中的数据是修改后的新数据,而Redis缓存中的数据还是更新前的旧数据,此时就发生了Redis缓存和数据库中的数据不一致的情况

💡 如果先操作Redis缓存的话,就可能出现将Redis中的数据删除之后旧数据又被放入其中去,如果后期再有查询操作,那么在Redis缓存中查询到的数据就会全部是旧的数据,产生了缓存脏读,直到数据到了过期时间被清除掉,如果没有设置过期时间该数据就永远都是脏数据,不能保证Redis和数据库的一致性

先操作数据库

那我们先操作数据库呢?还会有这种问题的发生吗?
在这里插入图片描述

🔆先进行数据库操作流程

  • 线程一进行删除Redis缓存和修改数据库数据操作,线程二进行查询数据操作
  • 线程一首先对数据库中的数据进行修改
  • 在此期间如果出现了另外一个线程来查询数据,就会直接从Redis中读取旧数据返回
  • 线程一操作完数据库后对Redis缓存进行延迟删除
  • 接下来的查询操作在Redis中查询不到数据,会向数据库中查询,并将查询到的数据放入Redis缓存中,此时Redis和数据库中的数据都是修改后的新数据

💡 先删数据库再删缓存,在多线程的情况下,当一个线程删除数据库,另一个线程读取缓存数据,读到的是未修改的Redis缓存中的旧数据,当先前一个线程删除完数据库后就会更新缓存,此时Redis和数据库中的数据就可以一致了,只会产生一次脏读

如何保证数据的一致性

🍭此时我们可以看到,在只读场景中是不会出现数据不一致的情况的,只有在并发的写操作中才会出现。问题已经很清楚了,那么如何解决呢?

强一致性和弱一致性

🔆首先我们先来了解两个概念:强一致性和弱一致性:

  • 强一致性:任何一次读都能读到某个数据的最近一次写的数据;
  • 弱一致性:数据更新后,如果能容忍后续的访问,只能访问到部分或全部访问不到;

延时双删策略

如果我们先操作Redis再操作数据库的话,就会出现我们上面所说到的脏读的问题,要解决这个问题,我们可以使用延迟双删的策略:在更新MySQL之后,等待一个短时间(例如几秒)再尝试第二次删除,防止在这段时间内有请求重建缓存而造成数据不一致。

基于消息队列

使用消息队列(如RabbitMQ、Kafka等)异步处理数据更新,更新MySQL的同时将更新事件发送到队列,消费者收到消息后负责更新Redis缓存。

订阅MySQL binlog同步

Redis可以订阅MySQL的binlog日志,拿MySQL举例,当一条数据发生变更时,MySQL就会产生一条变更日志,我们可以订阅这个日志,拿到具体操作的数据,然后再根据这条数据去删除对应的缓存。

删除重试机制

使用删除重试机制,保证删除缓存成功。比如重试三次,三次都失败则记录日志到数据库并发送警告让人工介入。在高并发的场景下,重试最好使用异步方式,比如发送消息到mq中间件,实现异步解耦。

🏮以上的这些方法最多只能实现弱一致性🏮,也就是不能保证更新后每次读取到的数据都是最新数据,如果我们想要保证每次查询到的数据都是新的数据的话,即保证数据的强一致性,那就必须要保证他们的操作是原子性的,那么只能加一把锁了。但是加锁的话是会影响到系统的吞吐量,而我们使用Redis就是为了提高性能,如果为了保证数据的强一致性而加一把锁,那么就得不偿失了。

总结

  • 数据不一致的情况在只读场景中是不会出现的,只有在并发的写操作中才会出现;
  • 相对于直接删除Redis缓存来讲,修改缓存的成本更高,因此更加推荐修改数据库后直接删除Redis缓存,在下一次查询操作时再向数据库中拿到数据保存在Redis中;
  • 先操作Redis的话为了保证和数据库数据的最终一致性,需要进行延时双删的操作,即对Redis缓存进行两次删除,相比于先操作MySQL来说多进行一次删除Redis的操作;
  • 无论是先操作数据库还是先操作缓存,都会出现脏读的情况,因为redis和数据库不是原子性的,要想保证其原子性那么只有加锁,但是加锁的话又会影响性能;
  • Redis删除失败要进行重删操作

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

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

相关文章

intellij idea 使用git ,快速合并冲突

可以选择左边的远程分支上的代码,也可以选择右边的代码,而中间是合并的结果。 一个快速合并冲突的小技巧: 如果冲突比较多,想要快速合并冲突。也可以直接点击上图中 Apply non-conflicting changes 旁边的 All 。 这样 Idea 就会…

深入浅出 -- 系统架构之垂直架构

当业务复杂度增加、访问量逐渐增大出现高并发时,单体架构无法满足需求,可以根据业务功能对系统进行拆分,以提高访问效率。 垂直架构介绍 1.垂直架构一般是因为单体架构太过于庞大而进行的拆分,拆分后各个系统应满足独立运行互相不…

wordpress外贸独立站模板

wordpress外贸独立站模板 WordPress Direct Trade 外贸网站模板,适合做跨境电商的外贸公司官方网站使用。 https://www.waimaoyes.com/wangzhan/22.html

【Android Studio】上位机-安卓系统手机-蓝牙调试助手

【Android Studio】上位机-安卓系统手机-蓝牙调试助手 文章目录 前言AS官网一、手机配置二、移植工程三、配置四、BUG五、Java语言总结 前言 提示:以下是本篇文章正文内容,下面案例可供参考 AS官网 AS官网 一、手机配置 Android Studio 下真机调试 …

unity学习(82)——profiler 限制帧率

实际测试发现当玩家个数增加时,客户端明显变的很卡,想知道为什么变卡了! 1.只有玩家自己的时候 2.两个时候感觉脚本的工作量增大了 拖了一会直接炸了!(数据包积压把内存搞炸,我第一次见) 3.我觉…

数据库的介绍分类作用特点

目录 1.概述 2.分类 2.1.关系型数据库 2.2.非关系型数据库 2.3.分布式数据库 ​​​​​​​2.4.云数据库 3.作用 4.特点 5.应用举例 5.1.MySQL ​​​​​​​5.1.1.作用 ​​​​​​​5.1.2.特点 ​​​​​​​5.1.3.应用案例 ​​​​​​​5.2.达梦 ​​​…

上位机图像处理和嵌入式模块部署(qmacvisual之tcp服务器端)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 上面一篇,我们谈到了tcp客户端,另外一种连接方法就是tcp服务器端。事实上,对于第三方系统,大多数情…

ES10 学习

文章目录 1. Object.fromEntries()2. trimStart() 和 trimEnd()3. 数组的flat() 和flatMap()4. Symbol 对象的description 属性5. try ... catch(e){} 1. Object.fromEntries() Object.fromEntries() 方法允许你轻松地将键 值对列表转换为对象 let arr [["name",&qu…

《搜广推算法指南》(2024版) 重磅发布!

节前,我们星球组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、参加社招和校招面试的同学,针对算法岗技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备、面试常考点分享等热门话题进行了深入的讨论。 结合…

城市道路井盖破损丢失目标检测数据集VOC-1377张

数据集格式:Pascal VOC格式(不包含分割路径的txt文件和yolo格式的txt文件,仅仅包含jpg图片和对应的xml) 图片数量(jpg文件个数):1377 标注数量(xml文件个数):1377 标注类别数:4 标注类别名称:["jg","jg…

(React组件基础)前端八股文修炼Day6

一 类组件与函数组件有什么异同 在React中,类组件和函数组件是创建组件的两种主要方式。随着React的发展,尤其是自Hooks在React 16.8中引入以来,函数组件的功能变得更加强大,使得它们能够更加方便地与类组件相竞争。下面是类组件…

Collection与数据结构 Stack与Queue(一): 栈与Stack

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

代码随想录算法训练营第四十四天 |卡码网52. 携带研究材料 、518. 零钱兑换 II、377. 组合总和 Ⅳ

代码随想录算法训练营第四十四天 |卡码网52. 携带研究材料 、518. 零钱兑换 II、377. 组合总和 Ⅳ 卡码网52. 携带研究材料题目解法 518. 零钱兑换 II题目解法 377. 组合总和 Ⅳ题目解法 感悟 卡码网52. 携带研究材料 题目 解法 题解链接 1. #include <iostream> #inc…

vscode开发ESP32问题记录

vscode 开发ESP32问题记录 1. 解决vscode中的波浪线警告 1. 解决vscode中的波浪线警告 参考链接&#xff1a;https://blog.csdn.net/fucingman/article/details/134404485 首先可以通过vscode 中的IDF插件生成模板工程&#xff0c;这样会自动创建.vscode文件夹中的一些json配…

【Week-Y4】修改yolov5s中C3模块的结构,common.py文件解读

修改C3模块的结构 一、commom.py文件解析二、修改代码&#xff0c;运行train.py训练 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 &#x1f4d5;本次任务&#xff1a;将yolov5s网络模型…

交换机特性解析

​1. 端口数量和类型: RJ-45端口: 最常见的端口类型,用于连接网线。 铜缆类型: 超五类、六类、七类等,影响传输速率和距离。 PoE功能: 支持为连接的设备供电,如IP电话、无线AP等。 光纤端口: 用于连接光纤,支持更长的传输距离和更高的速率。 光纤类型: 单模、多模等,影响传…

网易RAG问答知识库开源了,Star 6K!!

网易RAG问答知识库开源了&#xff0c;Star 6K&#xff01;&#xff01; RAG 问答知识库 QAnything 开源了QAnything 架构设计剖析整个架构的工作流程主要包含三个环节为什么需要两阶段检索&#xff1f;使用的基座大模型相关技术组件 QAnything 本地部署一键部署安装&#xff0c…

【CSS】基础选择器

目录 标签选择器 id选择器 类选择器 CSS的编写地点&#xff1a; 标签选择器 说明&#xff1a;标签选择器实际上就是HTML标签元素&#xff08;可以是任何HTML元素&#xff09;&#xff0c;用来改变一个指定标签的样式 示例&#xff1a; <style type"text/css"…

用vscode仿制小米官网

html内容: <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><link rel&quo…

深入Tauri开发——从环境搭建到项目构建

深入Tauri开发——从环境搭建到项目构建 开启你的Tauri桌面应用开发之旅&#xff08;续&#xff09; 经过上一篇文章的基础介绍&#xff0c;现在让我们更进一步&#xff0c;详细阐述如何在Windows和macOS平台上顺利搭建Tauri应用所需的开发环境&#xff0c;并指导您从创建项目…