Skip to content

Docker 实现 MySQL 主从复制

第一章:搭建一主一从

1.1 架构

  • 一台 主机 用于处理所有 写请求,一台 从机 负责所有 读请求,架构图如下:

1669878405565-5e566e72-2d55-4638-868e-b6b6bf168b79.png

  • MySQL  主机对外暴露的端口是 3306 ,从机对外暴露的端口是 3307 。

1.2 搭建步骤

1.2.1 自定义网络

  • 创建自定义网络,以便容器之间可以通过主机名和端口互相通信:
docker network create mysql

1669878410574-0af12272-1646-418f-b3f8-45572282104a.gif

1.2.2 新建主机容器实例

  • 新建主机容器实例,并将其加入到 mysql 自定义网络中:
docker run -d -p 3306:3306 --network mysql \
    --name mysql-master \
    -v mysql-master:/etc/mysql \
    -v /var/mysql-master/logs:/var/log/mysql \
    -v /var/mysql-master/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -e TZ=Asia/Shanghai \
    --restart=always mysql:8.0.26 \
    --lower_case_table_names=1 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_general_ci \
    --default-authentication-plugin=mysql_native_password

注意:

  • 这里我使用的是具名卷,意味着 MySQL 的配置文件在 /var/lib/docker/volumes/目录下的 mysql-master 进行维护。
  • -v mysql-master:/etc/mysql 其实也是可以写成 -v mysql-master:/etc/mysql/conf.d ,只不过如果写成 -v mysql-master:/etc/mysql/conf.d需要在 /etc/mysql/conf.d映射的目录中新建 my.cnf 配置文件,而写成 -v mysql-master:/etc/mysql是不需要新建 my.cnf 配置文件的,两种方式均可以。

1669878416632-43347d51-cce2-4f22-9a50-ff1dd29a08d9.gif

  • 查看主机容器实例:
docker ps

1669878420896-6da2bb64-068b-4e21-8e73-f568af46fdf7.gif

  • 列出所有卷:
docker volume ls

1669878425585-b17bd0bf-4d25-4128-889a-f41d01591b91.gif

  • 查看卷详情:
docker volume inspect mysql-master
cd /var/lib/docker/volumes/mysql-master/_data

1669878429917-79a77aa6-e816-407d-bf53-162f4c557bae.gif

1.2.3 修改主机容器实例的配置文件

  • 修改主机容器实例的配置文件,并重启主机容器实例:
vim my.cnf
[mysqld]
# 以下是增加的配置
server-id=1 # [必须] 主服务器唯一ID
log-bin=binlog # [必须] 启用二进制日志,指名路径。比如:自己本地的路径/log/mysqlbin
binlog_format=STATEMENT
docker restart mysql-master

1669878435146-c6036088-fd76-4ec9-8638-16944e8a62ab.gif

1.2.4 新建从机容器实例

  • 新建从机容器实例,并将其加入到 mysql 自定义网络中:
docker run -d -p 3307:3306 --network mysql \
    --name mysql-slave \
    -v mysql-slave:/etc/mysql \
    -v /var/mysql-slave/logs:/var/log/mysql \
    -v /var/mysql-slave/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -e TZ=Asia/Shanghai \
    --restart=always mysql:8.0.26 \
    --lower_case_table_names=1 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_general_ci \
    --default-authentication-plugin=mysql_native_password

注意:这里我使用的是具名卷,意味着 MySQL 的配置文件在 /var/lib/docker/volumes/目录下的 mysql-slave进行维护。

1669878439770-c798ea21-0849-4b6a-9941-1e345fbcdd72.gif

  • 查看从机容器实例:
docker ps

1669878443882-206f6613-509a-4136-b92b-835c3934af25.gif

  • 列出所有卷:
docker volume ls

1669878448254-eeedcf76-686d-41d8-8394-933481232c6a.gif

  • 查看卷详情:
docker volume inspect mysql-slave
cd /var/lib/docker/volumes/mysql-slave/_data

1669878454150-a860dc74-f9d8-412c-ab43-dd1d3ccdc71b.gif

1.2.5 修改从机容器实例的配置文件

  • 修改从机容器实例的配置文件,并重启从机容器实例:
vim my.cnf
[mysqld]
# 以下是增加的配置
server-id=2 # [必须] 从服务器唯一ID
relay-log=mysql-relay #[可选] 启用中继日志
docker restart mysql-slave

1669878463960-bd7af5ad-484b-4e36-8262-643563b386a7.gif

1.2.6 查看自定义网络

  • 查看自定义网络是否有 MySQL 的主机和从机:
docker network ls
docker network inspect mysql

1669878468611-918682c6-d1bf-410e-9579-ff553a0deead.gif

1.2.7 主机容器实例:建立账户并授权

  • 进入 MySQL 的主机容器实例,创建账户,并进行授权:
docker exec -it mysql-master /bin/bash
mysql -uroot -p123456
CREATE USER 'slave1'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'slave1'@'%';
ALTER USER 'slave1'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;

1669878489086-88814e30-e4ee-4634-868f-e5ff5b9d6367.gif

  • 查看 Master 的状态,并记录 File 和 Position 的值:
SHOW MASTER STATUS;

1669878494101-d198a8ca-8514-431b-9241-be3f7ef09fdf.gif

注意:执行完此步骤后 不要再操作 MySQL 主机容器实例,防止主服务器的状态值发生变化。

1.2.8 从机容器实例:配置需要复制的主机

  • 步骤 ①:从机上复制主机的命令
CHANGE MASTER TO 
MASTER_HOST='主机的IP地址',
MASTER_USER='主机用户名',
MASTER_PORT='主机的端口号',
MASTER_PASSWORD='主机用户名的密码',
MASTER_LOG_FILE='binlog.具体数字',
MASTER_LOG_POS=具体值;
  • 示例:
docker exec -it mysql-slave /bin/bash
mysql -uroot -p123456
CHANGE MASTER TO 
MASTER_HOST='mysql-master',
MASTER_USER='slave1',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='binlog.000006',
MASTER_LOG_POS=1114;

此处,如果没有写端口,默认是 3306,那么是暴露的 3306 ?当然不是,是内部的端口 3306 ,因为走的是自定义网络,和暴露的外部端口没有任何关系,暴露的外部端口只是让客户端(Java 等)进行远程连接而已,而主从复制之间的通信是不需要外部端口的。

1669878501078-7cd2dcd2-af0e-46d6-8a58-edb000a95775.gif

  • 步骤 ②:启动 Slave 同步
START SLAVE;
  • 示例:
START SLAVE;

1669878514343-eddee5a1-a8db-42b3-9322-d28b830ba642.gif

  • 步骤 ③:查看同步状态
SHOW SLAVE STATUS\G;
  • 示例:
SHOW SLAVE STATUS\G;

1669878523975-e7866f85-be58-4bf6-a682-5de27fb38aed.gif

注意:如果出现错误,可能的原因如下:

  • ① 网络不通。
  • ② 账户密码错误。
  • ③ ~~防火墙~~。
  • ④ MySQL 配置文件问题。
  • ⑤ 连接服务器时的语法。
  • ⑥ 主服务器 MySQL 的权限。

1.2.9 测试

  • 主机容器实例新建库、新建表、插入记录,从机复制:
CREATE DATABASE IF NOT EXISTS dbtest CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci';
USE dbtest;
CREATE TABLE student(id INT,`name` VARCHAR(16));
INSERT INTO student VALUES(1, 'xxx');
INSERT INTO student VALUES(2, 'yyy');

1669878544983-26facde8-a971-4d64-b40e-72111c9d0d94.gif

  • 主机容器实例和从机容器实例查询数据库:
USE dbtest;
SELECT * FROM student;

1669878549744-e2933ef2-c9e0-40e5-b7cb-b5b0e31ddbda.gif

1.2.10 停止主从同步

  • 在从机容器实例停止主从同步:
# 在从机执行
STOP SLAVE;
  • 如果在停止主从同步后,再开启同步命令的时候,报错,可以执行如下的命令:
# 在从机执行
STOP SLAVE;
# 在从机执行
RESET SLAVE; # 删除 SLAVE 数据库的 relay log 日志文件,并重新启用新的 relay log 文件
# 在主机执行
RESET MASTER;  #删除 Master 中所有的 bing log 文件,并将日志索引文件清空,重新开始所有新的日志文件(慎用)

第二章:搭建双主双从

2.1 架构

  • 一个主机 m1 用于处理所有写请求,它的从机 s1 和另一台主机 m2 还有它的从机 s2 负责所有读请求。当 m1 主机宕机后,m2 主机负责写请求,m1 和 m2 互为备机,架构图如下:

1669878556296-492f12c1-a9fb-437d-90c4-fe11f3f08a09.png

2.2 准备工作

  • 首先会创建一个自定义网络 mysql ,然后会启动 4 台 MySQL 容器实例,其中 2 台作为主机,2 台作为从机。
编号 主机名 备注
1 m1 主机
2 s1
3 m2 主机(备)
4 s2 从机(备)

2.3 自定义网络

  • 创建自定义网络,以便容器之间可以通过主机名和端口互相通信:
docker network create mysql

1669878563893-0e30098d-b964-400f-ab1b-f9253a20eee6.gif

2.4 新建 4 台 MySQL 容器实例

  • 新建 4 台 MySQL 容器实例,2 台容器实例为 MySQL 的主机,2 台容器实例为 MySQL 的从机:
docker run -d -p 3306:3306 --network mysql \
      --name m1 \
      -v m1:/etc/mysql \
      -v /var/m1/logs:/var/log/mysql \
      -v /var/m1/data:/var/lib/mysql \
      -e MYSQL_ROOT_PASSWORD=123456 \
      -e TZ=Asia/Shanghai \
      --restart=always mysql:8.0.26 \
      --lower_case_table_names=1 \
      --character-set-server=utf8mb4 \
      --collation-server=utf8mb4_general_ci \
      --default-authentication-plugin=mysql_native_password

1669878570293-4d93b245-ac92-4441-9eed-43263d508d90.gif

docker run -d -p 3307:3306 --network mysql \
    --name s1 \
    -v s1:/etc/mysql \
    -v /var/s1/logs:/var/log/mysql \
    -v /var/s1/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -e TZ=Asia/Shanghai \
    --restart=always mysql:8.0.26 \
    --lower_case_table_names=1 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_general_ci \
    --default-authentication-plugin=mysql_native_password

1669878575975-b93d0bb5-cc20-43d8-a1b0-44baaaa71b47.gif

docker run -d -p 3308:3306 --network mysql \
    --name m2 \
    -v m2:/etc/mysql \
    -v /var/m2/logs:/var/log/mysql \
    -v /var/m2/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -e TZ=Asia/Shanghai \
    --restart=always mysql:8.0.26 \
    --lower_case_table_names=1 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_general_ci \
    --default-authentication-plugin=mysql_native_password

1669878581166-cf37f72b-8e4f-4081-8de3-c5820259dafc.gif

docker run -d -p 3309:3306 --network mysql \
    --name s2 \
    -v s2:/etc/mysql \
    -v /var/s2/logs:/var/log/mysql \
    -v /var/s2/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -e TZ=Asia/Shanghai \
    --restart=always mysql:8.0.26 \
    --lower_case_table_names=1 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_general_ci \
    --default-authentication-plugin=mysql_native_password

1669878586178-da7855d5-1ea9-4e86-af9b-adfa7adbe547.gif

2.5 查看自定义网络

  • 查看自定义网络是否有 MySQL 的主机和从机:
docker network ls
docker network inspect mysql

1669878595157-b0771307-e458-49bf-9d9c-d3c182144ab3.gif

2.6 查看卷

  • 列出所有卷:
docker volume ls

1669878602499-80df102e-26cc-4ecd-9fa0-b40260ab206e.gif

  • 查看卷详情:
docker volume inspect m1
cd /var/lib/docker/volumes/m1/_data

其余,依次类推即可。

1669878607774-2e9fc560-8fe9-4fe6-a9a3-cd1930106984.gif

2.7 双主搭建

2.7.1 主机搭建(m1)

  • 修改主机 m1 容器实例的配置文件,并重启主机容器 m1  实例:
vim my.cnf
[mysqld]
# 以下是增加的配置
server-id=1 # [必须] 主服务器唯一ID,保证整个集群环境中唯一,取值范围在 1 - 2^32 -1 ,默认为 1
log-slave-updates # 在作为从库的时候,有写入操作也需要更新二进制日志文件
docker restart m1

1669878613051-b99bbc46-e5ed-406b-a226-497c314ac86a.gif

2.7.2 主机搭建(m2)

  • 修改主机 m2 容器实例的配置文件,并重启主机容器 m2  实例:
vim my.cnf
[mysqld]
# 以下是增加的配置
server-id=3 # [必须] 主服务器唯一ID,保证整个集群环境中唯一,取值范围在 1 - 2^32 -1 ,默认为 1
log-slave-updates # 在作为从库的时候,有写入操作也需要更新二进制日志文件
docker restart m2

1669878622011-faa770e6-3671-4f39-95e7-2fcc001955c5.gif

2.8 双从搭建

2.8.1 从机搭建(s1)

  • 修改从机 s1 容器实例的配置文件,并重启从机容器 s1  实例:
vim my.cnf
[mysqld]
# 以下是增加的配置
server-id=2 # [必须] 从服务器唯一ID
relay-log=mysql-relay #[可选] 启用中继日志
docker restart s1

1669878629427-fb0f76ab-82ae-45d1-82e5-96a77816b85b.gif

2.8.2 从机搭建(s2)

  • 修改从机 s2 容器实例的配置文件,并重启从机容器 s2  实例:
vim my.cnf
[mysqld]
# 以下是增加的配置
server-id=4 # [必须] 从服务器唯一ID
relay-log=mysql-relay #[可选] 启用中继日志
docker restart s2

1669878635127-6b086918-39e6-4dac-87ab-dfbdba301ca9.gif

2.9 双主:建立账户并授权

  • MySQL 主机(m1)创建账号并授权:
docker exec -it m1 /bin/bash
mysql -uroot -p123456
CREATE USER 'slave1'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'slave1'@'%';
ALTER USER 'slave1'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;

1669878640253-ba0bfb2f-18e8-4126-a499-7a849bd18144.gif

  • MySQL 主机(m2)创建账号并授权:
docker exec -it m2 /bin/bash
mysql -uroot -p123456
CREATE USER 'slave1'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'slave1'@'%';
ALTER USER 'slave1'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;

1669878650003-a2279c34-0db7-4e21-a4f3-d87129fa656a.gif

  • 查看 MySQL 主机(m1)的状态,并记录 File 和 Position 的值:
SHOW MASTER STATUS;

1669878656957-fed2485e-130e-4618-8922-11d62b79be16.png

  • 查看 MySQL 主机(m2)的状态,并记录 File 和 Position 的值:
SHOW MASTER STATUS;

1669878662124-2d474a37-79ad-47d7-a3fc-36ce345fb379.png

2.10 双从:配置需要复制的主机

  • 步骤 ①:从机上复制主机的命令
CHANGE MASTER TO 
MASTER_HOST='主机的IP地址',
MASTER_USER='主机用户名',
MASTER_PORT='主机的端口号',
MASTER_PASSWORD='主机用户名的密码',
MASTER_LOG_FILE='binlog.具体数字',
MASTER_LOG_POS=具体值;
  • 示例:s1
docker exec -it s1 /bin/bash
mysql -uroot -p123456
CHANGE MASTER TO 
MASTER_HOST='m1',
MASTER_USER='slave1',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='binlog.000003',
MASTER_LOG_POS=1114;

1669878673152-45257dec-c5c2-48b7-b74a-911b97dac571.gif

  • 示例:s2
docker exec -it s2 /bin/bash
mysql -uroot -p123456
CHANGE MASTER TO 
MASTER_HOST='m2',
MASTER_USER='slave1',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='binlog.000003',
MASTER_LOG_POS=1114;

1669878678962-73312cc5-7a63-4113-adcb-dde03759cbf4.gif

  • 步骤 ②:启动 Slave 同步
START SLAVE;
  • 示例:s1
START SLAVE;

1669878685313-7ec5a0ff-26c3-432f-89e3-da2c88e756b4.gif

  • 示例:s2
START SLAVE;

1669878691941-dbc0c8ed-46c8-4091-ba8a-21163d84fcc3.gif

  • 步骤 ③:查看同步状态
SHOW SLAVE STATUS\G;
  • 示例:s1
SHOW SLAVE STATUS\G;

1669878697434-d63d2426-98bd-473a-808d-30498bec4c58.gif

  • 示例:s2
SHOW SLAVE STATUS\G;

1669878703563-2f855b15-676d-4661-abd7-de994ea3203e.gif

到此为止,我们只是配置了两台主从复制,还没有配置主主复制,请看下面的操作。

2.11 双主之间的相互复制

  • 步骤 ① :查看 Master 的状态,并记录 File 和 Position 的值:
SHOW MASTER STATUS;

之所以再次执行,是为了防止 File 和 Position 有变化。

  • 示例:m1
SHOW MASTER STATUS;

1669878709927-36501872-0be0-43f5-9c2a-c56abbcc3fe7.png

  • 示例:m2
SHOW MASTER STATUS;

1669878717002-8e79d2a4-ef30-4d34-8d01-0bf5da9b0e40.png

  • 步骤 ②:m2 复制 m1,m1 复制 m2。
CHANGE MASTER TO 
MASTER_HOST='主机的IP地址',
MASTER_USER='主机用户名',
MASTER_PORT='主机的端口号',
MASTER_PASSWORD='主机用户名的密码',
MASTER_LOG_FILE='binlog.具体数字',
MASTER_LOG_POS=具体值;
  • 示例:m1
CHANGE MASTER TO 
MASTER_HOST='m2',
MASTER_USER='slave1',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='binlog.000003',
MASTER_LOG_POS=1114;

1669878723334-dd38597a-b2df-463d-a90a-049596122a6d.gif

  • 示例:m2
CHANGE MASTER TO 
MASTER_HOST='m1',
MASTER_USER='slave1',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='binlog.000003',
MASTER_LOG_POS=1114;

1669878729862-d24e1bdb-d300-4c39-ba4e-93aaeb542f4b.gif

  • 步骤 ③:启动 Master 同步
START SLAVE;
  • 示例:m1
START SLAVE;

1669878737733-5b1ad655-e5fb-4516-a02d-67159976542f.gif

  • 示例:m2
START SLAVE;

1669878741627-c704ee87-a5ef-4881-af45-fbadb72f2ce3.gif

  • 步骤 ④:查看同步状态
SHOW SLAVE STATUS\G;
  • 示例:m1
SHOW SLAVE STATUS\G;

1669878747293-c5918038-f9b6-4ea5-b503-f78036c997bf.gif

  • 示例:m2
SHOW SLAVE STATUS\G;

1669878752778-82d8b83c-13fb-4073-946e-7451085d3adc.gif

2.12 测试

  • 在 m1 和 m2 上执行 DDL 和 DML 语句,查看涉及到的数据库服务器的数据同步情况。

  • 示例:m1

# 执行 DDL 操作
CREATE DATABASE IF NOT EXISTS dbtest CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci';
USE dbtest;
CREATE TABLE student(id INT,`name` VARCHAR(16));

1669878760945-d4343ec8-228c-43d2-84c7-9a11beddaaff.gif

  • 示例:m2
# 执行 DML 操作
USE dbtest;
INSERT INTO student VALUES(1, 'xx');
INSERT INTO student VALUES(2, 'yy');

1669878766146-6f4f9b3b-dd43-45fa-9303-71122588f0ab.gif

  • 示例:m1
# 查看数据
USE dbtest;
SELECT * FROM student;

1669878771079-22ca4521-4411-4823-8a6f-785a2ffb3771.gif

  • 示例:m2
# 查看数据
USE dbtest;
SELECT * FROM student;

1669878776114-2f3e3860-fa46-4cee-9833-d17167291661.gif

  • 示例:s1
# 查看数据
USE dbtest;
SELECT * FROM student;

1669878781033-5fced8b7-0bb6-4738-a746-36e4ea97048f.gif

  • 示例:s2
# 查看数据
USE dbtest;
SELECT * FROM student;

1669878786513-6f739791-6d85-4c72-951e-e50d7b0465f9.gif

更新: 2022-12-05 01:40:40
原文: https://www.yuque.com/fairy-era/yg511q/aptyqmf46zssnpaz

Comments