0%

基于Docker的MySql 主从复制搭建

引言

MySQL从3.23版本开始提供复制功能,复制主要是指将主数据库上的DDL、DML操作通过日志(Binary Log)的形式传送到从数据库,然后在从数据库上对这些操作再次执行,从而实现从服务器与主服务器的数据同步。

复制过程概述

  • 主库将数据变更的操作作为事件记录在二进制日志(Binary Log)
  • 主库推送二进制日志(Binary Log)的信息到从库的中继日志(Relay Log)文件中
  • 从库根据中继日志(Relay Log)的内容更新数据库

原理图

系统环境

  • CentOS 7.6
  • Docker 18.09.6
  • MySql 5.6.46

搭建过程

创建MySql主、从容器

Docker如何安装MySql可以参考:Docker安装MySql

Master(主):

1
docker run -p 4001:3306 --name mysql-master -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6.46

Slave(从):

1
docker run -p 4002:3306 --name mysql-slave -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.6.46

配置Master(主)

设置server-id,打开 Binary Log

将配置文件拷贝出来(注:不同镜像版本的配置文件路径可能不同)

1
docker cp mysql-master:/etc/mysql/mysql.conf.d/mysqld.cnf mysqld-master.cnf 

在配置文件中添加如下内容:

1
2
3
4
5
[mysqld]
# 设置server_id,注意要唯一
server-id=1
# 开启二进制日志功能,名字可以随便取
log-bin=mysql-bin

然后将配置文件拷贝回去:

1
docker cp mysqld-master.cnf mysql-master:/etc/mysql/mysql.conf.d/mysqld.cnf

重启容器使配置生效

1
docker restart mysql-master

创建同步账户

登入主数据库的MySql

1
docker exec -it mysql-master /usr/bin/mysql -uroot -p123456

然后在MySql中分别执行如下命令创建同步用户

1
2
3
mysql> CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';
mysql> flush privileges;

其中slave为账户名,123456为密码。

锁定主数据库

锁定主数据库,只允许读取不允许写入,这样做的目的是为了防止搭建过程中有新数据插入,导致主、从数据库中的数据不一致。

1
mysql> flush tables with read lock;

随便插入或更新一条数据,如果有如下提示说明锁库成功

1
ERROR 1223 (HY000): Can't execute the query because you have a conflicting read lock

查看主数据库状态

在Master的MySql执行show master status;

1
2
3
4
5
6
7
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 | 512 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)

FilePosition字段的值后面将会用到,在后面的操作完成之前,需要保证Master库不能做任何操作,否则将会引起FilePosition字段的值变化。

配置Slave(从)

将配置文件拷贝出来

1
docker cp mysql-slave:/etc/mysql/mysql.conf.d/mysqld.cnf mysqld-slave.cnf

在配置文件中添加如下内容:

1
2
3
4
5
[mysqld]
# 设置server_id,注意要唯一
server-id=2
# 配置中继日志
relay_log=mysql-relay-bin

然后将配置文件拷贝回去:

1
docker cp mysqld-slave.cnf mysql-slave:/etc/mysql/mysql.conf.d/mysqld.cnf

重启容器使配置生效

1
docker restart mysql-slave

主库已有数据的处理(如无数据可跳过这步)

在实际情况下,主库往往已经是在生产环境跑了一段时间,里面已经有许多数据,如果这些数据不能丢弃,那么我们需要先将这部分数据导入到从库中然后再认主。

备份主数据库

要确保在开始备份之前主数据库已经锁定,防止备份过程中数据发生变化,下面的备份命令仅供参考,只要是能正确通过mysqldump备份出主数据的数据就行

1
docker exec mysql-master /usr/bin/mysqldump -uroot -p123456 -A > backup.sql

-A 表示要备份所有数据库,mysqldump具体用法可以参考 mysqldump
mysqldump在容器中的路径可以通过在容器中执行whereis mysqldump确定

将备份数据导入到从数据库

将主数据库的备份数据拷贝到从数据库的容器中

1
docker cp backup.sql mysql-slave:/tmp/

登录从数据库的MySql

1
docker exec -it mysql-slave /usr/bin/mysql -uroot -p123456

在MySql中执行如下命令来恢复数据

1
source /tmp/backup.sql

如果在备份主数据库的时候指定了数据库(比如指定了test数据库),那么导入数据的时候可能会报错

1
2
Warning: Using a password on the command line interface can be insecure.
ERROR 1049 (42000): Unknown database 'test'

这是因为从数据库没有名字为test的数据库,我们需要创建一个,登入从数据库的MySql,执行命令CREATE DATABASE test;,然后我们再次导入数据即可成功

获取主数据库的IP地址

命令如下

1
docker inspect --format='{{.NetworkSettings.IPAddress}}' mysql-master

执行结果

1
2
3
[root@172 temp]# docker inspect --format='{{.NetworkSettings.IPAddress}}' mysql-master
172.17.0.7

认主

登录从数据库的MySql

1
docker exec -it mysql-slave /usr/bin/mysql -uroot -p123456

建立主从链接

1
2
3
4
5
6
7
8
mysql> change master to 
-> master_host='172.17.0.7',
-> master_user='slave',
-> master_password='123456',
-> master_port=3306,
-> master_log_file='mysql-bin.000001',
-> master_log_pos=512,
-> master_connect_retry=30;

命令说明:
master_host:Master的地址
master_port:Master的端口号,指的是容器的端口号
master_user:用于数据同步的用户
master_password:用于同步的用户的密码
master_log_file:指定 Slave 从哪个日志文件开始复制数据,即上文中提到的 File 字段的值
master_log_pos:从哪个 Position 开始读,即上文中提到的 Position 字段的值
master_connect_retry:如果连接失败,重试的时间间隔,单位是秒,默认是60秒

开启主从同步

1
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
mysql> show slave status \G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 172.17.0.7
Master_User: slave
Master_Port: 3306
Connect_Retry: 30
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 512
Relay_Log_File: mysql-relay-bin.000002
Relay_Log_Pos: 283
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 512
Relay_Log_Space: 456
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1
Master_UUID: f0185a03-2ab7-11ea-bebf-0242ac110007
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 the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
1 row in set (0.00 sec)

如果Slave_IO_RunningSlave_SQL_Running 都是Yes,说明主从复制已经开启。

主从复制排错

使用start slave开启主从复制后,Slave_IO_Running一直是Connecting状态,如下:

1
2
3
4
 Slave_IO_Running: Connecting
Slave_SQL_Running: Yes
Last_IO_Errno: 2003
Last_IO_Error: error reconnecting to master 'slave@172.17.0.7:3306' - retry-time: 30 retries: 2

则说明主从复制一直处于连接状态,这种情况一般是下面几种原因造成的,我们可以根据 Last_IO_Error提示予以排除:

  • 网络不通:检查IP、端口、防火墙
  • 密码不对:检查用户、密码是否正确;权限授予是否正确;授予完权限后是否有执行flush privileges;
  • pos 不对:检查Master的Position

解锁主数据库

登入主数据库的MySql

1
docker exec -it mysql-master /usr/bin/mysql -uroot -p123456

解锁

1
mysql> unlock tables;

测试验证

测试验证就比较简单了,我们可以在Master中随便填充点数据,然后看Slave中是否有对应的变化就可以了。