MySQL系列(十):主从架构

在这里插入图片描述

一:主从架构

常见的主从架构模式有四种:

  • 一主多从架构:适用于读大于写的场景,采用多个从库来分担数据库系统的读压力。
  • 多主架构:适用于读写参半的场景,采用多个主库来承载数据库系统整体的读写压力。
  • 多主一从架构:适用于写大于读的场景,采用多个主库分担写压力,单个从库承载读压力。
  • 级联复制架构:适用于读大于写的场景,采用单个从节点来分担从库对主库造成的 I/O 压力。
    接下来的讨论都是针对最常见的一主多从架构。

主从架构中必须有一个主节点,以及一个或多个从节点,所有的数据都会先写入到主,接着其他从节点会复制主节点上的增量数据,从而保证数据的最终一致性,使用主从复制方案,可以进一步提升数据库的可用性和性能:

  • 在主节点宕机或故障的情况下,从节点能自动切换成主节点的身份,从而继续对外提供服务。

  • 提供数据备份的功能,当主节点的数据发生损坏时,从节点中依旧保存着完整数据。

  • 可以基于主从实现读写分离,主节点负责处理写请求,从节点处理读请求,进一步提升性能。
    但无论任何技术栈的主从架构,都会存在致命硬伤,同时也会存在些许问题需要解决:

  • 硬伤:木桶效应,一个主从集群中所有节点的容量,受限于存储容量最低的哪台服务器。

  • 数据一致性问题:由于同步复制数据的过程是基于网络传输完成的,所以存储延迟性。

  • 脑裂问题:从节点会通过心跳机制,发送网络包来判断主机是否存活,网络故障情况下会产生多主。

二:主从复制

MySQL 集群的主从复制过程梳理成 3 个阶段:

  • 写入 Binlog:主库更新本地存储数据,并写 binlog 日志,然后提交事务。
  • 同步 Binlog:把 binlog 复制到所有从库上,每个从库把 binlog 写到暂存日志中。
  • 回放 Binlog:回放 binlog,并更新存储引擎中的数据。
    在这里插入图片描述

具体详细过程如下:

1.MySQL 主库在收到客户端提交事务的请求之后,会先更新数据;
2.数据写入完成后,再去写入 binlog,然后提交事务,返回客户端“操作成功”的响应;
3.主节点上有一条专门监听 binlog 的 log dump 线程;
4.当 log dump 线程监听到日志发生变更时,会通知从节点来拉取数据;
5.从节点有专门的 I/O 线程(io_thread)用于等待主节点的通知,当收到通知时会去请求一定范围的数据;
6.当从节点在主节点上请求到数据后,会将得到的数据写入到 relay-log 中继日志,然后返回给主库“复制成功”的响应;
7.从节点上有专门负责监听 relay-log 变更的线程(sql_thread),当日志出现变更时会开始工作;
8.中继日志出现变更后,会从中读取日志记录,然后回放 binlog 更新数据,实现主从数据一致性。

复制模式
同步复制
MySQL 主库提交事务的线程要等待所有从库的复制成功响应,才返回客户端结果。这种方式在实际项目中,基本上没法用,原因有两个:一是性能很差,因为要复制到所有节点才返回响应;二是可用性也很差,主库和所有从库任何一个数据库出问题,都会影响业务。

异步复制
是 MySQL 默认使用的复制模式,MySQL 主库提交事务的线程并不会等待 binlog 同步到各从库,就返回客户端结果。这种模式一旦主库宕机,数据就会发生丢失。

半同步复制
MySQL 5.7 版本之后增加的一种复制方式,介于两者之间,事务线程不用等待所有的从库复制成功响应,只要一部分复制成功响应回来就行,比如一主二从的集群,只要数据成功复制到任意一个从库上,主库的事务线程就可以返回给客户端。这种半同步复制的方式,兼顾了异步复制和同步复制的优点,即使出现主库宕机,至少还有一个从库有最新的数据,不存在数据丢失的风险。

增强式半同步复制
也被称为无损复制,也是 MySQL5.7 引入的。和之前传统的半同步复制区别在于:从 after-commit 变成了 after-sync ,如果将复制模式配置成半同步时,默认就会选用无损复制模式。

after-commit:主库在未收到从库的 ACK 之前,虽然不会给客户端返回写入成功,但本质上会提交事务,也就是主库中的其他事务是可以看见对应数据的,当此时出现宕机时,就会导致旧主上能查询出的数据,在新主上无法查询出来了。
after-sync:当主库未收到从库的 ACK 之前,也不会在主库上提交事务,也就是保证了主从节点的数据强一致性,解决了 after-commit 中存在的问题。但是相较于 after-commit,可能导致事务迟迟不提交,从而导致锁资源不释放和阻塞等待等性能问题。

延迟复制
当从库上的 I/O 线程,将主库的 binlog 请求回来后,从节点的 SQL 线程并不会立刻解析日志执行,而是等待一段时间后再解析日志执行,这个等待的时间可以配置。

延迟复制的好处是可以防止误删操作。缺点是会有很长一段时间的数据不一致,可能导致数据的丢失。一般用于仅作为备库的节点使用,不能进行读写分离。

并行复制
GTID复制
GTID,Global Transaction Identifier,用于唯一地标识一个事务。

GTID 由节点 UUID + 事务 ID 两部分组成,MySQL 在第一次启动时都会利用 UUID 随机生成一个 server_id,还会对每一个写事务都分配一个顺序递增的值作为事务 ID,GTID 的格式为 server_uuid:trx_id。

基于 GTID 的复制过程:

master 在更新数据时,会为每一个写事务分配一个全局的 GTID,并记录到 binlog 中。
slave 节点的 I/O 线程拉取数据时,会将读到的记录写到 relay-log 中,并设置 gtid_next 值。
slave 节点的 SQL 线程执行前,会读取 gtid_next 值得知接下来该解析哪条日志并执行。
slave 节点的 SQL 线程在执行时,会先比对自身的 binlog 日志中是否有对应的 GTID:
有:意味着该 GTID 对应的事务已经执行过了,slave 会自动忽略掉这条记录。
没有:SQL 解析该 GTID 对应的 relay-log 记录并执行,再将 GTID 记录到 binlog。

基于 GTID 的自动同步:发生主从切换时可以执行 change master to master_auto_position=1,会自动去新主库上寻找数据的同步点。

GTID 是基于事务来实现的,也就代表不支持事务的存储引擎无法使用这种机制。

组复制
GTID 复制是组复制得基础。组复制是指将一组并行执行的事务,全部放入到一个 GTID 中记录,后续从节点同步数据时,会一次性读取这一组事务解析并执行,即组提交。

组复制的 GTID 通过逗号分隔。

并行复制
并行复制是在组复制的基础上实现的。因为能够在同一时间内提交的事务,绝对是不存在锁冲突的,所以可以开启多条线程同时执行一个组中不同的事务。

并行复制能够在很大程度上提升从库复制数据的速度,也就是能够让从库的数据实时性提升。

在 MySQL5.7 中官方为这种机制命名为 enhanced multi-threaded slave,简称 MTS 机制,同时为了兼容 5.6 版本中的并行复制,又多加入了一个 slave-parallel-type 参数:

DATABASE:默认的并行复制模式,表示基于库级别来完成并行复制。
LOGICAL_CLOCK:表示基于组提交的方式来完成并行复制。
虽然 5.7 中的并行复制,在一定程度上解决了原有的从库延迟问题,但如果一个新的从节点加入集群时,因为要从头开始同步数据,这种并行复制的模式依旧存在效率问题,而到了 MySQL8.0 对于并行复制技术提出了真正的解决之道,也就是基于 writeset 的 MTS 技术。即多个事务之间,只要变更的数据记录没有重叠,也就是操作的数据没有冲突,无需在一个事务组内,也可以支持并发执行。

两阶段提交
2PC,two-phase commit protocol。

事务提交后,redo log 和 binlog 都要持久化到磁盘,但是这两个是独立的逻辑,可能出现半成功的状态,这样就造成两份日志之间的逻辑不一致。

如果在将 redo log 刷入到磁盘之后, MySQL 突然宕机了,而 binlog 还没有来得及写入。MySQL 重启后,通过 redo log 能将 Buffer Pool 中的相应数据恢复到新值,但是 binlog 里面没有记录这条更新语句,在主从架构中,binlog 会被复制到从库,由于 binlog 丢失了这条更新语句,从库的数据是旧值;
如果在将 binlog 刷入到磁盘之后, MySQL 突然宕机了,而 redo log 还没有来得及写入。由于 redo log 还没写,崩溃恢复以后这个事务无效,所以数据还是旧值;而 binlog 里面记录了这条更新语句,数据是新值;
在持久化 redo log 和 binlog 这两份日志的时候,如果出现半成功的状态,就会造成主从环境的数据不一致性。因为 redo log 影响主库的数据,binlog 影响从库的数据,所以 redo log 和 binlog 必须保持一致才能保证主从数据一致。

MySQL 为了避免出现两份日志之间的逻辑不一致的问题,使用了「两阶段提交」来解决,两阶段提交其实是分布式事务一致性协议,它可以保证多个逻辑操作要不全部成功,要不全部失败,不会出现半成功的状态。

当客户端执行 commit 语句或者在自动提交的情况下,MySQL 内部开启一个 XA 事务,分两阶段来完成 XA 事务的提交,分别是准备阶段和提交阶段。

prepare 阶段:将 XID(内部 XA 事务的 ID) 写入到 redo log,同时将 redo log 对应的事务状态设置为 prepare,然后将 redo log 持久化到磁盘(innodb_flush_log_at_trx_commit = 1 的作用);
commit 阶段:把 XID 写入到 binlog,然后将 binlog 持久化到磁盘(sync_binlog = 1 的作用),接着调用引擎的提交事务接口,将 redo log 状态设置为 commit,此时该状态并不需要持久化到磁盘,只需要 write 到文件系统的 page cache 中就够了,因为只要 binlog 写磁盘成功,就算 redo log 的状态还是 prepare 也没有关系,一样会被认为事务已经执行成功;
在这里插入图片描述

不管是时刻 A(redo log 已经写入磁盘, binlog 还没写入磁盘),还是时刻 B (redo log 和 binlog 都已经写入磁盘,还没写入 commit 标识)崩溃,此时的 redo log 都处于 prepare 状态。

在 MySQL 重启后会按顺序扫描 redo log 文件,碰到处于 prepare 状态的 redo log,就拿着 redo log 中的 XID 去 binlog 查看是否存在此 XID:

如果 binlog 中没有当前内部 XA 事务的 XID,说明 redolog 完成刷盘,但是 binlog 还没有刷盘,则回滚事务。对应时刻 A 崩溃恢复的情况。
如果 binlog 中有当前内部 XA 事务的 XID,说明 redolog 和 binlog 都已经完成了刷盘,则提交事务。对应时刻 B 崩溃恢复的情况。
可以看到,对于处于 prepare 阶段的 redo log,即可以提交事务,也可以回滚事务,这取决于是否能在 binlog 中查找到与 redo log 相同的 XID,如果有就提交事务,如果没有就回滚事务。这样就可以保证 redo log 和 binlog 这两份日志的一致性了。

两阶段提交是以 binlog 写成功为事务提交成功的标识。

事务没提交的时候,redo log 也是可能被持久化到磁盘。但是 binlog 必须在事务提交之后,才可以持久化到磁盘。

组提交
两阶段提交虽然保证了两个日志文件的数据一致性,但是性能很差,主要有两个方面的影响:

磁盘 I/O 次数高:对于“双1”配置,每个事务提交都会进行两次 fsync(刷盘),一次是 redo log 刷盘,另一次是 binlog 刷盘。
锁竞争激烈:两阶段提交虽然能够保证「单事务」两个日志的内容一致,但在「多事务」的情况下,却不能保证两者的提交顺序一致,因此,在两阶段提交的流程基础上,还需要加一个锁来保证提交的原子性,从而保证多事务的情况下,两个日志的提交顺序一致。
MySQL 引入了 binlog 组提交(group commit)机制,当有多个事务提交的时候,会将多个 binlog 刷盘操作合并成一个,从而减少磁盘 I/O 的次数。

MySQL 5.7 开始有 redo log 组提交。
引入了组提交机制后,prepare 阶段不变,只针对 commit 阶段,将 commit 阶段拆分为三个过程:

flush 阶段:多个事务按进入的顺序将 binlog 从 cache 写入文件(不刷盘);
sync 阶段:对 binlog 文件做 fsync 操作(多个事务的 binlog 合并一次刷盘);
commit 阶段:各个事务按顺序做 InnoDB commit 操作;
上面的每个阶段都有一个队列,每个阶段有锁进行保护,因此保证了事务写入的顺序,第一个进入队列的事务会成为 leader,领导所在队列的所有事务,全权负责整队的操作,完成后通知队内其他事务操作结束。

同一时刻只允许一组事务提交。

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

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

相关文章

传输层之TCP协议

学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您: 想系统/深入学习某技术知识点… 一个人摸索学习很难坚持,想组团高效学习… 想写博客但无从下手,急需…

【python、opencv】opencv仿射变换原理及代码实现

opencv仿射变换原理 仿射变换是opencv的基本知识点,主要目的是将原始图片经过仿射变换矩阵,平移、缩放、旋转成目标图像。用数学公式表示就是坐标转换。 其中x,y是原始图像坐标,u,v是变换后的图像坐标。将公式转换为…

大数据技术2:大数据处理流程

前言:下图是一个简化的大数据处理流程图,大数据处理的主要流程包括数据收集、数据存储、数据处理、数据应用等主要环节。 1.1 数据收集 大数据处理的第一步是数据的收集。现在的中大型项目通常采用微服务架构进行分布式部署,所以数据的采集需…

JFlash烧写单片机bin/hex文件

1,安装压 JLink_Windows_V660c,官网可下载; 2,打开刚刚安装的 J-Flash V6.60c 选择创建新工程“Create a new project”,然后点击StartJ-Flash 点击之后跳出Select device框,选择TI 选择TI后&#xff0c…

浅析CPU 空闲时在干嘛?

人空闲时会发呆会无聊,计算机呢? 假设你正在用计算机浏览网页,当网页加载完成后你开始阅读,此时你没有移动鼠标,没有敲击键盘,也没有网络通信,那么你的计算机此时在干嘛? 你的计算…

ISIS默认路由下发的各种机制

作者简介:大家好,我是Asshebaby,热爱网工,有网络方面不懂的可以加我一起探讨 :1125069544 个人主页:Asshebaby博客 当前专栏: 网络HCIP内容 特色专栏: 常见的项目配置 本文内容&am…

Python Appium Selenium 查杀进程的实用方法

一、前置说明 在自动化过程中,经常需要在命令行中执行一些操作,比如启动应用、查杀应用等,因此可以封装成一个CommandExecutor来专门处理这些事情。 二、操作步骤 # cmd_util.pyimport logging import os import platform import shutil i…

返回列表中满足指定条件的连续元素:只返回第一个不符合条件元素之前的各元素itertools.takewhile()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 返回列表中满足指定条件的连续元素: 只返回第一个不符合条件元素之前的各元素 itertools.takewhile() [太阳]选择题 请问以下代码输出的结果是? import itertools a …

富时中国A50指数查询方法详解

富时中国A50指数,是指衡量中国A股市场50家具有代表性的上市公司股票表现的指数。对于投资者来说,了解和查询A50指数的走势对于制定投资策略至关重要。那么,如何轻松地查询富时中国A50指数呢? 1. 百度搜索 百度搜索引擎是最简便的…

TCP通信

第二十一章 网络通信 本章节主要讲解的是TCP和UDP两种通信方式它们都有着自己的优点和缺点 这两种通讯方式不通的地方就是TCP是一对一通信 UDP是一对多的通信方式 接下来会一一讲解 TCP通信 TCP通信方式呢 主要的通讯方式是一对一的通讯方式,也有着优点和缺点…

手机升级到iOS15.8后无法在xcode(14.2)上真机调试

之前手机是iOS14.2的系统,在xcode上进行真机测试运行良好,因为想要使用Xcode的Instruments功能,今天将系统更新到了iOS15.8 ,结果崩了 说是Xcode和手机系统不兼容不能进行真机测试。在网上查了好些方法,靠谱的就是下载相关版本的…

spark sql基于RBO的优化

前言 这里只对RBO优化进行简单的讲解。讲解RBO之前必须对spark sql的执行计划做一个简单的介绍。 这个里讲解的不是很清楚,需要结合具体的执行计划来进行查看 1、执行计划 在spark sql的执行计划中,执行计划分为两大类,即逻辑执行计划、物…