Redis之缓存和数据库双写一致方案讨论解读

目录

什么是缓存双写一致

更新缓存还是删除缓存?

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

场景描述

解决方案:延时双删策略

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

场景描述

解决方案:重试机制+引入MQ

为什么要引入MQ


什么是缓存双写一致

只要用缓存,就可能会涉及到缓存与数据库双存储双写,你只要是双写,就一定会有数据一致性的问题。我们需要保证redis跟数据库的中的数据保持一致,返回正确的数据。

更新缓存还是删除缓存?

删除缓存,而不是更新缓存

 如果更新缓存,在并发写时,可能出现数据不一致。

假设现在同时有请求A和请求B进行更新操作,那么会出现

  • (1)线程A更新了数据库
  • (2)线程B更新了数据库
  • (3)线程B更新了缓存
  • (4)线程A更新了缓存

这就出现请求A更新缓存应该比请求B更新缓存早才对,但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此不考虑。

如果因为每次数据发生变更,都「无脑」更新缓存,但是缓存中的数据不一定会被「马上读取」,这就会导致缓存中可能存放了很多不常访问的数据,浪费缓存资源。

而且很多情况下,写到缓存中的值,并不是与数据库中的值一一对应的,很有可能是先查询数据库,再经过一系列「计算」得出一个值,才把这个值才写到缓存中。

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

场景描述

A线程先成功删除了redis里面的数据,然后去更新mysql,此时mysql正在更新中,还没有结束。(比如网络延时)B突然出现要来读取缓存数据。此时redis里面的数据是空的,B线程来读取,先去读redis里数据(已经被A线程delete掉了)。

上述场景出现的问题:

  1. B从mysql获得了旧值:B线程发现redis里没有(缓存缺失)马上去mysql里面读取,从数据库里面读取来的是旧值
  2. B会把获得的旧值写回redis:获得旧值数据后返回前台并回写进redis(刚被A线程删除的旧数据有极大可能又被写回了)

最后:A线程更新完mysql,发现redis里面的缓存是脏数据

时间线程A线程B出现的问题
t1请求A进行写操作,删除缓存后,工作正在进行中......

A还更新完mysql,致B读到了旧值


线程B遵守回写机制,把旧值写回redis,导致其它请求读取的还是旧值,A白干了。

t2

缓存中读取不到,立刻读mysal,由于A还没有对mysal更新完,读到的是旧值。


 还把从mysgl读取的旧值,写回了redis

t3更新mysql数据库的值,overredis是被B写回的旧值
mysql是被A更新的新值
出现了,数据不一致问题。

解决方案:延时双删策略

如上图所示,可以先对缓存的数据先进行删除一次,再处理好数据库的业务以后睡眠一段时间后再进行一次删除。这就是延迟双删。

为什么要sleep一段时间? 

 加上sleep的这段时间,就是为了让线程B能够先从数据库读取数据,再把缺失的数据写入缓存,然后,线程A再进行删除。所以,线程A Sleep的时间,就需要大于现场B读取数据再写入缓存的时间。这样一来,其他线程读取数据时,会发生缓存缺失,所以会从数据库中读取最新值。因为这个方案会在第一次删除缓存值后,延迟一段时间再去进行删除,所以我们也把它叫做"延迟双删"

如果直接删掉的话,线程B可能还没写进去redis中,线程A写了,然后线程B再写,覆盖掉了。

休眠多久呢?这个时间怎么确定呢? 

  1. 在业务程序运行的时候,统计下线程读数据和写缓存的操作时间,自行评估自己的项目的读数据业务逻辑的耗时,以此为基础来进行估算。然后写数据的休眠时间则在读数据业务逻辑的耗时基础上加百毫秒即可
  2. 这么做的目的,就是确保读请求结束,写请求可以删除读请求造成的缓存脏数据

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

场景描述

时间线程A线程B出现的问题
t1删除数据库中的值

t2缓存中立刻命中,此时B读取的是缓存旧值。A还没有来得及删除缓存的值导致B缓存命中读到旧值。
t3更新缓存的数据,over

异常原因:假如缓存删除失败或者来不及,导致请求再次访问redis时缓存命中,读取到的是缓存旧值

解决方案:重试机制+引入MQ

  • 可以把要删除的缓存值或者是要更新的数据库值暂存到消息队列中(例如使用Kafka/RabbitMQ等)
  • 当程序没有能够成功地删除缓存值或者是更新数据库值时,可以从消息队列中重新读取这些值,然后再次进行删除或更新
  • 如果能够成功地删除或更新,我们就要把这些值从消息队列中去除,以免重复操作,此时,我们也可以保证数据库和缓存的数据一致了,否则还需要再次进行重试
  • 如果重试超过的一定次数后还是没有成功,我们就需要向业务层发送报错信息了,通知运维人员
     

  1. 更新数据库数据
  2. 数据库会将操作信息写入binlog日志当中
  3. 订阅程序提取出所需要的数据以及key
  4. 另起一段非业务代码,获得该信息
  5. 尝试删除缓存操作,发现删除失败
  6. 将这些信息发送至消息队列
  7. 重新从消息队列中获得改数据,重试操作。

为什么要引入MQ

  1. 在应用程序将数据更新到数据库后,将更新操作发送到消息队列中,然后再由消息队列异步地触发删除缓存数据的操作
  2. 这样做的好处是,即使在更新数据库后发生异常或者网络延迟等问题,数据更新操作也已经被放到消息队列中,并不会导致缓存数据和数据库数据不一致的问题

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

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

相关文章

rsync远程同步

与inodify结合使用,实现实时同步 rsync简介 rsync(Remote Sync,远程同步)是一个开源的快速备份工具,可以在不同主机之间镜像同步整个目录树,;支持增量备份,并保持链接和权限&#…

记录造数据测试接口

一、前言 在java开发中经常需要造数据进行测试接口,这里记录一下常用的通过造数据测试接口的方法。 二、一般的接口传参方式 1、接口的方式最好是使用JSON或者map的方式,这样的好处是传参可以灵活伸缩,返回的结果也最好是JSON或者map的方式…

Redis7--基础篇1(概述,安装、卸载及配置)

1. Redis概述 1.1 什么是Redis Redis:REmote Dictionary Server(远程字典服务器) Remote Dictionary Server(远程字典服务)是完全开源的,使用ANSIC语言编写遵守BSD协议,是一个高性能的Key-Value数据库提供了丰富的数…

MQTT 连接优化指南

🌷🍁 博主猫头虎(🐅🐾)带您 Go to New World✨🍁 🦄 博客首页——🐅🐾猫头虎的博客🎐 🐳 《面试题大全专栏》 🦕 文章图文…

JVM问题排查

本文详细说明了Java应用运行过程中几种常见的JVM相关问题,并给出了问题排查步骤。 一、堆中OOM 现象:Java线程负载过高,JVM内存几乎占满,甚至抛出java.lang.OutOfMemoryError错误。 思路:通过jmap能查看到对内存中实…

【Apollo 自动驾驶】Win11 中 WSL2 安装配置 Apollo 环境

【Apollo 自动驾驶】Win11 中 WSL2 安装配置 Apollo 环境 【1】Win11 WSL2 安装配置 Nvidia Cuda 【1.1】检查计算机硬件的显卡信息 计算机图标右击 -> 管理 -> 设备管理器 -> 显示适配器; 【1.2】检查对应显卡并安装 Nvidia 显卡驱动 下载对应的 Nv…

第15章_瑞萨MCU零基础入门系列教程之Common I2C总线模块

本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写,需要的同学可以在这里获取: https://item.taobao.com/item.htm?id728461040949 配套资料获取:https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总: ht…

vue3+vite项目运行报错[plugin vite:dep-pre-bundle]

报错截图 原因:vue-i18n 插件于9.5更新,打包和引入方式有改变,所以新启动和新部署的项目都会有这个问题。 解决方式: 1.项目全局搜索:vue-i18n/dist/v ue-i18n.cjs.js 然后将搜索到的代码替换为 :vue-i18…

为特征向量数据(1D数组)叠加噪声实现数据增强

为特征向量数据(1D数组)叠加噪声实现数据增强 日期作者版本备注2023.09.11Dog TaoV1.0完成文档的初始版本。 文章目录 为特征向量数据(1D数组)叠加噪声实现数据增强背景介绍叠加噪声的主要方法高斯噪声(Gaussian Nois…

微服务05-Docker基本操作

Docker的定义 1.什么是Docker Docker是一个快速交付应用、运行应用的技术: 可以将程序及其依赖、运行环境一起打包为一个镜像,可以迁移到任意Linux操作系统运行时利用沙箱机制形成隔离容器,各个应用互不干扰启动、移除都可以通过一行命令完…

超详细-Vivado配置Sublime+Sublime实现VHDL语法实时检查

目录 一、前言 二、准备工作 三、Vivado配置Sublime 3.1 Vivado配置Sublime 3.2 环境变量添加 3.3 环境变量验证 3.4 Vivado设置 3.5 配置验证 3.6 解决Vivado配置失败问题 四、Sublime配置 4.1 Sublime安装Package Control 4.2 Sublime安装VHDL插件 4.3 语法检查…

STM32F4X DMA

STM32F4X DMA 什么是DMASTM32F4X DMADMA框图DMA通道DMA仲裁器DMA FIFO DMA传输模式DMA传输方向存储器到存储器存储器到外设外设到存储器 DMA循环模式和普通模式循环模式(Circular)普通模式(Normal) DMA源、目标寄存器增量模式DMA例…

React【组件生命周期 、组件生命周期_挂载、 组件生命周期_更新 、组件生命周期_卸载、表单_受控组件、表单_受控组件处理多个输入】(三)

文章目录 组件生命周期 组件生命周期_挂载 组件生命周期_更新 组件生命周期_卸载 表单_受控组件 表单_受控组件处理多个输入 组件生命周期 每个组件都有自己的生命周期,从“生”到”死“。 在这个过程当中,它会有不同的状态,针对不同的状态…

大型语言模型的幻觉研究|减轻及避免大模型LLM幻觉(二)

“ 本文及上一篇综述了最近关于语言模型中幻觉问题的研究进展,主要集中在ChatGPT发布后的研究。文章讨论了如何评估、追踪和消除幻觉,并探讨了现有挑战和未来方向。希望本文能为对LLM幻觉问题感兴趣的朋友提供有价值的资源,促进LLM的实际应用…

【数学建模竞赛】超详细Matlab二维三维图形绘制

二维图像绘制 绘制曲线图 g 是表示绿色 b--o是表示蓝色/虚线/o标记 c*是表示蓝绿色(cyan)/*标记 ‘MakerIndices,1:5:length(y) 每五个点取点(设置标记密度) 特殊符号的输入 序号 需求 函数字符结构 示例 1 上角标 ^{ } title( $ a…

Aztec的隐私抽象:在尊重EVM合约开发习惯的情况下实现智能合约隐私

1. 引言 Aztec的架构,不同于当前“通过EVM兼容执行环境”所实现的区块链水平扩容趋势。Aztec内部笑称其构建的为首个非zkEVM协议。 Aztec专注于实现: 成为理解和需要智能合约隐私的开发者的终极解决方案。 Aztec为开发者提供构建隐私优先app所需的网…

Java 复习笔记 - 面向对象进阶篇

文章目录 一,Static(一)Static的概述(二)静态变量(三)静态方法(四)工具类(五)static的注意事项 二,继承(一)继…

TortoiseSVN 详细操作指南

文章底部有个人公众号:热爱技术的小郑。主要分享开发知识、有兴趣的可以关注一下。为何分享? 踩过的坑没必要让别人在再踩,自己复盘也能加深记忆。利己利人、所谓双赢。 热爱技术的小郑 1、引言 考虑以下几种情况: 你是否在一个…

Nginx详解 五:反向代理

文章目录 1. 正向代理和反向代理1.1 正向代理概述1.1.1 什么是正向代理1.1.2 正向代理的作用1.1.3 正向代理的基本格式 1.2 反向代理概述1.2.1 什么是反向代理1.2.2 反向代理可实现的功能1.2.3 反向代理的可用模块 2. 配置反向代理2.1 反向代理配置参数2.1.1 proxy_pass2.1.2 其…

华为云云耀云服务器L实例评测|老用户回归的初印象

华为云云耀云服务器L实例评测|老用户回归的初印象 前言一、新面孔1. 云耀云服务器2. 服务器特色 二、上手感官体验1. 性价比感受2. 推荐宝塔面板3. CloudShell登录4. 安全性 总结 前言 其实笔者接触华为云已经很久了,第一次使用的云服务器就是华为云。当…