一、异步复制原理

mysql-async-repl-arch.jpg

  • 主库有一个线程:binlog dump线程。

  • 从库有两个线程:IO线程、SQL线程。


1.异步复制原理

  • 用户对数据的修改进行提交,然后Master(主库)把所有数据库的变更写进binlog中,主库线程binlog dump把binlog内容推送给Slave(从库)。

    注意

    启动复制通道后,第一次是从库告诉主库要从哪个位置点或者gtid位执行,然后主库启动一个dump线程,向从库推数据。

  • 从库I/O线程读取主库上的binlog信息,并把binlog写到本地中继日志 (relay log)中。

  • 从库SQL线程读取并解析ralay log内容,按照主库中的提交顺序进行事务回放,写 入本地数据文件中,这样就实现了数据在主从实例之间的同步。

注意

主库在写入binlog并落盘之后,通知dump线程有新的 binlog 产生,并发送到从库中。然后主库并不理会从库是否接收到binlog,而是自顾自地照常进行事务的提交,如下图。

mysql_async_rep.png


2.异步复制过程

  • 从库通过执行change master to 语句连接主库,提供了连接的用户一切条件(user 、password、port、ip),并且让从库知道,二进制日志的起点位置(file名 position 号); 然后start slave启动从库线程。
  • 从库的IO线程和主库的dump线程建立连接。
  • 从库根据change master to 语句提供的file名和position号,IO线程向主库发起binlog的请求。
  • 主库dump线程根据从库的请求,将本地binlog以events的方式推送给从库IO线程。
  • 从库IO线程接收binlog events,并存放到本地relay-log中,传送过来的信息,会记录到master.info中
  • 从库SQL线程应用relay-log,并且把应用过的记录到relay-log.info中,默认情况下,已经应用过的relay 会自动被清理purge
master.info:用于保存从库的I/O线程连接主库的连接状态、账号、IP地址、端口、密码,以及I/O线程当前读取主库binlog的文件和位置 信息(称为I/O线程信息日志)。

relay-log.info:当从库的I/O线程从主库获取 到最新的binlog事件信息后会先写入从库本地的relay log中,然后SQL线程再去读取relay log解析并重放。relay-log.info就是用于记录最新的 relay log的文件和位置,以及SQL线程当前重放的事件对应的主库binlog的文件和位置信息 的(SQL线程位置被称为SQL线程信息日志)。

 

二、异步复制搭建

1.主库配置

1.1 修改主库配置文件

[root@db01 ~]# vim /etc/my.cnf
[mysqld]
skip_name_resolve = ON
socket = /tmp/mysql.sock
basedir = /usr/local/mysql
datadir = /usr/local/mysql/data
log_bin = mysql-bin #开启binlog
server_id = 1 #该参数在同一个复制架构中需要保持唯一

[client]
socket = /tmp/mysql.sock

1.2 在主库授权一个用户,用与从库连接主库‘

mysql> grant replication slave on *.* to rep@'172.16.1.%' identified by '123';

1.3 查看主库的binlog信息,从库连接主库时需要用到。

mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000002 | 326 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

2.从库配置

2.1 修改配置文件

[root@db02 ~]# vim /etc/my.cnf
[mysqld]
skip_name_resolve = ON
socket = /tmp/mysql.sock
basedir = /usr/local/mysql
datadir = /usr/local/mysql/data
server_id = 2 #该参数在同一个复制架构中需要保持唯一

[client]
socket = /tmp/mysql.sock

2.2 从主库的备份同步主库数据(为了保证主库、从库数据一致,可省略)

[root@db02 ~]# mysql -uroot -p123 < master.sql

2.3 登录从库,执行change master to 语句

mysql > change master to 
master_host='172.16.1.51',
master_port=3306,
master_user='rep',
master_password='123',
master_log_file='mysql-bin.000002',
master_log_pos=326;

2.4 启动从库线程

mysql> start slave;

2.4 查看从库IO线程和SQL线程是否正常

[root@db02 ~]# mysql -uroot -p123 -e "show slave status\G" | grep -Ew "Slave_(IO|SQL)_Running"
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

 

三、主从故障处理

1.IO线程故障

  • IO线程为Connecting状态
[root@db02 ~]# mysql -uroot -p123 -e "show slave status\G" | grep -Ew "Slave_IO_Running"
Slave_IO_Running: Connecting
  • IO线程为No状态
[root@db02 ~]# mysql -uroot -p123 -e "show slave status\G" | grep -Ew "Slave_IO_Running"
Slave_IO_Running: No

出现这2种状态的故障一般有以下几种原因:

1.从库与主库的网络不通

2.主库的mysql服务没有启动

3.从库在执行change master to语句时,配置有问题

4.从库在连接主库的数据库时,IP被反向解析成了主机名

5.开启了防火墙,并未对mysql服务、端口放行

2.SQL线程故障

  • SQL线程为No状态
[root@db02 ~]# mysql -uroot -p123 -e "show slave status\G" | grep -Ew "Slave_SQL_Running"
Slave_SQL_Running: No

SQL线程出现故障的原因一般都是主库和从库数据不一致:

1.主库有数据,从库没有
2.从库有数据,主库没有
3.主库与从库数据库结构不一致

SQL线程故障解决方法:

方法一:让从库跳过下一个event记录

1.停止从库SQL线程

mysql> stop slave sql_thread;

2.跳过下一个event记录(错误的event)

若有N个错的的events,可以跳过N个events

mysql> set global sql_slave_skip_counter=1;

3.启动SQL线程

mysql> start slave sql_thread;

方法二:跳过错误代码

[root@db02 ~]# vim /etc/my.cnf
[mysqld]
slave_skip_errors=1107,1032,1062

方法三:重新同步主库数据、重做从库(同步数据前,从库环境要纯净)

 

四、延时从库

1.对已经为从库的数据库做延时从库

  • 停止从库的IO、SQL线程
mysql> stop slave;
  • 执行change master to 语句,配置延迟从库
mysql> change master to master_delay=3600;
  • 启动从库线程
mysql> start slave;
  • 查看从库的状态
mysql> show slave status\G
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
SQL_Delay: 3600 #延时从库延时的时间
SQL_Remaining_Delay: NULL #执行语句倒计时,如果主库没有操作,为NULL

2.对新数据配置延迟从库

  • 修改配置文件
[root@db01 ~]# vim /etc/my.cnf
[mysqld]
server_id=3
  • 从主库的备份同步主库数据(为了保证主库、从库数据一致,可省略)
[root@db02 ~]# mysql -uroot -p123 < master.sql
  • 在从库执行change master to 语句,配置延迟从库
mysql > change master to 
master_host='172.16.1.51',
master_port=3306,
master_user='rep',
master_password='123',
master_log_file='mysql-bin.000002',
master_log_pos=326,
master_delay=3600;
  • 启动从库线程
mysql> start slave;
  • 查看从库的状态
mysql> show slave status\G
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
SQL_Delay: 3600 #延时从库延时的时间
SQL_Remaining_Delay: NULL #执行语句倒计时,如果主库没有操作,为NULL

 

五、过滤复制

过滤复制其实就是在从库或者主库,配置白名单、黑名单,只针对某些库、表进行复制。

1.过滤复制的方式

  • 白名单:只执行白名单中列出的库或者表的binlog

配置方法:

#只同步test1库
replicate_do_db=test1
#只同步test1库下面的tb1表
replicate_do_table=test1.tb1
#同步test1库下面的t开头的所有表(支持通配符)
replicate_wild_do_table=test.t*

#如果需要同步多个库,则可以用逗号隔开,如:test1,test2,test3
#也可以写多条配置。
  • 黑名单:不执行黑名单中列出的库或者表的binlog

配置方法:

#不同步test2库
replicate_ignore_db=test2
#不同步test2库下面的tb2表
replicate_ignore_table=test2.tb2
#不同步test2库下面的t开头的所有表(支持通配符)
replicate_wild_ignore_table=test2.t*

#如果需要指定多个库,则可以用逗号隔开,如:test1,test2,test3
#也可以写多条配置。

2.在主库配置过滤复制

在主库配置过滤复制后,从库的IO线程只会读取主库的白名单内或黑名单外的库或表的binlog(即:主库的dump线程只会推送白名单内或黑名单外的表库的binlog给从库)。

  • 给主库配置白名单
[root@db01 ~]# vim /etc/my.cnf
[mysqld]
replicate_do_db=yxlm

保存并重启数据库

  • 查看主库状态
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000007 | 120 | yxlm | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

在Binlog_Do_DB看到只同步yxlm库.

在数据中新建一些库、表

mysql> create table yxlm.hsmg(id int);
mysql> create database db_test1;
mysql> create database db_test2;
  • 去从库查看数据库信息

看到在主库执行的三条语句当中,只有第一条同步了过来。

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| test |
| yxlm |
+--------------------+
10 rows in set (0.00 sec)

mysql> use yxlm
mysql> show tables;
+----------------+
| Tables_in_yxlm |
+----------------+
| hsmg |
+----------------+
1 row in set (0.00 sec)

查看从库中继日志:

mysql_master_slave-1.png从中继日志中,确实发现只记录了create table yxlm.hsmg(id int)语句。

3.在从库配置过滤复制

在从库配置过滤复制后,从库的IO线程会读取主库的白名单内或黑名单外的所有库或表的binlog,但是从库的SQL线程只执行白名单内或黑名单外的所有库或表的binlog。

  • 在主库创建几个新库
mysql> create database jdqs;
mysql> create database lol;
  • 给从库配置白名单
[root@db02 ~]# vim /etc/my.cnf
[mysqld]
replicate_do_db=jdqs,lol

查看从库的主从状态:

mysql> show slave status\G
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB: jdqs,lol
  • 再次在主库创建一些库和表
mysql> create table jdqs.yafu(id int);
mysql> create table lol.cjzd(id int);
mysql> create database new_test1;
mysql> create database new_test2;

查看从库数据库信息

mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| jdqs |
| lol |
| mysql |
| performance_schema |
| test |
+--------------------+
11 rows in set (0.00 sec)

mysql> use lol
mysql> show tables;
+---------------+
| Tables_in_lol |
+---------------+
| hsmg |
+---------------+
1 row in set (0.00 sec)

mysql> use jdqs
mysql> show tables;
+----------------+
| Tables_in_jdqs |
+----------------+
| yafu |
+----------------+
1 row in set (0.00 sec)

从结果来看,new_test1库和new_test2库并没有创建。

在查看下从库的binlog日志:

mysql_master_slave-2.png

发现在主库执行的4条SQL,从库中继日志中都记录了下来。

但因为在从库设置了白名单,所以SQL线程只重写了前2行SQL命令。

4.过滤复制总结

  • 在主库配置时:

1.配置白名单:主库只将白名单配置的库相关语句记录到binlog
2.配置黑名单:主库只不记录黑名单配置的库相关语句到binlog

  • 在从库配置时:

1.配置白名单:IO线程将主库数据拿到relay-log,但是SQL线程只执行白名单设置的库相关语句
2.配置黑名单:IO线程将主库数据拿到relay-log,但是SQL线程不执行黑名单设置的库相关语句