Docker 实现 MySQL 主从复制
搭建一主一从
一台主机用于处理所有 写请求,一台 从机 负责所有 读请求
MySQL 主机对外暴露的端口是 3306 ,从机对外暴露的端口是 3307
创建自定义网络,以便容器之间可以通过主机名和端口互相通信
docker network create mysql
新建主机容器实例,并将其加入到 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 进行维护
查看主机容器实例
docker ps
列出所有卷
docker volume ls
查看卷详情
docker volume inspect mysql-master
cd /var/lib/docker/volumes/mysql-master/_data
修改主机容器实例的配置文件
vim my.cnf
[mysqld]
#以下是增加的配置
server-id=1 # [必须] 主服务器唯一ID
log-bin=binlog # [必须] 启用二进制日志,指名路径。比如:自己本地的路径/log/mysqlbin
binlog_format=STATEMENT
docker restart mysql-master
新建从机容器实例
并将其加入到 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进行维护
查看从机容器实例
docker ps
列出所有卷
docker volume ls
查看卷详情
docker volume inspect mysql-slave
cd /var/lib/docker/volumes/mysql-slave/_data
修改从机容器实例的配置文件
并重启从机容器实例
vim my.cnf
[mysqld]
#以下是增加的配置
server-id=2 # [必须] 从服务器唯一ID
relay-log=mysql-relay #[可选] 启用中继日志
docker restart mysql-slave
查看自定义网络
查看自定义网络是否有 MySQL 的主机和从机
docker network ls
docker network inspect 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;
查看 Master 的状态,并记录 File 和 Position 的值
SHOW MASTER STATUS;
执行完此步骤后 不要再操作 MySQL 主机容器实例,防止主服务器的状态值发生变化
从机容器实例:配置需要复制的主机
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 等)进行远程连接而已,而主从复制之间的通信是不需要外部端口的
启动 Slave 同步
START SLAVE;
查看同步状态
SHOW SLAVE STATUS\G;
注意:如果出现错误,可能的原因如下:
● ① 网络不通。
● ② 账户密码错误。
● ③ 防火墙。
● ④ MySQL 配置文件问题。
● ⑤ 连接服务器时的语法。
● ⑥ 主服务器 MySQL 的权限。
测试:主机容器实例新建库、新建表、插入记录,从机复制
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’);
主机容器实例和从机容器实例查询数据库
USE dbtest;
SELECT * FROM student;
停止主从同步
在从机容器实例停止主从同步
在从机执行
STOP SLAVE;
如果在停止主从同步后,再开启同步命令的时候,报错,可以执行如下的命令
在从机执行
RESET SLAVE;
#删除 SLAVE 数据库的 relay log 日志文件,并重新启用新的 relay log 文件
在主机执行
RESET MASTER;
#删除 Master 中所有的 bing log 文件,并将日志索引文件清空,重新开始所有新的日志文件(慎用)
搭建双主双从
架构
● 一个主机 m1 用于处理所有写请求,它的从机 s1 和另一台主机 m2 还有它的从机 s2 负责所有读请求。当 m1 主机宕机后,m2 主机负责写请求,m1 和 m2 互为备机
首先会创建一个自定义网络 mysql ,然后会启动 4 台 MySQL 容器实例,其中 2 台作为主机,2 台作为从机
编号 | 主机名 | 备注 |
---|---|---|
1 | m1 | 主机 |
2 | s1 | 从 |
3 | m2 | 主机(备) |
4 | s2 | 从机(备) |
创建自定义网络,以便容器之间可以通过主机名和端口互相通信
docker network create 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
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
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
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
查看自定义网络是否有 MySQL 的主机和从机
docker network ls
docker network inspect mysql
列出所有卷
docker volume ls
查看卷详情
docker volume inspect m1
cd /var/lib/docker/volumes/m1/_data
其余,依次类推即可
双主搭建
- 主机搭建(m1)
修改主机 m1 容器实例的配置文件,并重启主机容器 m1 实例
vim my.cnf
[mysqld]
#以下是增加的配置
server-id=1
#[必须] 主服务器唯一ID,保证整个集群环境中唯一,取值范围在 1 - 2^32 -1 ,默认为 1
log-slave-updates
#在作为从库的时候,有写入操作也需要更新二进制日志文件
docker restart m1
- 主机搭建(m2)
修改主机 m2 容器实例的配置文件,并重启主机容器 m2 实例
vim my.cnf
[mysqld]
#以下是增加的配置
server-id=3
#[必须] 主服务器唯一ID,保证整个集群环境中唯一,取值范围在 1 - 2^32 -1 ,默认为 1
log-slave-updates
#在作为从库的时候,有写入操作也需要更新二进制日志文件
docker restart m2
双从搭建
- 从机搭建(s1)
修改从机 s1 容器实例的配置文件,并重启从机容器 s1 实例
vim my.cnf
[mysqld]
#以下是增加的配置
server-id=2
#[必须] 从服务器唯一ID
relay-log=mysql-relay
#[可选] 启用中继日志
docker restart s1
- 从机搭建(s2)
修改从机 s2 容器实例的配置文件,并重启从机容器 s2 实例
vim my.cnf
[mysqld]
#以下是增加的配置
server-id=4
#[必须] 从服务器唯一ID
relay-log=mysql-relay
#[可选] 启用中继日志
docker restart s2
双主:建立账户并授权
- 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;
- 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;
- 查看 MySQL 主机(m1)的状态,并记录 File 和 Position 的值
SHOW MASTER STATUS;
- 查看 MySQL 主机(m2)的状态,并记录 File 和 Position 的值
SHOW MASTER STATUS;
双从:配置需要复制的主机
从机上复制主机的命令
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;
示例: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;
示例:s1 示例:s2
启动 Slave 同步
START SLAVE;
查看同步状态 示例:s1 s2
SHOW SLAVE STATUS\G;
到此为止,我们只是配置了两台主从复制,还没有配置主主复制,请看下面的操作
双主之间的相互复制
查看 Master 的状态,并记录 File 和 Position 的值
示例:m1 m2
SHOW MASTER STATUS;
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;
示例:m2
CHANGE MASTER TO
MASTER_HOST=‘m1’,
MASTER_USER=‘slave1’,
MASTER_PASSWORD=‘123456’,
MASTER_LOG_FILE=‘binlog.000003’,
MASTER_LOG_POS=1114;
启动 Master 同步
示例m1
START SLAVE;
示例m2
START SLAVE;
查看同步状态,示例m1 m2
SHOW SLAVE STATUS\G;
测试,在 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));
示例:m2
#执行 DML 操作
USE dbtest;
INSERT INTO student VALUES(1, 'xx');
INSERT INTO student VALUES(2, 'yy');
示例:m1
#查看数据
USE dbtest;
SELECT * FROM student;
示例:m2
#查看数据
USE dbtest;
SELECT * FROM student;
示例:s1
#查看数据
USE dbtest;
SELECT * FROM student;
示例:s2
#查看数据
USE dbtest;
SELECT * FROM student;