MySQL 中主从之间是怎样保证数据一致的呢?

news2024/11/14 1:17:52

在我们日常的工作中,处理 MySQL 数据库相关问题时,我相信绝大多数 DBA 处理最棘手的问题就是数据库主从数据不一致的问题。

处理过关于 MySQL 数据库主从数据不一致的朋友一定印象非常深刻,因为稍有不慎就会将造成原有数据的丢失,并且这种丢失是持久性的,也就是说如果我们没有相关备份的话,该数据将会永久丢失,这对于一家互联网公司来说将是非常致命的错误。

那么,我们该如何保证 MySQL 数据库主从数据一致呢?

在介绍这个问题之前,我首先跟大家介绍一下 MySQL 数据库主从复制的原理。

注意:在开启主从复制之前,需要在 Worker 节点上关联 Master 节点,不知道的朋友可以上网查询一下,这里不再赘述。

通常,我们在从库上执行 start slave;,开启主从复制。我们确认是否成功开启主从复制最简单的办法是通过 show slave status; 查看 IO 线程 和 SQL 线程 是否开启。

下面我们来介绍一下这两个线程背后的逻辑。

  • 在从库上执行 start slave;,开启主从复制。
  • 从库的 IO 线程开始在读取 Master 节点信息,该信息保存在master.info中。
  • 主库在接收到从库的主从同步请求时,会开启一个 dump 线程,主要用于将 Master 节点的 binlog 日志发送给 Worker 节点。
  • Worker 节点中的 IO 线程接收 Master 节点发送过来的 binlog 日志的内容。
  • IO 线程接收到的 binlog 日志内容并不是直接写入到 Worker 节点,而是先保存在缓存之中,这个步骤最主要的原因是为了防止大量数据同时写入中继日志时导致的数据库异常。
  • Worker 节点在接收完 Master 发送过来的数据时,会回复 Master 节点一个 ACK 信号,这个步骤的目的是告诉 Master 节点数据已接收完毕。
  • IO 线程更新本节点中的 master.info,这个步骤主要是记录当前复制数据的留痕,以便下一次追加复制数据。
  • IO 线程将 TCP 缓存中的数据分批写入中继日志中,做持久化管理。此时 IO 线程的数据到此结束。
  • SQL 线程读取 relay-log.info 获取上次同步数据的位置。
  • SQL 线程根据上一个步骤中获取的位置开始读取中继日志中的数据
  • SQL 线程将读取出来的数据分批写入本节点中,并更新 relay-log.info 文件。
  • 中继日志自动清理同步过的数据。 此时,一次主从复制的过程完成
注意:上面步骤中的 ACK 表示回应的意思。一次基于 TCP 协议的通信中,请求方会发送一个 SYNC 信号请求连接,连接完成之后被请求方会返回一个 ACK 信号以示回应。

我们通过上述的主从复制的步骤中可以看到,一次主从复制是比较复杂的。那么在这些复杂的过程中,有哪些地方可能造成数据丢失,进而导致数据不一致呢?

最主要的就是 binlog 日志中的数据丢失。下面我们来详细介绍一下 binlog 日志为什么会丢失数据。

在介绍 binlog 日志相关问题之前,先创建一个测试数据表:

CREATE TABLE `t1` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_1` (`a`),
  KEY `index_2` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO t1 VALUES (10,60,10);
INSERT INTO t1 VALUES (20,50,20);
INSERT INTO t1 VALUES (30,40,30);
INSERT INTO t1 VALUES (40,30,40);
INSERT INTO t1 VALUES (50,20,50);
INSERT INTO t1 VALUES (60,10,60);

首先,我们简要说明一下 binlog 日志的三种类型,分别是 statement、row 以及 mixed。下面我们不详细介绍这三个类型的,如若不清楚可以参考 前几篇:MySQL 中的日志类型这么多,它们都有哪些作用?。在这里我们只介绍在三个类型可能导致的问题。

更多C++后台开发技术点知识内容包括C/C++,Linux,Nginx,ZeroMQ,MySQL,Redis,MongoDB,ZK,流媒体,音视频开发,Linux内核,TCP/IP,协程,DPDK多个高级知识点。

C/C++Linux服务器开发高级架构师/C++后台开发架构师免费学习地址

【文章福利】另外还整理一些C++后台开发架构师 相关学习资料,面试题,教学视频,以及学习路线图,免费分享有需要的可以点击领取

当 binlog 日志的类型设置成 statement 时,binlog 日志中记录的是我们执行的 SQL 的原文。具体如下:

mysql> delete from t1 where a < 50 and b > 10 limit 1;
Query OK, 1 row affected, 1 warning (0.01 sec)

mysql> show binlog events in 'mybinlog.000001';
+-----------------+-----+----------------+-----------+-------------+--------------------------------------------------------------+
| Log_name        | Pos | Event_type     | Server_id | End_log_pos | Info                                                         |
+-----------------+-----+----------------+-----------+-------------+--------------------------------------------------------------+
| mybinlog.000001 | 154 | Anonymous_Gtid |         1 |         219 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS'                         |
| mybinlog.000001 | 219 | Query          |         1 |         302 | BEGIN                                                        |
| mybinlog.000001 | 302 | Query          |         1 |         426 | use `test06`; delete from t1 where a < 50 and b > 10 limit 1 |
| mybinlog.000001 | 426 | Xid            |         1 |         457 | COMMIT /* xid=9 */                                           |
+-----------------+-----+----------------+-----------+-------------+--------------------------------------------------------------+
6 rows in set (0.00 sec)

从上方的执行结果来看,似乎是没有什么问题的,但实际它是有问题的,具体的问题如下:

-- 查询当前数据库的警告信息
mysql> show warnings;
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Level | Code | Message                                                                                                                                                                                                                         |
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Note  | 1592 | Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted. |
+-------+------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

通过上面的 SQL 语句我们可以看出,当我们采用 BINLOG_FORMAT = STATEMENT 的时候,执行 delete from t1 where a < 50 and b > 10 limit 1; 这条 SQL 时,会报一个警告错误,这个错误的原因是什么呢?

在介绍这个问题的原因之前,我们先将 binlog 日志的类型更换成 row 类型,看一下 binlog 日志在 row 类型下是否会发生这一问题。具体如下:

mysql> SHOW GLOBAL VARIABLES LIKE '%BINLOG_FORMAT%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.01 sec)

mysql> delete from t1 where a < 50 and b > 10 limit 1;
Query OK, 1 row affected (0.01 sec)

mysql> show warnings;
Empty set (0.00 sec)

这个时候,我们可以惊奇地发现,当我们将 binlog 日志的类型设置成 row 时,上面的错误居然消失了。这个时候我们一定很想问为什么。

在介绍具体原因之前,我们首先来看看 binlog 日志中记录的两种模式的不同。具体如下:

执行如下 SQL ,来解析 binlog 日志中的二进制日志。

[root@dxd ~]# mysqlbinlog -vv mybinlog.000001 --start-position=123;
  • STATEMENT

  • ROW

通过比较,我们可以明显看出,STATEMENT 类型中是直接保存 SQL 的,但 ROW 类型中并没有直接保存 SQL ,而是通过事件的方式保存需要处理的数据的。

就拿我们上面执行删除数据的这条语句来说,当 binlog 日志类型为 STATEMENT 时,在 binlog 日志中记录的是原生 SQL,那么如果我们直接拿这条 SQL 去数据库执行时,可能就会存在争议,具体如下:

如果我们按照索引 index_1 去执行该 SQL 时,我们删除的这一条数据可能是 id = 30;但是如果我们按照索引 index_2 去执行该 SQL 时,我们删除的数据却可能是 id = 20。此时如果我们按照这种方式去 Worker 节点执行该 SQL 时,我们是无法保证 Worker 节点和 Master 节点删除的数据是一致的,这也就是导致主从数据不一致的根源。

那么,如果我们使用 ROW 类型时,为什么没有这一问题呢?此时我们通过 ROW 类型的图可以看到的是在 ROW 类型下并不直接保存具体的 SQL,而是通过事件的方式(其实也就是利用主键) ,这种方式就有效地保证了 Worker 节点和 Master 节点之间数据一致性。

但是,我们可以看到,ROW 类型的日志中记录了很多其他的字符,这其实也是 ROW 类型的一个非常大的缺点,就是非常占用存储空间。

那么,我们总结一下: STATEMENT 类型可能会导致数据不一致,但是它的数据量比较小,节省存储空间;而 ROW 能够有效地处理数据不一致的问题,但是占用的空间非常大。

为了解决上面的问题, MySQL 为我们提供了一个中性的 binlog 日志类型,即:mixed

mixed 类型最主要的就是结合了 STATEMENT 类型存储数据量比较小的优点,同时也结合了 ROW 类型解决数据不一致这一特点,也就是说通常在记录不会产生歧义的 SQL 中使用 STATEMENT 类型的记录方式,在有可能产生歧义的 SQL 中使用 ROW 类型的记录方式

总结

今天,我们介绍了 MySQL 数据库主从复制过程中可能会遇到的一些问题。其中最主要的就是 binlog 日志在 STATEMENT 类型下有着存储数据量小的优点,但是也有导致数据不一致的情况;ROW 类型能够有效地解决数据不一致这种情况,但是也有存储数据量大这一缺点;MySQL 数据库结合前面两种类型的优点,又为我们提供了一个 mixed 类型。

在日常的生产环境中,常用的 binlog 日志类型就是 mixed 类型。

原文链接:MySQL 中主从之间是怎样保证数据一致的呢?

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

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

相关文章

精益安灯电子看板实现了实时监测

众所周知&#xff0c;智能工厂的规划建设是一个十分复杂的系统工程。所以安灯电子看板是精益生产中一一个重要组成要素&#xff0c;可以提升工厂生产车间的过程管理&#xff0c;生产数据做的信息化、目视化&#xff1b;信息快捷化、生产工序透明化等&#xff0c;是提高生产率的…

自动控制原理笔记-根轨迹的概念-根轨迹方程

目录 根轨迹的基本概念&#xff1a; 根轨迹的概念&#xff1a;当开环系统某一参数从 0 到∞变化时&#xff0c;闭环极点在S 平面上变化所描绘出的轨迹。 闭环零极点与开环零极点之间的关系&#xff1a; 根轨迹方程&#xff1a; 开环增益于根轨迹间的关系&#xff1a; 闭环系…

excel 格式化日期为字符串

最近经常遇到excel打开文件的时候&#xff0c;excel自动将yyyy-MM-dd HH:mm:ss &#xff08;如&#xff1a;2022-01-21 12:12:12 &#xff09;之类的时间的自动转为这样的格式列&#xff0c;2022/1/21 12:12:12 &#xff0c;导致有想从excel/csv格式 中复制原始日期格式比较麻烦…

【软件测试】一个真正的测试面试过程,我比面试官还狡猾......

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 先卖个关子&#xf…

关羽这灵敏度你们爱了吗?#走位 #游戏外设

关羽这灵敏度你们爱了吗&#xff1f;#走位 #游戏外设 关羽这灵敏度你们爱了吗&#xff1f;#走位 #游戏外设

var、let、const之间的区别

说一下var、let、const之间的区别一、var二、let三、const四、var、let 、const的区别&#xff1f;一、var 用var声明的变量既是全局变量&#xff0c;也是顶层变量 注意&#xff1a;顶层对象&#xff0c;在浏览器环境指的是window对象&#xff0c;在Node指的是global对象。 var…

老杨说运维 | AIOps如何助力实现全面可观测性(下)

上期我们讲到可观测性是什么&#xff0c;以及它能给企业带来的价值&#xff0c; 戳→「老杨说运维 | AIOps如何助力实现全面可观测性&#xff08;上&#xff09;」一键回看上期精彩内容。 说完了什么是可观测性&#xff0c;这期我们来看看可观测性是如何落地实践的。 一、可观…

若依配置教程(五)数据权限的使用及配置

文章目录一、Controller模块二、Mapper.xml三、在Impl服务层加入权限注解四、分配数据权限若依文档 学会了前几篇文章如何新建模块和生成代码&#xff0c;这篇接下来介绍数据权限的配置和使用&#xff1a; 首先&#xff0c;在建立数据库表的时候&#xff0c;必须要添加user_id…

深入理解spring三级缓存解决循环依赖的设计思路

说到这个话题的时候&#xff0c;很多人再熟悉不过了&#xff0c;因为听到太多了&#xff0c;而且百度一大堆&#xff0c;但是理解到什么程度了呢&#xff0c;或者说只是知道这回事&#xff0c;但是为什么这样设计&#xff0c;代码中有什么可以借鉴的&#xff0c;在实际业务中有…

Jmeter和JDK下载安装及环境变量配置详细教程

写在最前面的话&#xff0c;jmeter安装部署依赖java环境&#xff0c;所以得安装JDK java环境检查 命令行输入&#xff1a;java -version 如果出现以下内容&#xff0c;说明java环境已安装&#xff0c;无需理会&#xff0c;如果没有&#xff0c;需要安装JDK 一、下载并安装JDK…

PSP模拟器截图CG的高清化-Waifu2x

由PSP游戏本体提取CG图片直接超分自然是比较舒服的&#xff0c;但实际上因了加密等诸多问题&#xff0c;甚或不如直接模拟器截了图进行处理来得方便 1. 模拟器设置 如果要截图的话&#xff0c;自然是以得到更好的效果为宜&#xff0c;于是可以对模拟器进行一些基本的设置。 对…

Java两大工具库:Commons和Guava(3)

您好&#xff0c;我是湘王&#xff0c;这是我的CSDN博客。值此新春佳节&#xff0c;我给您拜年啦&#xff5e;祝您在新的一年中所求皆所愿&#xff0c;所行皆坦途&#xff0c;展宏“兔”&#xff0c;有钱“兔”&#xff0c;多喜乐&#xff0c;常安宁&#xff01;虽然Apache Com…

【33】C语言 | 联合体详解

目录 1、联合类型的定义 2、联合的特点 3、联合大小的计算 1、联合类型的定义 联合也是一种特殊的自定义类型这种类型定义的变量也包含一系列的成员&#xff0c;特征是这些成员公用同一块空间《所以联合也叫共用体)先看下面代码输出什么&#xff1f; union Un {char c;int …

我用css3为好友胡歌的宝宝做了一个动画照片墙

软件人生风雨十年&#xff0c;仙剑一过去也有十年有余了&#xff0c;和胡歌认识那么久&#xff0c;今日喜闻好友胡歌生宝宝的消息&#xff0c;惊喜之余用css3为胡歌的宝宝做了动画照片墙的模板效果。 目录 1. 实现思路 2. 墙体的实现 3. 选取模板素材&#xff0c;进行图片元…

虹科案例 | 石油天然气行业CFD高性能计算解决方案

公司简介 DNV GL 是全球领先的能源、石油和海事行业风险管理及资产绩效提升的软件供应商&#xff0c;主要为客户提供全面的风险管理和各类评估认证服务&#xff0c;认证涉及信息通信技术、汽车及航空天、食品与饮料、医疗等方面。 DNV GL以让世界更安全、更智能、更环保为使命…

随笔记——MQ

文章目录1、 概要2、 为什么使用MQ/使用MQ的好处&#xff1f;3、 使用MQ的缺点&#xff1f;4、 使用MQ会产生的问题及解决办法&#xff1f;4.1、如何保证消息的顺序性&#xff1f;4.2、如何解决消息被重复消费&#xff1f;4.3、如何解决消息丢失&#xff1f;4.4、如何解决消息积…

基于turtle实现的新冠疫情传播模拟 附完整代码可直接运行

代码运行视频参考:https://www.bilibili.com/video/BV1hR4y1h7Te/?spm_id_from=333.999.0.0&vd_source=8f3cf4ad6c08a40d40ca6809c9c9e8ca 高阶版运行结果展示

基于Android的家校互动系统app

需求信息&#xff1a; 功能需求: 1&#xff1a;发通知、发作业 发通知和发作业&#xff0c;是学校教师特有的功能&#xff0c;教师可以通过平台进行通知和作业的发放&#xff0c;每当发一条信息&#xff0c;该班的所有人员便可以收到来自服务器的信息推送&#xff0c;提醒家长打…

经济下行压力下的销售行业,将数据效益最大化方能立于不败之地

2022年9月&#xff0c;世界经济论坛发布《首席经济学家展望》报告&#xff0c;并指出“当前每10位经济学家中&#xff0c;就有7位认为全球经济发生了一定程度的衰退。” 在整体下行的经济环境中&#xff0c;由于销售人员会获悉客户削减预算&#xff0c;推迟采购&#xff0c;或…

mysql服务nginx和firewalld代理实现

文章目录环境准备nginx代理mysql服务linux防火墙实现mysql流量转发工作中常常遇到只有某个特定服务器才能访问数据库的情况&#xff0c;这个时候为了解决团队同时访问数据库的问题可以采用nginx代理和linux防火墙流量转发的方式解决。实战测试如下&#xff1a;环境准备 准备一…