【redis】redis缓存与数据库的一致性

news2024/10/3 2:15:01

【redis】redis缓存与数据库的一致性

  • 【1】四种同步策略
  • 【2】更新缓存还是删除缓存
    • (1)更新缓存
    • (2)删除缓存
  • 【3】先更新数据库还是先删除缓存
    • (1)出现失败时候的情况
      • 1-先删除缓存,再更新数据库(更新数据库失败了)
      • 2-先更新数据库,再删除缓存(删除缓存失败了)
      • 3-总结
    • (2)没有出现失败时候的情况
      • 1-先删除缓存,再更新数据库
        • 延时双删解决这个问题
        • 如果是读写分离的架构怎么办(强制读主库)
        • 删除失败了怎么办?
      • 2-先更新数据库,再删除缓存(最优方案)
        • 利用消息队列进行删除的补偿
  • 【4】总结

【1】四种同步策略

想要保证缓存与数据库的双写一致,一共有4种方式,即4种同步策略:
(1)先更新缓存,再更新数据库;
(2)先更新数据库,再更新缓存;
(3)先删除缓存,再更新数据库;
(4)先更新数据库,再删除缓存。

更新缓存与删除缓存哪种方式更合适?应该先操作数据库还是先操作缓存?

【2】更新缓存还是删除缓存

(1)更新缓存

(1)优点
每次数据变化都及时更新缓存,所以查询时不容易出现未命中的情况。

(2)缺点
更新缓存的消耗比较大。如果数据需要经过复杂的计算再写入缓存,那么频繁的更新缓存,就会影响服务器的性能。如果是写入数据频繁的业务场景,那么可能频繁的更新缓存时,却没有业务读取该数据。

(2)删除缓存

(1)优点
操作简单,无论更新操作是否复杂,都是将缓存中的数据直接删除。

(2)缺点
删除缓存后,下一次查询缓存会出现未命中,这时需要重新读取一次数据库。从上面的比较来看,一般情况下,删除缓存是更优的方案。

【3】先更新数据库还是先删除缓存

(1)出现失败时候的情况

首先,我们将先删除缓存与先更新数据库,在出现失败时进行一个对比:

1-先删除缓存,再更新数据库(更新数据库失败了)

先删除缓存再更新数据库,在出现失败时可能出现的问题:
(1)线程A删除缓存成功,线程A更新数据库失败;
(2)线程B从缓存中读取数据;由于缓存被删,进程B无法从缓存中得到数据,进而从数据库读取数据;此时数据库中的数据更新失败,线程B从数据库成功获取旧的数据,然后将数据更新到了缓存。
(3)最终,缓存和数据库的数据是一致的,但仍然是旧的数据

在这里插入图片描述

2-先更新数据库,再删除缓存(删除缓存失败了)

先更新数据库再删除缓存,在出现失败时可能出现的问题:
(1)线程A更新数据库成功,线程A删除缓存失败;
(2)线程B读取缓存成功,由于缓存删除失败,所以线程B读取到的是缓存中旧的数据。
(3)最后线程A删除缓存成功,有别的线程访问缓存同样的数据,与数据库中的数据是一样。
(4)最终,缓存和数据库的数据是一致的,但是会有一些线程读到旧的数据。

在这里插入图片描述

3-总结

经过上面的比较,我们发现在出现失败的时候,是无法明确分辨出先删缓存和先更新数据库哪个方式更好,以为它们都存在问题。上述场景出现的问题,应该如何解决呢?都建议采用重试机制解决。

(2)没有出现失败时候的情况

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

(1)线程A删除缓存成功;
(2)线程B读取缓存失败;
(3)线程B读取数据库成功,得到旧的数据;
(4)线程B将旧的数据成功地更新到了缓存;
(5)线程A将新的数据成功地更新到数据库。
在这里插入图片描述可见,进程A的两步操作均成功,但由于存在并发,在这两步之间,进程B访问了缓存。最终结果是,缓存中存储了旧的数据,而数据库中存储了新的数据,二者数据不一致。

延时双删解决这个问题

如果是先删缓存、再更新数据库,在没有出现失败时可能会导致数据的不一致。如果在实际的应用中,出于某些考虑我们需要选择这种方式,可以采用延时双删的策略,延时双删的基本思路如下:
(1)删除缓存;
(2)更新数据库;
(3)sleep N毫秒;
(4)再次删除缓存。

public void write(String key, Object data) {
    Redis.delKey(key);
    db.updateData(data);
    Thread.sleep(1000);
    Redis.delKey(key);
}

阻塞一段时间之后,再次删除缓存,就可以把这个过程中缓存中不一致的数据删除掉。而具体的时间,要评估你这项业务的大致时间,按照这个时间来设定即可。最终保证了数据库和缓存的数据一致

如果是读写分离的架构怎么办(强制读主库)

如果数据库采用的是读写分离的架构,那么又会出现新的问题,如下图:
此时来了两个请求,请求 A(更新操作) 和请求 B(查询操作)
(1)请求 A 更新操作,删除了 Redis;
(2)请求主库进⾏更新操作,主库与从库进行同步数据的操作;
(3)请 B 查询操作,发现 Redis 中没有数据;
(4)去从库中拿去数据;
(5)此时主从同步数据还未完成,拿到的数据是旧数据;

在这里插入图片描述
此时的解决办法就是如果是对 Redis 进行填充数据的查询数据库操作,那么就强制将其指向主库进⾏查询。

删除失败了怎么办?

如果删除依然失败,则可以增加重试的次数,但是这个次数要有限制,当超出一定的次数时,要采取报错、记日志、发邮件提醒等措施。

2-先更新数据库,再删除缓存(最优方案)

(1)线程A更新数据库成功;
(2)线程B读取缓存成功;
(3)线程A删除缓存成功。
在这里插入图片描述
可见,最终缓存与数据库的数据是一致的,并且都是最新的数据。但线程B在这个过程里读到了旧的数据,可能还有其他线程也像线程B一样,在这两步之间读到了缓存中旧的数据,但因为这两步的执行速度会比较快,所以影响不大。对于这两步之后,其他进程再读取缓存数据的时候,就不会出现类似于进程B的问题了。

利用消息队列进行删除的补偿

先更新数据库,后删除缓存这⼀种情况也会出现问题,比如更新数据库成功了,但是在删除缓存的阶段出错了没有删除成功,那么此时再读取缓存的时候每次都是错误的数据了。

此时解决方案就是利用消息队列进行删除的补偿。具体的业务逻辑⽤语⾔描述如下:
(1)请求 线程A 先对数据库进行更新操作;
(2)在对 Redis 进行删除操作的时候发现报错,删除失败;
(3)此时将Redis 的 key 作为消息体发送到消息队列中;
(4)系统接收到消息队列发送的消息后再次对 Redis 进行删除操作;

在这里插入图片描述但是这个方案会有⼀个缺点就是会对业务代码造成大量的侵入,深深的耦合在⼀起,所以这时会有⼀个优化的方法,我们知道对 Mysql 数据库更新操作后再 binlog 日志中我们都能够找到相应的操作,那么我们可以订阅 Mysql 数据库的 binlog 日志对缓存进行操作。

【4】总结

一般情况下,删除缓存是比更新缓存更优的方案;先更新数据库是比先删除缓存更优的方案;总的来说【先更新数据库,再删除缓存】就是四个策略中影响最小,效果最优的方案。

但是如果需要使用【先删除缓存,再更新数据库】的方案的话,可以使用【延时双删】【读写分离时强制读主库】【重试机制】来解决问题。

如果使用【先更新数据库,再删除缓存】时出现删除缓存失败的情况,可以使用【binlog同步到redis】来解决问题。

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

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

相关文章

【C++】string类(上)

文章目录1.为什么要学习string类2.标准库中的string类1.string分类2.string类对象的常见构造1.string3. string类对象的容量操作1.size2.capacity3.reserve4.resize扩容初始化删除数据4. string类对象的修改操作1.push_back2.append3.operator1.为什么要学习string类 c语言的字…

35岁测试工程师,面临中年危机,我该如何自救...

被辞的原因 最近因故来了上海,联系上了一位许久不见的老朋友,老王;老王和我是大学同学,毕业之后他去了上海,我来到广州。因为我们大学专业关系,从12年毕业以后我们从事着相同的职业,软件自动化…

在自己的电脑上使用ChatGPT做一个公网可访问的小应用

文章目录介绍ChatGPT的简单介绍和使用介绍从一个指令开始增加一些样例调整参数创建你自己的应用(python(Flask版本))安装配置环境将你的网站放到公网上总结介绍 本文将会简单介绍一下ChatGPT,并使用ChatGPT做一个简单…

Java字节码深度知多少?

文章目录1、字节码结构1.1、基本结构1.2、实际观测2、内存表示3、方法调用指令4、invokedynamicEND结语Java真的是长盛不衰,拥有顽强的生命力。其中,字节码机制功不可没。字节码,就像是 Linux 的 ELF。有了它,JVM直接摇身一变&…

计算机视觉方向地理空间遥感图像数据集汇总

文章目录1.DSTL卫星图像数据集/Kaggle竞赛2.Swimming Pool and Car Detection/Kaggle竞赛3.SpaceNet Challenge 3数据集4.RarePlanes数据集5.BigEarthNet数据集6.NWPU VHR-10数据集7.UC Merced Land-Use数据集8.Inria Aerial Image Labeling数据集9.RSOD数据集1.DSTL卫星图像数…

项目管理工具dhtmlxGantt甘特图入门教程(九):支持哪些数据格式(上篇)

dhtmlxGantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表,可满足项目管理控件应用程序的所有需求,是最完善的甘特图图表库这篇文章给大家讲解 dhtmlxGantt 的数据属性和数据库结构。 DhtmlxGantt正版试用下载(qun:764…

工厂模式(Factory Pattern)

1.什么是工厂模式 定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。 2.工厂模式的作用 实现创建者和调用者的分离 3.工厂模式的分类 简单工厂模式工厂方法模式抽象工厂模式 4.工厂模式的优缺点 优…

云原生 -- Docker进阶(Docker-compose,Docker网络简单介绍)

Dockerfile的构建过程 每条保留字段必须为大写字母。Dockerfile每行只支持一条指令,但是每条指令可以带多个参数,并且每条保留字指令后面至少要带有一个参数。从上到下依次执行。每条指令都会创建一个新的镜像层,并提交新的镜像。 大致流程…

CRM系统五大技巧集成Excel为销售流程赋能

销售过程中有很多情况会降低团队的效率。通过正确的实施CRM客户管理系统,可以帮助您的企业自动执行手动任务、减少错误并专注于完成交易。这里有5个技巧,可以帮助您的销售人员通过CRM集成Excel为销售流程赋能并提高他们的整体效率。 技巧1:将…

技术源自洛克希德·马丁,光场XR眼镜FYR解析

专注于医疗场景的一家XR眼镜厂商FYR(全称:FYR Medical)近期亮相,并宣布完成了260万美元A轮融资,本轮融资由NuVasive领投,资金将用于开发世界上第一个XR光场“放大镜”类产品。据青亭网了解,NuVa…

1、HAL库UART 中断|DMA 自动回显接收数据

1、实现代码: stm32f4xx_hal_conf.h文件开启UART宏定义 #define HAL_UART_MODULE_ENABLED添加stm32f4xx_hal_uart.c和stm32f4xx_hal_dma.c到自己工程; 编写好的代码:usart_Driver.c /***************************************************…

K8s管理应用生命周期-Deployment篇

在k8s中部署应用程序流程 1、使用Deployment部署Java应用 kubectl create deployment web --imageXXX/java-demokubectl get deployment,pods 2、使用Service发布Pod kubectl expose deployment web --port80 --typeNodePort --target-port8080 --namewebkubectl get servic…

深入【虚拟列表】动态高度、缓冲、异步加载... Vue实现

前言🎀 在前文中我们了解到: 1.在某种特殊场景下,我们需要将 大量数据 使用不分页的方式渲染到列表上,这种列表叫做长列表。 2.因为事件循环的机制,一次性大量的渲染耗时较长,并且渲染期间会阻塞页面交互…

Centos安装OpenResty

文章目录一. OpenResty是什么二. OpenResty的安装1. 安装开发库2. 安装OpenResty仓库3. 安装OpenResty4. 安装opm工具5. 目录结构6. 配置nginx的环境变量7. 启动和运行8. 配置文件修改三. 小案例1. 案例说明2. OpenResty监听请求3. 编写业务代码4. 获取请求参数一. OpenResty是…

深度解析React性能优化API

性能优化一直是前端领域讨论的一个热门问题,但在平时沟通及code review过程中发现很多人对于React中性能优化理解很模糊,讲不清楚组件什么时候更新,为什么会更新,关于React性能优化的文章虽然比较多,但大多数都是在罗列…

【C/C++】内存管理详解

目录内存布局思维导图1.C/C内存分布数据段:栈:代码段:堆:2.C语言中动态内存管理方式3.C内存管理方式3.1new/delete操作内置类型3.2new和delete操作自定义类型4.operator new 与 operator delete函数5.new和delete的实现原理5.1内置类型5.2自定…

ChatGPT is not all you need,一文看尽SOTA生成式AI模型:6大公司9大类别21个模型全回顾(二)

文章目录ChatGPT is not all you need,一文看尽SOTA生成式AI模型:6大公司9大类别21个模型全回顾(二)Image-to-Text 模型FlamingoVisualGPTText-to-Video 模型PhenakiSoundifyText-to-Audio 模型AudioLMJukeboxWhisperChatGPT is n…

protoc-gen-go的使用和问题

最近 在网上查看关于proto文件编译为golang代码的文章,发现遇到的问题好多都是文件目录不对,参数不对的情况,这里主要解决,使用 不同版本的proto-gen-go 参数不一样和找不到文件问题 安装protoc-gen-go google.golang.org仓库版本…

互联网新时代要来了(二)什么是AIGC?

什么是AIGC? 最近,又火了一个词“**AIGC”**2022年被称为是AIGC元年。那么我们敬请期待,AIGC为我们迎接人工智能的下一个时代。 TIPS:内容来自百度百科、知乎、腾讯、《AIGC白皮书》等网页 什么是AIGC?1.什么是AIGC?…

Vue3篇.01-简介及基本使用,项目创建方式, 模板语法, 事件监听, 修饰符

一.简介1.概念Vue 是一款用于构建用户界面的 JS框架, 基于标准 HTML、CSS 和 JavaScript 构建,并提供了一套声明式的、组件化的编程模型, 高效地开发用户界面。渐进式框架, 适应不同需求进行开发。两个核心功能:声明式…