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

目录

一、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>() {@Overridepublic Object execute(RedisOperations operations) throws DataAccessException {// 开启事务operations.multi();// 更新MySQLuserMapper.update(user);// 更新Redisoperations.opsForValue().set("user_" + user.getId(), user);// 执行事务operations.exec();return null;}
});

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

三、总结

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

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

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

相关文章

vue3 vuedraggable draggable element must have an item slot

vue3vite 看官网使用这种<template #item“{ element }”> <draggablev-model"myArray"start"onStart"end"onEnd":sort"false"item-key"id"draggable".item"handle".mover" ><template…

linux中最常用的网络命令

文章目录 linux中最常用的网络命令查看网络信息的原初 ifconfig默认无参数使用-s显示短列表配置IP地址修改MTU启动关闭网卡 网络中不中&#xff0c;先看ping行不行语法不加任何参数发送指定数目设定发送时间间隔组合使用 Linux ip命令显示网络设备设置IP地址启动关闭网卡统计方…

视图与索引连表查询(内/外联)和子查询

目录 一、视图 1.1、概念&#xff1a; 1.2、场景&#xff1a; 1.3、用视图的意义 1.2、创建(增加)视图 1.3、修改视图 1.4、删除视图 1.5、查看视图 ​编辑 二、索引 2.1、概念 2.2、优缺点 优点&#xff1a; 缺点&#xff1a; 2.3、应用场景 2.4、会失效 2.5、…

2024年汉字小达人区级选拔备考——选择题:选字填空

前面的几篇文章&#xff0c;六分成长介绍了汉字小达人区级选拔样题的前面三道题&#xff1a;看拼音写汉字、补充成语、诗词连线&#xff0c;这三道大题都是填空题&#xff0c;适合线下笔试&#xff0c;不太适合线上比赛。事实上&#xff0c;在区级自由比赛和市级比赛的时候&…

【LMM 012】TinyGPT-V:24G显存训练,8G显存推理的高效多模态大模型

论文标题&#xff1a;TinyGPT-V: Efficient Multimodal Large Language Model via Small Backbones 论文作者&#xff1a;Zhengqing Yuan, Zhaoxu Li, Lichao Sun 作者单位&#xff1a;Anhui Polytechnic University, Nanyang Technological University, Lehigh University 论文…

MIT 6.s081 实验解析——labs2

系列文章目录 MIT 6.s081 实验解析——labs1 MIT 6.s081 实验解析——labs2 文章目录 系列文章目录测试判断流程System call tracingsysinfo![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ab9ca34f1fc64b6aa1df74613dc1a397.png) 测试判断流程 完成代码后将.c文…

MySql海量数据存储与优化

一、Mysql架构原理和存储机制 1.体系结构 2.查询缓存 3.存储引擎 存储引擎的分类 innodb&#xff1a;支持事务&#xff0c;具有支持回滚&#xff0c;提交&#xff0c;崩溃恢复等功能&#xff0c;事务安全myisam:不支持事务和外键&#xff0c;查询速度高Memory&#xff1a;利…

应用OpenCV绘制箭头

绘制箭头函数 方法&#xff1a;函数cv2.arrowedLine( ) 语法格式&#xff1a;cv2.arrowedLine(img, pt1, pt2, color[, thickness[, line_type[, shift[, tipLength]]]]) 参数说明&#xff1a; img&#xff1a;要画的直线所在的图像&#xff0c;也称为画布。。 pt1&#x…

西电期末1027.判断同构数

一.题目 二.分析与思路 不用把他转成字符串再转成数字之类的&#xff0c;用数学解决就好&#xff01;找出一个数的最后位就是将其对求余啊&#xff0c;找一个数有几位以前也有过啊&#xff0c;那不就过了嘛&#xff01; 三.代码实现 #include<bits/stdc.h>//万能头 in…

msckf_vio在ubuntu20.04中的编译

1.新建catkin workspace文件夹&#xff0c;并在其中新建src文件夹&#xff0c;并将源码clone至src内。 源码地址&#xff1a;https://github.com/KumarRobotics/msckf_vio 目录层级示意如下&#xff0c;build和devel不必新建&#xff0c;后续指令会自动新建。 2. 在编译之前…

探索网络信息的利器 whois

文章目录 探索网络信息的利器 whois什么是whois命令&#xff1f;如何使用whois命令&#xff1f;注意事项 更多信息 探索网络信息的利器 whois 在网络的世界中&#xff0c;虚虚实实&#xff0c;真真假假&#xff0c;了解域名、IP地址的所有者信息是至关重要的。 Linux系统也提供…

LeetCode 2125. 银行中的激光束数量【数组,遍历】1280

本文属于「征服LeetCode」系列文章之一&#xff0c;这一系列正式开始于2021/08/12。由于LeetCode上部分题目有锁&#xff0c;本系列将至少持续到刷完所有无锁题之日为止&#xff1b;由于LeetCode还在不断地创建新题&#xff0c;本系列的终止日期可能是永远。在这一系列刷题文章…