Redis与MySQL如何保证数据一致性
Redis与MySQL如何保证数据一致性
简单来说
该场景主要发生在读写并发进行时,才会发生数据不一致。
主要流程就是要么先操作缓存,要么先操作Redis,操作也分修改和删除。
一般修改要执行一系列业务代码,所以一般直接删除成本较低。
如果我们先删除Redis中数据,就会出现修改数据库数据时,其他线程读取到Redis为空,从数据库查,并写入Redis中,但这就是脏数据了,所以可以采用延迟双删策略,就是再删除一次,需要延迟的原因是因为在其他线程查时给Redis设置旧数据时,不延迟,就会覆盖新数据。(所以一般都要根据自己的业务逻辑评估一下大概时间。)如果说需要在这个阶段也需要保证数据一致性,那就只能上锁,保证强一致性,因为Redis和数据库是两个服务,只能通过加锁保证原子性,而这就影响了系统吞吐量(就违背了使用Redis提高吞吐量,也就是AP和CP不能同时满足的问题),所以一般都是保证最终一致性。
如果先修改数据库,同样会有数据不一致情况,在修改数据库处理时,其他线程也会读取旧数据,处理完了数据库就会删除缓存,保证了数据最终一致性,(所以比较推荐这种操作)
但是上述两种都会有删除Redis失败的情况,可以选择异步发送消息到MQ,线程监听MQ执行重试删除。
但这里的MQ代码过于耦合,需要解耦的话,就可以将他们提取出来,使用阿里的一款开源框架canal主要用途是基于MySQL数据库增量日志解析,提供增量数据订阅和消费Canal提供了各种语言的客户端,当Canal监听到binlog变化时,会通知Canal的客户端,过程也就是执行失败后,异步发送消息到MQ,Canal客户端监听到MQ消息执行重试
详细来说
1、引入
Redis 用来实现应用和数据库之间读操作的缓存层,主要目的是减少数据库 IO,还可以提升数据的 IO 性能。
当应用程序需要去读取某个数据的时候,首先会先尝试去 Redis 里面加载,如果命中就直接返回。如果没有命中,就从数据库查询,查询到数据后再把这个数据缓存到 Redis 里面。
这样一个架构中,会出现一个问题,就是一份数据,同时保存在数据库和 Redis 里面,当数据发生变化的时候,需要同时更新 Redis 和 Mysql,由于更新是有先后顺序的,并且它不像 Mysql中的多表事务操作,可以满足 ACID 特性。所以就会出现数据一致性问题。

2、同步策略
想要保证缓存与数据库的双写一致,一共有4种方式,即4种同步策略:
先更新缓存,再更新数据库; 先更新数据库,再更新缓存; 先删除缓存,再更新数据库; 先更新数据库,再删除缓存。 从这4种同步策略中,我们需要作出比较的是:
更新缓存与删除缓存哪种方式更合适?应该先操作数据库还是先操作缓存?
一般直接删除,因为缓存的更新成本更高 (因为你写入数据库的值,很多情况并不是直接写入缓存的,而是要经过一系列复杂的计算再写入缓存。那么,每次写入数据库后,都再次计算写入缓存的值无疑是浪费性能的。显然,删除缓存更为适合。)删除缓存操作简单,副作用只是增加了一次 chache miss,建议大家使用该策略。
一般先操作数据库,因为更方便保证最终一致性,先往下看
3、操作理解
先操作缓存

如果我们先删除Redis中数据,就会出现修改数据库数据时,其他线程读取到Redis为空,从数据库查,并写入Redis中,但这就是脏数据了,所以可以采用延迟双删策略,就是再删除一次,需要延迟的原因是因为在其他线程查时给Redis设置旧数据时,不延迟,就会覆盖新数据。(所以一般都要根据自己的业务逻辑评估一下大概时间。)如果说需要在这个阶段也需要保证数据一致性,那就只能上锁,保证强一致性,因为Redis和数据库是两个服务,只能通过加锁保证原子性,而这就影响了系统吞吐量(就违背了使用Redis提高吞吐量,也就是AP和CP不能同时满足的问题),所以一般都是保证最终一致性。
先操作数据库

如果先修改数据库,同样会有数据不一致情况,在修改数据库处理时,其他线程也会读取旧数据,处理完了数据库就会删除缓存,保证了数据最终一致性,(所以比较推荐这种操作)
4、解决
一般使用延迟双删和删除重试机制
延迟双删
一个读线程发现缓存中没有对应的数据,去查库,在查询完毕准备更新缓存期间,另一个写线程完成了写库以及对缓存的删除(此时数据库中的数据是新数据,而读线程将缓存更新为旧数据),一般情况下,这种事件的发生概率很低,但是使用延迟双删可以规避掉这种问题,进一步提高数据的一致性,但缺点就是性能会有所下降
那具体的超时时间要根据你具体的业务来定,一般设置几秒足够了
队列+重试机制
上述情况都会有删除Redis失败的情况,可以使用重试机制
比如重试三次,三次都失败则记录日志到数据库并发送警告让人工介入在高并发的场景下,重试最好使用异步方式,比如发送消息到 mq 中间件,实现异步解耦
也就是选择异步发送消息到MQ,线程监听MQ执行重试删除。

Cannal解耦
但这里的MQ代码过于耦合,需要解耦的话,就可以将他们提取出来,使用阿里的一款开源框架canal主要用途是基于MySQL数据库增量日志解析,提供增量数据订阅和消费Canal提供了各种语言的客户端,当Canal监听到binlog变化时,会通知Canal的客户端,过程也就是执行失败后,异步发送消息到MQ,Canal客户端监听到MQ消息执行重试
通过该框架可以对MySQL的binlog进行订阅,而canal正是模仿了mysql的slave数据库的备份请求,使得Redis的数据更新达到了相同的效果。 MQ消息中间可以采用RocketMQ来实现推送

相关文章:
Redis与MySQL如何保证数据一致性
Redis与MySQL如何保证数据一致性 简单来说 该场景主要发生在读写并发进行时,才会发生数据不一致。 主要流程就是要么先操作缓存,要么先操作Redis,操作也分修改和删除。 一般修改要执行一系列业务代码,所以一般直接删除成本较低…...
基于微信小程序的教室预约系统+LW示例参考
1.项目介绍 功能模块:管理员(学生管理、教师管理、申请管理、设备管理、报修管理等)、普通用户/学生(注册登录、申请预约、退订、报修等)技术选型:SSM、JSP、uniapp等测试环境:idea2024&#x…...
Linux 安装 Git 服务器
一、安装 Git 1. 在 CentOS/RHEL 中使用以下命令: sudo yum update -y # 或者 sudo dnf update -y (在较新的系统中) sudo yum install git -y验证安装:git --version 2. 配置 Git 用户 git config --global user.name "Your Name" git co…...
总结:Yarn资源管理
一、介绍 本文梳理下Yarn的资源分配计算逻辑。 二、配置 - 资源限制 1、配置NodeManager可分配的资源池的总量 <property><name>yarn.nodemanager.resource.memory-mb</name><value>4096</value> </property> 作用对象:节点管理器(No…...
Python学习34天
import random class Game: peo0 rob0 # # def __init__(self,peo,rob): # self.peopeo # self.robrob def Play(self): """ 石头剪刀布游戏,0代表石头,1代见到,2代表石头 …...
深入浅出 WebSocket:构建实时数据大屏的高级实践
简介 请参考下方,学习入门操作 基于 Flask 和 Socket.IO 的 WebSocket 实时数据更新实现 在当今数字化时代,实时性是衡量互联网应用的重要指标之一。无论是股票交易、在线游戏,还是实时监控大屏,WebSocket 已成为实现高效、双向…...
三开关VUE组件
一、使用效果 <template><QqThreeSwitch v-model"value" /><!-- <SqThreeSwitch v-model"value" :options"[test1, test2, test3]"><template #left-action><div style"display: flex"><IconMoon…...
SpringCloud+SpringCloudAlibaba学习笔记
SpringCloud 服务注册中心 eureka ap 高可用 分布式容错 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> <dependency><groupId…...
牛客小白月赛105(A~E)
文章目录 A lz的吃饭问题思路code B lz的数字问题思路code C lz的蛋挞问题思路code D lz的染色问题思路code E lz的括号问题思路code 总结 牛客小白月赛105 A lz的吃饭问题 思路 签到题,比较大小即可 code void solve(){int a,b,c,d;cin >> a >> b…...
OSPF协议整理
OSPF(Open Shortest Path First)即开放式最短路径优先协议,是一种广泛应用于大型网络中的链路状态路由协议。 OSPF 的基本概念 OSPF 是基于链路状态算法的内部网关协议(IGP),用于在一个自治系统ÿ…...
Java中的多线程
文章目录 Java中的多线程一、引言二、多线程的创建和启动1、继承Thread类2、实现Runnable接口 三、线程的常用方法1、currentThread()和getName()2、sleep()和yield()3、join() 四、线程优先级五、使用示例六、总结 Java中的多线程 一、引言 在Java中,多线程编程是…...
什么是聚簇索引、非聚簇索引、回表查询
其实聚集索引也叫聚簇索引,二级索引也叫非聚簇索引,大家不要认为这是不同的两个知识点。 定义 先看一下数据库的索引介绍。 聚簇索引 1. 如果存在主键(一般都存在),主键索引就是聚簇索引。 2. 如果不存在,…...
探索 Spring 框架核心组件:构建强大 Java 应用的基石
Spring框架作为Java企业级开发的首选框架之一,其强大的功能和灵活的架构深受开发者喜爱。Spring框架的核心组件共同构建了一个高效、可扩展的应用程序开发平台。本文将深入探讨Spring框架的核心组件,揭示它们如何在Spring框架中发挥关键作用。 一、Bean…...
Android 13 Aosp 默认允许应用动态权限
图库 frameworks/base/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java 修改 public void grantDefaultPermissions(int userId) {DelayingPackageManagerCache pm new DelayingPackageManagerCache();grantPermissionsToSysCompon…...
【C++知识总结1】c++第一篇,简单了解一下命名空间是什么
一、C的由来 C语言是一种结构化和模块化的编程语言,它对于处理较小规模的程序非常适用。然而,当面临需要高度抽象和建模的复杂问题,以及规模较大的程序时,C语言就显得不那么合适了。为了应对这种挑战,并在解决软件危机…...
从0开始深度学习(32)——循环神经网络的从零开始实现
本章将从零开始,基于循环神经网络实现字符级语言模型(不是单词级) 首先我们把从0开始深度学习(30)——语言模型和数据集中的load_corpus_time_machine()函数进行引用,用于导入数据: train_iter…...
GitLab使用操作v1.0
1.前置条件 Gitlab 项目地址:http://******/req Gitlab账户信息:例如 001/******自己的分支名称:例如 001-master(注:master只有项目创建者有权限更新,我们只能更新自己分支,然后创建合并请求&…...
cuda conda yolov11 环境搭建
优雅的 yolo v11 标注工具 AutoLabel Conda环境直接识别训练 nvidia-smi 检查CUDA版本 下载nvidia cudnn对应的版本 将cuDNN压缩包内对应的文件复制到本地bin、include、lib的文件夹中 C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.6 miniConda快速开始-安装 执行…...
解决SpringBoot连接Websocket报:请求路径 404 No static resource websocket.
问题发现 最近在工作中用到了WebSocket进行前后端的消息通信,后端代码编写完后,测试一下是否连接成功,发现报No static resource websocket.,看这个错貌似将接口变成了静态资源来访问了,第一时间觉得是端点没有注册成…...
element-plus的组件数据配置化封装 - table
目录 一、封装的table、table-column组件以及相关ts类型的定义 1、ATable组件的封装 - index.ts 2、ATableColumn组件的封装 - ATableColumn.ts 3、ATable、ATableColumn类型 - interface.ts 二、ATable、ATableColumn组件的使用 三、相关属性、方法的使用以及相关说明 1. C…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)
骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术,它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton):由层级结构的骨头组成,类似于人体骨骼蒙皮 (Mesh Skinning):将模型网格顶点绑定到骨骼上,使骨骼移动…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
快刀集(1): 一刀斩断视频片头广告
一刀流:用一个简单脚本,秒杀视频片头广告,还你清爽观影体验。 1. 引子 作为一个爱生活、爱学习、爱收藏高清资源的老码农,平时写代码之余看看电影、补补片,是再正常不过的事。 电影嘛,要沉浸,…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...
【SpringBoot自动化部署】
SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一,能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时,需要添加Git仓库地址和凭证,设置构建触发器(如GitHub…...
【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...
《Docker》架构
文章目录 架构模式单机架构应用数据分离架构应用服务器集群架构读写分离/主从分离架构冷热分离架构垂直分库架构微服务架构容器编排架构什么是容器,docker,镜像,k8s 架构模式 单机架构 单机架构其实就是应用服务器和单机服务器都部署在同一…...
