学而实习之 不亦乐乎

MySQL 主从复制的概念、原理

2024-04-20 21:06:25

一、主从复制

主从复制是指将主库的 DDL 和 DML 操作通过二进制日志传到从库服务器中,然后在从库上对这些日志重新执行(也叫重做),从而使得从库和主库的数据保持一致。

MySQL 支持一台主库同时向多台从库进行复制,从库同时也可以作为其他从服务器的主库,实现链状复制。

MySQL 复制的优点,主要包含以下三个方面:

  1. 实时灾备,用于故障切换:主库出现问题,可以快速切换到从库提供服务。
  2. 读写分离,提供查询服务:实现读写分离,降低主库的访问压力。
  3. 备份,避免影响业务:可以在从库中执行备份,以避免备份期间影响主库服务。

二、主从复制应用场景

  • 负载均衡(load balancing):主库压力较大扛不住时,可通过主从复制把读压力分担一部分到从库上;
  • 业务高峰数据库连接数太多时,例如高峰的认证,只需要把这个业务分拆到从库上;
  • 低频复杂类且难以优化的 SQL(报表统计分析类的 SQL),专门在从库上执行;
  • 高可用性(high availability)和容错:利用从库做高可用切换。

三、主从复制原理

主从复制原理图示

>>>

主从复制的过程原理可分为三个部分:

  1. 主库汇报数据变更记录在二进制日志文件 binlog 中:
    • 启动(Slave I/O Thread->master.info;Slave SQL Thread,Relay-log.info);
    • 建立连接(slave I/O Thread->master.info;Master log Dump Thread->binlog )。
  2. 从库连接主库,读取 binlog 日志,并写入自身中继日志 Relay log:
    • 获取更新日志(Master log dump Thread->binlog);
    • 传递更新日志(Master log dump Thread->binlog;slave I/O Thread->master.info);
    • 保存更新日志(slave I/O Thread->master.info;slave Relay log)。
  3. slave 重做中继日志,将改变反映它自身的数据:应用中继日志(Slave SQL Thread;relay-log.info;slave Relay Log)。

四、MySQL主从复制工作方式

MySQL主从复制涉及到三个线程,一个运行在主节点(log dump thread),其余两个(I/O thread,SQL thread)运行在从节点,如下图所示:

1、主节点 binary log dump 线程

当从节点连接主节点时,主节点会创建一个log dump线程,用于发送bin-log的内容。在读取bin-log中的操作时,此线程会对主节点的bin-log加锁,当读取完成,甚至在发送给从节点之前,锁会被释放。

2、从节点I/O线程

当从节点上执行‘start slave’命令之后,从节点会创建一个I/O线程用来连接主节点,请求主库中更新的bin-log。I/O线程接收到主节点binlog dump 进程发来的更新之后,保存在本地relay-log中。 

3、从节点SQL线程

SQL线程负责读取relay log中的内容,解析成具体的操作并执行,最终保证主从数据的一致性。

对于每一个主从连接,都需要三个进程来完成。当主节点有多个从节点时,主节点会为每一个当前连接的从节点建一个binary log dump 进程,而每个从节点都有自己的I/O进程,SQL进程。从节点用两个线程将从主库拉取更新和执行分成独立的任务,这样在执行同步数据任务的时候,不会降低读操作的性能。比如,如果从节点没有运行,此时I/O进程可以很快从主节点获取更新,尽管SQL进程还没有执行。如果在SQL进程执行之前从节点服务停止,至少I/O进程已经从主节点拉取到了最新的变更并且保存在本地relay日志中,当服务再次起来之后,就可以完成数据的同步。

要实施复制,首先必须打开Master 端的binary log(bin-log)功能,否则无法实现。工作流程如下:

  1. master 记录二进制日志。在每个事务完成数据更新之前,master 在二进制日志记录这些改变。MySQL 将事务串行地写入二进制日志,完成后,master 通知存储引擎提交事务。
  2. slave 将 master 的 binary log 拷贝到它自身的中继日志:
    • a) 首先,slave 开始一个工作线程:I/O 线程;
    • b) I/O 线程在 master 上打开一个普通的连接,然后开始 binlog dump process。binlog dump process 从 master 的二进制日志中读取事件(接收的单位是 event),如果已经跟上 master,它会睡眠并等待 master 产生新的事件;
    • c) I/O 线程将这些事件写入中继日志。
  3. SQL slave 重做中继日志
    • a) Thread(SQL 从线程)是处理该过程的最后一步。SQL 线程从中继日志读取事件,并重放其中的事件(回放的单位也是 event)更新 slave 的数据,使其与 master 中的数据一致。只要该线程与 I/O 线程保持一致,中继日志通常会位于 OS 的缓存中,所以中继日志的开销很小;
    • b) 在 master 中也有一个工作线程:和其他 MySQL 的连接一样,slave 在 master 中打开一个连接也会使得 master 开始一个线程。

五、主从复制分类及形式

1.主从复制分类

主从复制可分为异步复制、同步复制和半同步复制三种:

异步复制

异步复制为 MySQL 默认的复制模式,指主库写 binlog、从库 I/O 线程读 binlog 并写入 relaylog、从库 SQL 线程重放事务这三步之间是异步的。

异步复制的主库不需要关心备库的状态,主库不保证事务被传输到从库,如果主库崩溃,某些事务可能还未发送到从库,切换后可能导致事务的丢失。其优点是可以有更高的吞吐量,缺点是不能保持数据实时一致,不适合要求主从数据一致性要求较高的应用场景。

同步复制

同步复制的模式下,主库在提交事务前,必须确认事务在所有的备库上都已经完成提交。即主库是最后一个提交的,在提交前需要将事务传递给从库并完成重放、提交等一系列动作。其优点是任何时候主备库都是一致的,主库的崩溃不会丢失事务,缺点是由于主库需要等待备库先提交事务,吞吐量很低。

半同步复制

MySQL 从 5.5 版本开始引入了半同步复制,半同步复制介于异步复制和同步复制之间。主库在提交事务时先等待,必须确认至少一个从库收到了事件(从库将事件写入 relaylog,不需要重放和提交,并向主库发送一个确认信息 ACK),主库收到确认信息后才会正式 commit。

与同步复制相比,半同步复制速度快很多,因为它只需要至少1个从库确认写入 relaylog,并不需要完成在从库上的事务提交,同时又比异步复制更安全,因为主库在提交时,事务至少已经存在2个地方(主库的 binlog 和从库的 relaylog)。

由于半同步复制在提交事务前,需要从库返还确认信息,所以这里涉及到网络的往返通信开销,因此半同步复制只适合在网络条件较好且地理上距离不远的环境部署,否则可能会因为网络延迟大幅降低主库性能。

2、主从复制形式

  1. 一主一从
  2. 一主多从
    • 一主一从和一主多从是最常见的主从架构,实施起来简单并且有效,不仅可以实现HA,而且还能读写分离,进而提升集群的并发能力。
  3. 多主一从 (从5.7开始支持)
    • 多主一从可以将多个mysql数据库备份到一台存储性能比较好的服务器上。
  4. 双主复制
    • 双主复制,也就是互做主从复制,每个master既是master,又是另外一台服务器的slave。这样任何一方所做的变更,都会通过复制应用到另外一方的数据库中。
  5. 级联复制
    • 级联复制模式下,部分slave的数据同步不连接主节点,而是连接从节点。因为如果主节点有太多的从节点,就会损耗一部分性能用于replication,那么我们可以让3~5个从节点连接主节点,其它从节点作为二级或者三级与从节点连接,这样不仅可以缓解主节点的压力,并且对数据一致性没有负面影响。

六、GTID 技术

GTID(Global Transaction Identifiers)是已提交事务的一个编号,是事务的全局唯一编号。GTID 和事务会记录到 binlog 中,用来标识事务。GTID 用于替代以前传统复制方法(binglog+position),在MySQL 5.6.2 开始支持 GTID。

MySQL 支持 GTID 后,一个事务在集群中就不再孤单,在每一个节点中,如果存在具有相同标识符的情况,可以避免同一个事务,在同一节点中出现多次的情况。

GTID 的出现,最直接的效果就是,每一个事务在集群中具有了唯一性的意义,相对于行复制来讲,数据安全性更高,故障切换更简单。

GTID 由 server_uuid、sequence_Number 组成,其中:

  • server_uuid:是 MySQL 实例的全局唯一标识;存放在$datadir/auto.cnf。
  • sequence_Number:是 MySQL 内部的事务编号,在一个 MySQL 实例中不会重复。序列号(保证服务器内唯一),也表示该实例上已经提交事务的数量,并且随着事务提交而递增。

举个例子:server_uuid 存放于“ cat /mysql/data/3306/data/auto.cnf ”,GTID样例如下:

[auto]server_uuid:1599dcea-5a7b-11e8-94bd-000c292834bo

根据 GTID 可以知道事务最初是在哪个实例上提交的,方便故障排查和切换。

小知识:GTID 只能基于 binlog_ROW 格式。

bin_log 二进制日志格式有以下3种:

  1. 基于语句记录,对应的参数值为 statement。二进制日志文件中保存的是执行过的 SQL 语句,存储日志量最小
  2. 基于行格式记录(Row-Based Logging,RBL),对应的参数值为 row。二进制日志文件中记录的是变更的行的信息,存储日志量大,但是无法直接地进行读取。
  3. 混合记录模式,对应的参数值为 mixed。在记录事件到二进制日志时,MySQL 服务根据需要,动态修改日志记录的格式。对于不确定的操作使用 row 记录,如果每天数据操作量很大,产生的日志比较多,可以考虑选择使用 mixed 格式。

GTID优缺点

优点:可以很方便地进行故障转移,从库不会丢失主库上的任何修改。

缺点:故障处理比较复杂,对执行的SQL有一定限制。以下三类语句不被执行:

a) create table ... select statements

b) create temporary table or drop temporary table statements inside transactions

c) 同时更新事务引擎和非事务引擎