MySQL 主从复制

247 阅读12分钟

一、什么是Mysql主从复制

MySQL主从复制是其最重要的功能之一。主从复制是指一台服务器充当主数据库服务器,另一台或多台服务器充当从数据库服务器,主服务器中的数据自动复制到从服务器之中。对于多级复制,数据库服务器即可充当主机,也可充当从机。MySQL主从复制的基础是主服务器对数据库修改记录二进制日志,从服务器通过主服务器的二进制日志自动执行更新。

image.png

主从复制的工作过程:

Master节点需要开启二进制日志,Slave节点需要开启中继日志。

(1)Master 节点将数据的改变记录成二进制日志(bin log) ,当Master上的数据发生改变时(增删改),则将其改变写入二进制日志中。

(2)Slave节点会在一定时间间隔内对Master的二进制日志进行探测其是否发生改变,如果发生改变,则开始一个I/O线程请求Master的二进制事件。(请求二进制数据)

(3)同时Master 节点为每个I/O线程启动一个dump线程,用于通知和向其发送二进制事件,I/O线程接收到bin-log内容后,将内容保存至slave节点本地的中继日志(Relay log)中,Slave节点将启动SQL线程从中继日志中读取二进制事件,在本地重放,即解析成sql 语句逐一执行,使得其数据和Master节点的保持一致。最后I/O线程和SQL线程将进入睡眠状态,等待下一次被唤醒。

记住两个日志和三个线程:

两个日志:二进制日志(bin log)中继日志(Relay log)

三个线程:I/O线程dump线程SQL线程

注意:

  • 中继日志通常会位于 OS 缓存中,所以中继日志的开销很小。
  • 复制过程有一个很重要的限制,即复制在 Slave上是串行化的,也就是说 Master上的并行更新操作不能在 Slave上并行操作。
  • 半同步复制,会多一个ack确认线程(ack collector thread),专门用于接收slave 的反馈信息(收集slave节点返回的ack信息)。

二、Mysq主从复制的类型

基于语句的复制:

主服务器上面执行的语句在从服务器上面再执行一遍,在MySQL-3.23版本以后支持。

存在的问题:时间上可能不完全同步造成偏差,执行语句的用户也可能是不同一个用户。

基于行的复制:

把主服务器上面改编后的内容直接复制过去,而不关心到底改变该内容是由哪条语句引发的,在MySQL-5.0版本以后引入。

存在的问题:比如一个工资表中有一万个用户,我们把每个用户的工资+1000,那么基于行的复制则要复制一万行的内容,由此造成的开销比较大,而基于语句的复制仅仅一条语句就可以了。

混合类型的复制:

MySQL默认使用基于语句的复制,当基于语句的复制会引发问题的时候就会使用基于行的复制,MySQL会自动进行选择。

在MySQL主从复制架构中,读操作可以在所有的服务器上面进行,而写操作只能在主服务器上面进行。主从复制架构虽然给读操作提供了扩展,可如果写操作也比较多的话(多台从服务器还要从主服务器上面同步数据),单主模型的复制中主服务器势必会成为性能瓶颈。

三、 mysql复制的优点

  • 如果主库出现问题,可以快速切换到从库提供服务
  • 可以在从库执行查询操作,降低主库的访问压力。
  • 可以在从库进行备份,以免备份期间影响主库的服务。

四、 Mysql复制解决的问题

  • 数据分布 (Data distribution )
  • 负载平衡(load balancing)
  • 数据备份(Backups) ,保证数据安全
  • 高可用性和容错行(High availability and failover)
  • 实现读写分离,缓解数据库压力,在开发工作中,有时候会遇见某个sql 语句需要锁表,导致暂时不能使用读的服务,这样就会影响现有业务,使用主从复制,让主库负责写,从库负责读,这样,即使主库出现了锁表的情景,通过读从库也可以保证业务的正常运作。

五、Mysql主从复制的过程

MySQL主从复制的两种情况:同步复制和异步复制,实际复制架构中大部分为异步复制。

MySQL 主从复制默认是 异步的模式。MySQL增删改操作会全部记录在 Binlog 中,当 slave 节点连接 master 时,会主动从 master 处获取最新的 Binlog 文件。并把 Binlog 存储到本地的 relay log 中,然后去执行 relay log 的更新内容。

 

 

relay log

这里又引申出一个新的日志概念。MySQL 进行主主复制或主从复制的时候会在要复制的服务器下面产生相应的 relay log。

relay log 是怎么产生的呢?

从服务器 I/O 线程将主服务器的 Binlog 日志读取过来,解析到各类 Events 之后记录到从服务器本地文件,这个文件就被称为 relay log。然后 SQL 线程会读取 relay log 日志的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致。中继日志充当缓冲区,这样 master 就不必等待 slave 执行完成才发送下一个事件。

六、MySQL 主从复制主流架构模型

一主一从 / 一主多从

多主一从

双主复制,互为主从

连级复制

七、MySQL 主从复制的模式

异步模式

异步模式如下图所示,这种模式下,主节点不会主动推送bin-log到从节点,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样就会有一个问题,主节点如果崩溃掉了,此时主节点上已经提交的事务可能并没有传到从节点上,如果此时,强行将从提升为主,可能导致新主节点上的数据不完整。

image.png

半同步模式

介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay-log中才返回成功信息给客户端(只能保证主库的bin-log至少传输到了一个从节点上,但并不能保证从节点将此事务执行更新到db中),否则需要等待直到超时时间然后切换成异步模式再提交。相对于异步复制,半同步复制提高了数据的安全性,一定程度的保证了数据能成功备份到从库,同时它也造成了一定程度的延迟,但是比全同步模式延迟要低,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。如下图所示:

image.png

全同步模式

指当主库执行完一个事务,然后所有的从库都复制了该事务并成功执行完才返回成功信息给客户端。因为需要等待所有从库执行完该事务才能返回成功信息,所以全同步复制的性能必然会收到严重的影响。

八、主从复制案例演示

实验环境: Master 服务器:192.168.10.22,mysql5.7

Slave1 服务器:192.168.10.33,mysql5.7

Slave2 服务器:192.168.10.44,mysql5.7

image.png

实验思路:

  1. 主从服务器时间同步。
  2. 配置主服务器,修改其配置文件,在主数据库中创建一个同步账号授权给从数据库使用。
  3. 配置从数据库,修改其配置文件,在从数据库中配置同步。
  4. 验证能否同步成功。

实验步骤:

每台服务器关闭防火墙和selinux 。

 systemctl disable --now firewalld
 setenforce 0

1669804659523.png

8.1 MYSQL主从服务器时间同步

时间同步:

  • 每台mysql服务器需要设置时间同步,以免数据同步时产生错乱。
  • 如果不通外网,则使用ntp服务。如果通外网,可以使用网络时钟源。

下面将演示主节点使用本地时钟源,从节点进行时间同步。

1)主服务器使用本地时钟源。

 #安装时间同步工具(本地设置时钟源)
 [root@mysql-1 ~]# yum install -y ntp
 •
 #修改ntp配置文件,在末尾加入
 [root@mysql-1 ~]# vim /etc/ntp.conf
 server 127.127.10.0              #设置本地是时钟源,注意修改为网段,若本机网段为192.168.10.0,则本地时钟源为127.127.10.0
 fudge 127.127.10.0 stratum 8     #设置时间层级为8(限制在15以内)
 •
 #开启ntpd
 [root@mysql-1 ~]# systemctl start ntpd

1669804901540.png

1669805208069.png

1669805271688.png

2)两台从服务器同步主服务器的时间。

 #安装时间同步工具
 yum install -y ntp
 •
 #和主服务器进行时间同步
 /usr/sbin/ntpdate 192.168.10.22
 •
 #设置定时任务,每30分钟同步一次
 crontab -e  
 */30 * * * * /usr/sbin/ntpdate 192.168.10.22
 •
  #开启ntpd
 systemctl start ntpd

1669805446147.png

1669818077345.png

8.2 主服务器的mysql配置

修改配置文件,创建一个同步账号授权给从数据库使用。

 [root@mysql-1 ~]# vim /etc/my.cnf
 [mysqld]
 ......
 server-id = 1               #指定服务ID号,master和两台slave都要不同
 log-bin=mysql-bin           #添加,主服务器开启二进制日志
 binlog_format = MIXED       #指定二进制日志(binlog)的记录格式为MIXED
 log-slave-updates=true      #添加,允许slave从master复制数据时可以写入到自己的二进制日志
 expire_logs_days = 7        #设置二进制日志文件过期时间,默认值为0,表示logs不过期
 max_binlog_size = 500M      #设置二进制日志限制大小,如果超出给定值,日志就会发生滚动,默认值是1GB

 #重启服务
 [root@mysql-1 ~]# systemctl restart mysqld

 [root@mysql-1 ~]# mysql -u root -p123456
 #给从服务器授权
 grant replication slave on *.* to 'myslave'@'192.168.10.%' identified by 'abc123';  
 flush privileges;               #刷新权限

 show master status;     #查看主服务器状态
 //显示如下
 +-------------------+----------+--------------+------------------+
 | File              | Position | Binlog_Do_DB | Binlog_Ignore_DB |
 +-------------------+----------+--------------+------------------+
 | mysql-bin.000006  |      603 |              |                  |
 +-------------------+----------+--------------+------------------+
 1 row in set (0.00 sec)
 #File 列显示日志名,Position 列显示偏移量

1669817298222.png

1669818613525.png

1669818822226.png

1669818962944.png

8.3 从服务器的mysql配置

Slave1 服务器:192.168.10.33,mysql5.7

Slave2 服务器:192.168.10.44,mysql5.7

 #修改配置文件
 [root@mysql-2 ~]# vim /etc/my.cnf
 [mysqld]
 ......
 server-id = 2            #修改,注意id与Master的不同,两个Slave的id也要不同
 relay-log=relay-log-bin  #添加,开启中继日志,从主服务器上同步日志文件记录到本地
 relay-log-index=slave-relay-bin.index   #添加,定义中继日志索引文件的位置和名称,一般和relay-log在同一目录
 relay_log_recovery = 1                  #选配项
 #当 slave 从库宕机后,假如 relay-log 损坏了,导致一部分中继日志没有处理,则自动放弃所有未执行的 relay-log,并且重新从 master 上获取日志,这样就保证了 relay-log 的完整性。默认情况下该功能是关闭的,将 relay_log_recovery 的值设置为 1 时, 可在 slave 从库上开启该功能,建议开启。
 •
 #重启服务
 [root@mysql-2 ~]# systemctl restart mysqld     
 •
 #登录数据库,进行同步设置
 [root@mysql-2 ~]# mysql -u root -p
 CHANGE master to master_host='192.168.10.22',master_user='myslave',master_password='abc123',master_log_file='mysql-bin.000001',master_log_pos=900;     
 #配置同步,注意 master_log_file 和 master_log_pos 的值要与Master查询的一致
 •
 start slave;                #启动同步,如有报错执行 reset slave;
 •
 show slave status\G         #查看 Slave 状态
 ##确保 IO 和 SQL 线程都是 Yes,代表同步正常。
 Slave_IO_Running: Yes               #负责与主机的IO通信
 Slave_SQL_Running: Yes              #负责自己的slave mysql进程
 •
 ##一般 "Slave_IO_Running: No" 的可能原因:
 1. 网络不通 
 2. my.cnf配置有问题(server-id重复)
 3. 密码、file文件名、pos偏移量不对 
 4. 防火墙没有关闭 

配置Slave1 服务器 :

1669819317447.png

1669819407607.png

1669819621464.png

1669819861112.png Slave2 服务器也是同样配置,注意配置文件中 server-id 要和前面两台不同,我这里设置为 "server-id =3"。

1669820095201.png

小贴士:

一般 "Slave_IO_Running: No" 的可能原因:

  1. 网络不通
  2. my.cnf配置有问题(server-id重复)
  3. 密码、file文件名、pos偏移量不对
  4. 防火墙没有关闭

8.4 验证主从复制效果

主服务器上创建新的数据库,执行 create database zhoujian;

去从服务器上查看是否同步成功,执行 show databases;

1669820297188.png

1669820389514.png

1669820509242.png

九、扩展

1、 数据库主从数据不一致解决方案

方法一:忽略错误后,继续同步

该方法适用于主从库数据相差不大,或者要求数据可以不完全统一的情况,数据要求不严格的情况。

方式二:重新做主从,完全同步

该方法适用于主从库数据相差较大,或者要求数据完全统一的情况。

2、mysql从服务器挂了 恢复后怎么保证数据同步?

  • 物理方法: rsync 磁盘文件同步。 使用文件恢复,主节点需要停服务。
  • 主从复制: 将从节点原有库删除,通过偏移量,重新做一次主从复制。

3、半同步复制什么情况下会降为异步复制?什么时候又会恢复同步复制?

  • 当半同步复制发生超时(由rpl_semi_sync_master_timeout 参数控制,默认为10000ms, 即10s),会暂时关闭半同步复制,转而使用异步复制,也就是会自动降为异步工作。
  • 当malster dump 线程发送完一个事务的所有事件之后,如果在rpl_ semi_sync_master_ timeout 内,收到了从库的响应,则主从又重新恢复为半同步复制。

4、MySQL主从复制延迟原因和优化方法

主从复制延迟原因:

  1. master服务器高并发,形成大量事务。
  1. 网络延迟。
  1. 主从硬件设备导致(cpu主频、内存IO、硬盘IO)。
  1. 是同步复制,而不是异步复制。

优化方法:

  • 从库优化Mysql参数。比如增大innodb_buffer_pool_size,让更多操作在Mysql内存中完成,减少磁盘操作。
  • 从库使用高性能主机。包括cpu强悍、内存加大。避免使用虚拟云主机,使用物理主机,这样提升了I/O方面性。
  • 从库使用SSD磁盘。
  • 网络优化,避免跨机房实现同步。

5、注意事项

  1. 每个master可以有多个slave。
  1. 每个slave只能有一个master。
  1. 每个slave只能有一个唯一的服务器ID(server-id)。
  2. master一定要开启binlog二进制日志功能;通常为了数据安全,slave也开启binlog功能。