主要内容
MySQL主从复制
MySQL可以通过两种方式配置主从复制,一种是通过二进制日志(binary log)的方式;另一种是通过GTID(全局事务ID)方式,不过GTID方式仍然依赖MySQL的binary log。
通过binary log配置主从
binary log的核心是事件(event)。基本原理是:主服务器把所有对数据库的操作(例如update、delete,create等)作为一个事件,当有事件产生,就把它们写入到对应的binary log中,每个事件都对应一个位置(可以理解这个位置就是事件的编号)。然后从服务器读取主服务器中的日志文件来获取主服务产生的事件(把这些日志保存到从服务器本地的relay-log中),从而把主服务的数据库操作在从服务器中重复执行一次,达到数据复制的目的。
由于一切操作都是基于binary log,因此主服务器必须开启log-bin选项,另外,主从服务器都必须分配一个唯一的server-id。
binary log方式实战
master服务器配置步骤
- 为master分配一个唯一id(server-id)
- 开启log-bin选项
- 重启服务器
- 创建一个专门用于复制的用户(非必需,但更安全)
- 锁表,禁止数据库写入数据
- 获取bin log日志的名称和位置
- slave创建成功,解锁master上的表格(unlock tables)
修改master服务器配置文件(/etc/mysql/mysql.conf.d/mysqld.cnf)
1 2 3 4 5 6 |
[mysqld] log-bin = mysql.bin #binary log日志的前缀名称,可随意 server-id = 1 bind-address = 0.0.0.0 # 让mysql接收外部网络连接 .... |
由于MySQL默认只监听127.0.0.1
,因此无法被外部网络连接,因此要把它改为0.0.0.0
。
修改完后一定要先重启服务器,否则后面的步骤无法顺利进行
创建一个专门用于复制,且没有其他权限的用户
1 2 3 |
mysql> create user 'repl'@'%' identified by 'repl'; #创建用户 mysql> grant replication slave on *.* to 'repl'@'%'; #只授予复制权限 |
接下来就要锁表,禁止数据库被修改,目的是获取当前数据库的日志名称和事件位置
1 2 |
mysql> flush tables with read lock; |
输入命令后不能退出MySQL,以保持锁的状态。
数据库锁定后,接下来就是最重要的一步,获取日志的名称和位置信息
1 2 3 4 5 6 7 8 9 |
mysql> show master status\G *************************** 1. row *************************** File: mysql-bin.000002 Position: 334 Binlog_Do_DB: Binlog_Ignore_DB: Executed_Gtid_Set: 1 row in set (0.00 sec) |
master服务准备完毕,下面就是配置slave服务器。
slave服务器配置步骤
- 和master服务器一样,slave服务器也要分配一个唯一的id。
- 重启服务器
- 设置主服务器的相关信息
- 开启slave工作线程
- 检查slave服务器的状态
修改slave服务器配置文件(/etc/mysql/mysql.conf.d/mysqld.cnf)
1 2 3 |
[mysqld] server-id = 2 |
和master服务器相比,slave服务器目前只需要添加一个唯一的id即可。
重启服务器
重启后就可以把主服务器的相关信息设置给从服务器
1 2 3 4 5 6 |
mysql> change master to MASTER_HOST='192.168.88.204',\ MASTER_USER=’repl',\ MASTER_PASSWORD='repl',\ MASTER_LOG_FILE='mysql-bin.000002',\ #主服务配置最后一步获得的信息 MASTER_LOG_POS=334; #同样是主服务器配置最后一步获得的信息 |
准备好后就可以开启从服务器
1 2 |
mysql> start slave; |
顺利的话,主从复制到这一步就完成了,可以通过下面的命令查看从服务器是否正常工作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.88.204 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000002 Read_Master_Log_Pos: 334 Relay_Log_File: ubunt-host1-relay-bin.000002 Relay_Log_Pos: 500 Relay_Master_Log_File: mysql-bin.000002 Slave_IO_Running: Yes Slave_SQL_Running: Yes ... Last_Errno: 0 Last_Error: ... Master_Server_Id: 1 Master_UUID: bd9480c5-6f83-11e8-a486-000c29e23e48 Master_Info_File: /var/lib/mysql/master.info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates ... 1 row in set (0.00 sec) |
正常的输出不止这些,由于内容太多,上面省略了部分输出
通过GTID配置主从
GTID实际上是对传统基于binary log的复制进行了增强。在binary log复制方式中,我们必须手动跟踪主服务器的日志名称和位置;但在GTID工作方式下面,我们无需跟踪这两个值,取而代之的是由MySQL自动跟踪它们,并使用GTID来标记哪些事务已经被处理,哪些还没有被处理。GTID的一个优点是:相同GTID的事务不会被重复处理,好处是可以最大限度地确保数据的一致性。
GTID由两部分构成:一部分是服务器的UUID,另一部分是事务ID;如果服务器的UUID是bd9480c5-6f83-11e8-a486-000c29e23e48
,那么它和第1个事务组成的GTID如下:
1 2 |
bd9480c5-6f83-11e8-a486-000c29e23e48:1 |
在GTID方式配置过程中,主从服务器都应该确保开启了log-bin选项,还有另外两个新增选项分别是:gtid-mode
和enforce-gtid-consistency
。
GTID方式实战
GTID复制的配置步骤如下:
- 为master和slave服务器分配唯一的server id
- master和slave都要开启log-bin选项
- master和slave都要开启gtid-mode和enforce-gtid-consistency选项
- slave服务器开启日志自动跟踪
修改主从服务器的配置文件,配置如下(主从配置相同):
1 2 3 4 5 6 7 |
[mysqld] server-id=1 #除了server-id不同外,其他配置主从都相同 log-bin=mysql-bin gtid-mode = on enforce-gtid-consistency = on |
重启服务器
重启后登入slave服务器,修改change master to的内容如下:
注意:执行下面的命令前先确保slave线程是停止的,如果当前slave线程正在运行,先使用stop slave
停止该线程。
1 2 3 4 5 |
mysql> change master to MASTER_HOST='192.168.88.204',\ MASTER_USER=’repl',\ MASTER_PASSWORD='repl',\ MASTER_AUTO_POSITION=1; #自动跟踪日志名称和位置 |
上面的命令是为了示例的完整性,如果使用GITD配置之前,两台服务器已经是传统的binlog主从服务器,则命令可以更简单:
1 2 |
mysql> change master to MASTER_AUTO_POSITION=1; #自动跟踪日志名称和位置 |
测试配置是否成功
配置完毕,我们可以往主数据库创建一些测试数据,之后再运行show master status
查看结果:
1 2 3 4 5 6 7 8 9 |
mysql> show master status\G *************************** 1. row *************************** File: mysql-bin.000003 Position: 340 Binlog_Do_DB: Binlog_Ignore_DB: Executed_Gtid_Set: bd9480c5-6f83-11e8-a486-000c29e23e48:1 1 row in set (0.00 sec) |
你会发现Executed_Gtid_Set
多了一个值:bd9480c5-6f83-11e8-a486-000c29e23e48:1,这个值表示这个事务是当前的一个事务。
同样,登入从服务器运行show slave status
观察结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.168.88.204 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000003 Read_Master_Log_Pos: 340 Relay_Log_File: ubunt-host1-relay-bin.000002 Relay_Log_Pos: 553 Relay_Master_Log_File: mysql-bin.000003 Slave_IO_Running: Yes Slave_SQL_Running: Yes ... Last_Errno: 0 Last_Error: ... Master_Server_Id: 1 Master_UUID: bd9480c5-6f83-11e8-a486-000c29e23e48 Master_Info_File: /var/lib/mysql/master.info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates ... Retrieved_Gtid_Set: bd9480c5-6f83-11e8-a486-000c29e23e48:1 Executed_Gtid_Set: bd9480c5-6f83-11e8-a486-000c29e23e48:1 Auto_Position: 1 Replicate_Rewrite_DB: Channel_Name: Master_TLS_Version: 1 row in set (0.00 sec) |
和之前相比,从服务器的输出信息也有点不一样了
- Retrieved_Gtid_Set和Executed_Gtid_Set都填充了值
- Auto_Position变成了1
从以上结果可以看到,我们的主从服务器已经切换到了GTID工作模式。
一些注意事项和问题
添加更多从服务器
有时候我们希望配置多台从服务器。如果是新建立的主从环境,没有什么数据,那么无论多少从服务器,搭建方式和上面说的没有什么两样。重点是,如果在已经运行了一段时间的主从环境中添加更多的从服务器,那么这台从服务器的配置就有点不一样。原因是:在主从配置的过程中,我们需要对服务器进行锁表操作,也就是说,任何数据都不可以继续写入,且持续的时间有可能会很长,在生产环境下,基本上是不允许的。
也就是说,我们不能在主服务器上下手,相反,我们应该在已经存在的从服务器这边下手。基本过程如下:
- 停止旧slave服务器的MySQL服务
- 把旧的slave服务器上的数据复制到新的从服务器里
- 为新服务器分配唯一的server-id和开启其他选项
- 重启服务器
详细步骤
第一步:我们需要停止旧的slave服务器的MySQL
1 2 |
root@slave1:~# mysqladmin -u root -p shutdown |
第二步:把旧从库的数据复制到新的从库中
可以有多种方式复制数据,例如先使用mysqldump备份旧的数据,然后在新库中还原,也可以直接打包原始数据,然后复制到新库的对应目录下面。这里我选择了后者:
1 2 3 4 5 6 |
root@slave1:~# cd /var/lib/mysql root@slave1:/var/lib/mysql# ls auto.cnf ibdata1 master.info mysql-bin.index replication slave1-relay-bin.000002 debian-5.7.flag ib_logfile0 mysql performance_schema sys slave1-relay-bin.index ib_buffer_pool ib_logfile1 mysql-bin.000001 relay-log.info slave1-relay-bin.000001 |
接下来使用tar命令对这些数据进行打包操作:
1 2 |
root@slave1:/var/lib/mysql# tar -zcvf data.tar.gz2 . |
打包完成后,我们就可以把数据复制到新的从库里面,这里我选择了使用scp命令,如果数据非常大的话请选择其他方案。
1 2 3 4 5 6 7 8 |
root@slave1:/var/lib/mysql# scp data.tar.gz2 root@192.168.88.217:/var/lib/mysql/ #复制数据到远程主机的/var/lib/mysql目录 The authenticity of host '192.168.88.217 (192.168.88.217)' can't be established. ECDSA key fingerprint is SHA256:iAoWxPJ22h3p9T9grTgcC5lqpB1Q1Tw7wjznkUab0HI. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added '192.168.88.217' (ECDSA) to the list of known hosts. root@192.168.88.217's password: data.tar.gz2 100% 1443KB 1.4MB/s 00:00 |
复制完后到新服务器中解压文件
1 2 3 4 |
root@slave2:~# cd /var/lib/mysql root@slave2:/var/lib/mysql# ls auto.cnf data.tar.gz2 debian-5.7.flag ib_buffer_pool ibdata1 ib_logfile0 ib_logfile1 ibtmp1 mysql performance_schema sys |
可以见到,文件已经传输过来了,把它们解压:
1 2 |
root@slave2:/var/lib/mysql# tar -zxf data.tar.gz2 |
使用ls命令查看:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
root@ubuntu-host2:/var/lib/mysql# ls -l total 124384 -rw-r----- 1 mysql mysql 56 Jun 13 23:37 auto.cnf -rw-r--r-- 1 root root 1477311 Jun 16 03:47 data.tar.gz2 -rw-r--r-- 1 root root 0 Jun 13 23:37 debian-5.7.flag -rw-r----- 1 mysql mysql 298 Jun 16 03:35 ib_buffer_pool -rw-r----- 1 mysql mysql 12582912 Jun 16 03:35 ibdata1 -rw-r----- 1 mysql mysql 50331648 Jun 16 03:35 ib_logfile0 -rw-r----- 1 mysql mysql 50331648 Jun 13 23:37 ib_logfile1 -rw-r----- 1 mysql mysql 12582912 Jun 15 20:57 ibtmp1 -rw-r----- 1 mysql mysql 125 Jun 16 03:35 master.info drwxr-x--- 2 mysql mysql 4096 Jun 13 23:37 mysql -rw-r----- 1 mysql mysql 177 Jun 16 03:35 mysql-bin.000001 -rw-r----- 1 mysql mysql 19 Jun 16 02:57 mysql-bin.index drwxr-x--- 2 mysql mysql 4096 Jun 13 23:37 performance_schema -rw-r----- 1 mysql mysql 65 Jun 16 03:35 relay-log.info drwxr-x--- 2 mysql mysql 4096 Jun 16 03:10 replication drwxr-x--- 2 mysql mysql 12288 Jun 13 23:37 sys -rw-r----- 1 mysql mysql 213 Jun 16 03:00 slave1-relay-bin.000001 -rw-r----- 1 mysql mysql 576 Jun 16 03:35 slave1-relay-bin.000002 -rw-r----- 1 mysql mysql 62 Jun 16 03:00 slave1-relay-bin.index |
数据都过来了。
第三步:配置mysqld.cnf文件
1 2 3 4 5 |
[mysqld] server-id = 3 gtid-mode = on enforce-gtid-consistency = on |
重启服务器
发现异常
异常1
在新的服务器使用show slave status
查看结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Master_Host: 192.168.88.204 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000003 Read_Master_Log_Pos: 340 Relay_Log_File: slave1-relay-bin.000002 Relay_Log_Pos: 553 Relay_Master_Log_File: mysql-bin.000003 Slave_IO_Running: No Slave_SQL_Running: No ... Last_Errno: 1872 Last_Error: Slave failed to initialize relay log info structure from the repository Skip_Counter: 0 ... Last_SQL_Errno: 1872 Last_SQL_Error: Slave failed to initialize relay log info structure from the repository Replicate_Ignore_Server_Ids: Master_Server_Id: 0 Master_UUID: bd9480c5-6f83-11e8-a486-000c29e23e48 Master_Info_File: /var/lib/mysql/master.info |
虽然MySQL服务可以正常运作,但slave线程却没有如我们期待的正常开启,而且还发现了错误:
1 2 3 |
Last_Errno: 1872 Last_Error: Slave failed to initialize relay log info structure from the repository |
通过查看MySQL的日志文件,发现有类似下面的错误信息:
/var/log/mysql/error.log日志文件部分输出
1 2 3 4 |
2018-06-16T07:54:49.511960Z 0 [Warning] Neither --relay-log nor --relay-log-index were used; so replication may break when this MySQL server acts as a slave and has his hostname changed!! Please use '--relay-log=slave2-relay-bin' to avoid this problem. 2018-06-16T07:54:49.526580Z 0 [ERROR] Failed to open the relay log './slave1-relay-bin.000002' (relay_log_pos 553). 2018-06-16T07:54:49.526659Z 0 [ERROR] Could not find target log file mentioned in relay log info in the index file './slave2-relay-bin.index' during relay log initialization. |
原因在于,默认情况下,slave线程会使用主机名-relay-bin
的方式保存日志文件, 而新库的所有数据文件都是由旧库那里复制过来的,包括从库的一些配置文件,这就导致配置信息中的主机名冲突。
解决办法一:
解决办法其实错误日志中已经给出,就是使用–relay-log选项明确指定relay-log的名称
修改mysqld.cnf,添加relay-log选项:
1 2 3 4 5 6 |
[mysqld] server-id = 3 gtid-mode = on enforce-gtid-consistency = on relay-log = slave1-releay-bin #使用旧主机的relay-log |
解决办法二:
当然,也可以不添加relay-log选项,解决办法如下:
- 把旧slave的slave1-relay-bin.index中的内容追加到新slave的slave2-relay-bin.index文件中
- 重启新slaveMySQL服务
具体实现
1 2 3 4 5 6 7 |
root@slave2:/var/lib/mysql# ls ai ib_buffer_pool ibtmp1 mysql-bin.index sys slave2-relay-bin.000001 auto.cnf ibdata1 master.info performance_schema slave1-relay-bin.000005 slave2-relay-bin.index data.tar.gz2 ib_logfile0 mysql relay-log.info slave1-relay-bin.000006 debian-5.7.flag ib_logfile1 mysql-bin.000001 replication slave1-relay-bin.index root@slave2:/var/lib/mysql# cat slave1-relay-bin.index >> slave2-relay-bin.index |
异常2
修改后重启服务器,再次使用show slave status
检查,发现另一个问题,错误如下:
1 2 |
Last_IO_Error: Got fatal error 1236 from master when reading data from binary log: 'A slave with the same server_uuid/server_id as this slave has connected to the master; the first event '' at 4, the last event read from './mysql-bin.000003' at 493, the last byte read from './mysql-bin.000003' at 493.' |
意思是已经有相同uuid的服务器连接到master,造成这种错误的原因和上面的一样。服务器的uuid其实是保存在数据目录的auto.cnf
文件里面,在复制数据到新slave的时候,把这个auto.cnf
文件也复制到新的slave中,因此,两个slave的uuid就发生了冲突。
解决办法
如果MySQL发现没有auto.cnf文件,就会重新生成一个,也就是说,这要把发生冲突的auto.cnf文件删除,服务器就会生成一个新的uuid。所以,只要把这个文件删除,然后重启服务器就OK了。
全部问题都解决之后,在master中运行如下命令:
1 2 3 4 5 6 7 8 9 |
mysql> show slave hosts; +-----------+------+------+-----------+--------------------------------------+ | Server_id | Host | Port | Master_id | Slave_UUID | +-----------+------+------+-----------+--------------------------------------+ | 3 | | 3306 | 1 | d77f4132-7142-11e8-b21c-000c29c59da1 | | 2 | | 3306 | 1 | 46108417-6f84-11e8-a559-000c29c7aaa0 | +-----------+------+------+-----------+--------------------------------------+ 2 rows in set (0.00 sec) |
两个slave都被master识别出来。
转载请注明:Pure nonsense » MySQL主从复制配置