Mysql与Redis如何保证数据一致性问题

news2024/9/28 5:27:26

目录

一、Mysql与Redis同步数据是否存在延迟呢?

二、如何保证一致性?

2.1、第一种方式:手动编码

2.2、第二种方式:MQ异步更新

2.3、第三种方式:binlog同步数据

2.4、第四种方式:双写一致性

2.5、第五种方式:使用Redis的事务支持

三、总结


一、Mysql与Redis同步数据是否存在延迟呢?

数据同步过程中,会存在短暂的延迟,这属于正常的现象,在分布式架构中很难实现数据强一致性,但你不能延迟太久。
弱一致性: 主从之间数据允许不一致性;
强一致性: 主从之间数据必须一致性; 如果实现 成本是非常高,会设计到一些锁的技术。
最终一致性:短暂的数据延迟是允许的,但是最终数据是需要一致。在分布式中做数据同步需要经过网络传输的,网络传输数据需要一定的时间,所以数据短暂的延迟是允许的,但是最终数据一定达成一致。
延迟是很难避免的,优化 减少延迟的时间。
公司中数据 同步延迟 优化在10-30毫秒。

二、如何保证一致性?

2.1、第一种方式:手动编码

更新mysql数据,再手动清除 Redis 缓存 ,之后再请求的时候因为Redis没有缓存了,所以重新查询数据库最新的数据,再手动同步到Redis中。

这种方式可以实现,但效率不高。

耦合性太大,因为是同步,当请求redis没有缓存时,查询数据库数据存到redis中,那么在缓存到redis过程中,redis宕机了,那么必然会触发重试机制,导致影响接口的响应速度。

2.2、第二种方式:MQ异步更新

首先用户发布请求去更新数据,我们先更新Mysql数据库,更新成功后再采用异步的形式投递消息放到我们的MQ中间件中,然后通过MQ的消费者去订阅我们的MQ的主题,获取到消息再异步去同步到Redis中。

// 更新MySQL
userMapper.update(user);
// 发送消息
rabbitTemplate.convertAndSend("updateUser", user.getId());
然后在消息消费者中更新Redis。
@RabbitListener(queues = "updateUser")
public void updateUser(String userId) {
    User user = userMapper.selectById(userId);
    redisTemplate.opsForValue().set("user_" + user.getId(), user);
}

优点:具有解耦性。

缺点:延迟概率很大。如果我们的MQ消费者没有及时获取到消息,那么也就不会及时的更新Redis,导致Mysql和Redis数据不一致,而且MQ也可能因为各种原因丢失消息。

2.3、第三种方式:binlog同步数据

订阅 mysql binlog 文件 异步的形式同步到 redis 中(canal 框架)。

首先我们要知道Mysql集群的原理

假如说现在的集群是一主一从,一般主节点用来做增删改操作,从节点用来做查询操作。从节点订阅主节点,当主节点的数据发生改变时,会给从节点发送binlog文件,从节点通过binlog文件进行数据更新同步。

那么同理,我们Mysql与Redis的一致性能不能也这么做呢?

我们使用canalserver端来伪装mysql从节点,订阅mysql主节点。

优点:前两种方式都是业务层面上编码去同步数据,当我们绕过代码,手动从数据库直接改数据,那么就无法同步。使用binlog方式是全局的方式,即使应用程序崩溃,也不会丢失binlog,因此能够保证最终的数据一致性。

缺点:canalserver端需要暴露出ip和端口号,然后我们单独的项目再连接canalserver端,意味着如果我们的项目是单机版本的话,同步的效率并不高,因为是单个线程去同步的。

假如我现在高并发的环境下写数据到mysql主,每秒写个几万次,我们只有一个单独的项目连接canalserver端,那么每次只能写一条到redis,那这个效率可太低了。

2.4、第四种方式:双写一致性

首先什么是双写?

先更新数据库,再同步更新redis,这就是双写。

但是这种情况会有个很严重的bug,就是在多线程的情况下会导致数据不一致。

比如有t1和t2两个线程,现在redis和mysql的数据都为clay:

  1. t1线程更新DB,t2线程也更新DB,但由于update操作一般条件都带着主键id,所以具有行锁,t1更新时t2在阻塞。
  2. t1线程更新DB数据改为zhangsan,并释放行锁。
  3. t2线程获取行锁,开始更新DB数据改为lisi(这时DB数据为lisi)。
  4. t2线程继续更新redis缓存数据位lisi。
  5. t1线程更新redis缓存数据为zhangsan(这时redis数据为zhangsan)。

从步骤上来看,此时redis与mysql数据不一致。

那么如何解决呢?

可以使用分布式锁,当然如果你要是单机版本项目,使用synchronized也可以。

分布式锁解决多个线程同时执行双写业务逻辑,最终只会有一个获取到分布式锁线程才可以执行,没有获取到分布式锁线程则阻塞等待,这样确保线程执行双写不会被其他线程干扰,但是效率非常低。

2.5、第五种方式:使用Redis的事务支持

Redis提供了事务(Transaction)支持,可以将一系列的操作作为一个原子操作执行。我们可以利用Redis的事务来实现MySQL和Redis的原子更新。

redisTemplate.execute(new SessionCallback<Object>() {
    @Override
    public Object execute(RedisOperations operations) throws DataAccessException {
        // 开启事务
        operations.multi();
        // 更新MySQL
        userMapper.update(user);
        // 更新Redis
        operations.opsForValue().set("user_" + user.getId(), user);
        // 执行事务
        operations.exec();
        return null;
    }
});

使用Redis事务可以确保MySQL和Redis的更新在同一事务中执行,避免了中间出现不一致的情况。但需要注意的是,Redis的事务并非严格的ACID事务,可能存在部分成功的情况。

三、总结

根据具体的业务需求和系统环境,选择合适的方案可以提高数据一致性的可靠性。然而,每种方案都有其优缺点和适用场景,需要综合考虑权衡。

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

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

相关文章

mysql之视图mysql连接案例索引

文章目录 一、视图1.1 含义1.2 操作1.2.1 创建视图1.2.2 视图的修改1.2.3 删除视图1.2.4 查看视图 二、连接案例01)查询" 01 "课程比" 02 "课程成绩高的学生的信息及课程分数02)查询同时存在" 01 "课程和" 02 "课程的情况03&#xff0…

SpringMVC-@RequestMapping注解

0. 多个方法对应同一个请求 RequestMapping("/")public String toIndex(){return "index";}RequestMapping("/")public String toIndex2(){return "index";}这种情况是不允许的&#xff0c;会报错。 1. 注解的功能 RequestMapping注…

Java面试之集合篇

前言 本篇主要总结JAVA面试中关于集合相关的高频面试题。本篇的面试题基于网络整理以及自己的总结编辑。在不断的完善补充哦。欢迎小伙伴们在评论区发表留言哦&#xff01; 1、基础 1.1、Java 集合框架有哪些&#xff1f; Java 集合框架&#xff0c;大家可以看看 《Java 集…

Latex + Overleaf 论文写作新手笔记

.tex 文件main.tex 文件 Latex 的文档层次结构不同文档类型的层次结构report 6 层结构实例article 5 层结构实例 Latex 语法图表插入与引用使用 figure 环境来插入图片使用 ref 命令来引用已有的图表格的插入与引用 代码块列表无序列表 itemize有序列表 enumerate 学位论文项目…

利用Type类来获得字段名称(Unity C#中的反射)

使用Type类以前需要引用反射的命名空间&#xff1a; using System.Reflection; 以下是完整代码&#xff1a; public class ReflectionDemo : MonoBehaviour {void Start(){A a new A();B b new B();A[] abArraynew A[] { a, b };foreach(A v in abArray){Type t v.GetTyp…

【CSS】文字描边的三种实现方式

目录 1. 可行的几种方式1.1. text-shadow 描边代码优缺点 1.2. text-stroke 描边实现优缺点 1.3. svg 描边实现优缺点 总结 1. 可行的几种方式 text-shadow–webkit-text-strokesvg 1.1. text-shadow 描边 MDN text-shadow 代码 <div class"text stroke">…

OpenMMlab导出CenterNet模型并用onnxruntime和tensorrt推理

导出onnx文件 直接使用脚本 import torch import torch.nn.functional as F from mmdet.apis import init_detectorconfig_file ./configs/centernet/centernet_r18_8xb16-crop512-140e_coco.py checkpoint_file ../checkpoints/centernet_resnet18_140e_coco_20210705_093…

【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax快速入门

【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax概述 【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax快速入门 【大数据进阶第三阶段之Datax学习笔记】阿里云开源离线同步工具Datax类图 【大数据进阶第三阶段之Datax学习笔记】使用…

DNS安全与访问控制

一、DNS安全 1、DNSSEC原理 DNSSEC依靠数字签名保证DNS应答报文的真实性和完整性。权威域名服务器用自己的私有密钥对资源记录&#xff08;Resource Record, RR&#xff09;进行签名&#xff0c;解析服务器用权威服务器的公开密钥对收到的应答信息进行验证。如果验证失败&…

5年经验之谈 —— 探索自动化测试用例设计粒度!

自动化测试用例的粒度指的是测试用例的细致程度&#xff0c;即每个测试用例检查的功能点的数量和范围。 通常&#xff0c;根据测试用例的粒度&#xff0c;可以被分为3种不同的层次&#xff0c;从更低层次的细粒度到更高层次的粗粒度。 第一种&#xff1a;单元测试 - 细粒度 单…

【IC设计】移位寄存器

目录 理论讲解背景介绍什么是移位寄存器按工作模式分类verilog语法注意事项 设计实例循环移位寄存器算术双向移位寄存器5位线性反馈移位寄存器伪随机码发生器3位线性反馈移位寄存器32位线性反馈移位寄存器串行移位寄存器&#xff08;打4拍&#xff09;双向移位寄存器&#xff1…

设置输入法默认为英文

1、进入语言首选项 2、添加首选的语言&#xff0c;选英文 3、 选择始终默认使用的输入法 4、选择默认语言 5、输入语言热键

LeetCode刷题:面试题 02.01. 移除重复节点

题目&#xff1a; 是否独立完成&#xff1a;算是&#xff0c;但是使用自己的办法时间复杂度会超标 解题思路&#xff1a; 1.双循环嵌套&#xff0c;定义快慢节点&#xff0c;双层嵌套循环&#xff0c;如果值一样则剔除&#xff0c;但是时间复杂度为O&#xff08;n&#xff09;…

数据交互系列:认识 cookie

cookie的原理 http本身是一个无状态的请求&#xff0c;cookie最初的原始目的是为了维持状态而产生的。在首次访问网站时&#xff0c;浏览发送请求中并未携带cookie&#xff0c;即发送无状态请求服务器接受请求之后会在请求上的respond header上加入cookie相关信息并返回给浏览…

【计算机组成原理】通过带符号整数的减法运算中加法器的溢出标志 OF 和符号标志 SF 对两个带符号整数的大小进行比较

对于带符号整数的减法运算&#xff0c;能否直接根据 CF 的值对两个带符号整数的大小进行比较&#xff1f; 对于带符号整数的减法运算&#xff0c;不能直接根据CF&#xff08;进/借位标志&#xff09;的值对两个带符号整数的大小进行比较。 CF标志位在带符号整数运算中主要用于…

使用Python+selenium3.0实现第一个自动化测试脚本

这篇文章主要介绍了使用Pythonselenium实现第一个自动化测试脚本&#xff0c;文中通过示例代码介绍的非常详细&#xff0c;对大家的学习或者工作具有一定的参考学习价值&#xff0c;需要的朋友们下面随着小编来一起学习学习吧 最近在学web自动化&#xff0c;记录一下学习过程。…

冬天夺去的清爽,可爱,春天都会还给你

这款外套上身可太时尚好看了 春天日常穿着或者出行游玩 应对早晚温差&#xff0c;兼具时尚和温度两不误 干净率性闲适的洒脱范整件衣服干净不失细节 下摆有橡筋收紧更加保暖了工艺方面也毫不逊色&#xff0c;防水拉链 四合扣、猪鼻扣一应俱全简直就是一件实用与时尚并存的…

Spring Boot实现数据加密脱敏:注解 + 反射 + AOP

文章目录 1. 引言2. 数据加密和脱敏的需求3. Spring Boot项目初始化4. 敏感数据加密注解设计5. 实现加密和脱敏的工具类6. 实体类和加密脱敏注解的使用7. 利用AOP实现加密和脱敏8. 完善AOP切面9. 测试10. 拓展功能与未来展望10.1 加密算法的选择10.2 动态注解配置 11. 总结 &am…

ReentrantLock底层原理学习一

J.U.C 简介 Java.util.concurrent 是在并发编程中比较常用的工具类&#xff0c;里面包含很多用来在并发场景中使用的组件。比如线程池、阻塞队列、计时器、同步器、并发集合等等。并发包的作者是大名鼎鼎的 Doug Lea。我们在接下来的课程中&#xff0c;回去剖析一些经典的比较…

【Docker基础三】Docker安装Redis

下载镜像 根据自己需要下载指定版本镜像&#xff0c;所有版本看这&#xff1a;Index of /releases/ (redis.io) 或 https://hub.docker.com/_/redis # 下载指定版本redis镜像 docker pull redis:7.2.0 # 查看镜像是否下载成功 docker images 创建挂载目录 # 宿主机上创建挂…