Docker 部署 MySQL 双主双从同步架构详细笔记

一、前置准备:Docker 安装

1. 卸载旧版本 Docker(可选)

复制代码
sudo yum remove docker \
                  docker-client \
                  docker-client-latest \
                  docker-common \
                  docker-latest \
                  docker-latest-logrotate \
                  docker-logrotate \
                  docker-engine

代码解释

  • sudo:以管理员权限执行命令
  • yum remove:通过 yum 包管理器卸载指定软件
  • 后续列出的是 Docker 旧版本的相关组件,确保彻底清理旧版本,避免冲突

2. 设置 Docker 仓库(这里国内一般用镜像源,之前拉去镜像的文章中有阿里云网址)

复制代码
sudo yum install -y yum-utils
sudo yum-config-manager \
    --add-repo \
    https://download.docker.com/linux/centos/docker-ce.repo

代码解释

  • yum install -y yum-utils:安装 yum 工具包(包含 yum-config-manager),-y表示自动确认安装
  • yum-config-manager --add-repo:添加 Docker 官方 yum 仓库,指定仓库地址为 Docker CE 的 CentOS 版本仓库

3. 安装 Docker 引擎

复制代码
sudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

代码解释

  • docker-ce:Docker 社区版引擎(核心组件)
  • docker-ce-cli:Docker 命令行客户端
  • containerd.io:容器运行时(管理容器生命周期)
  • docker-buildx-plugin:扩展 Docker 构建功能的插件
  • docker-compose-plugin:支持 docker-compose 命令的插件

4. 启动并设置 Docker 开机自启

复制代码
sudo systemctl start docker
sudo systemctl enable docker

代码解释

  • systemctl start docker:启动 Docker 服务
  • systemctl enable docker:设置 Docker 服务开机自动启动

5. 验证 Docker 安装

一开始找不到就会自动拉取

复制代码
sudo docker run hello-world

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

代码解释

  • 拉取并运行官方的hello-world镜像,若输出欢迎信息则说明 Docker 安装成功

二、MySQL 双主双从架构部署

复制代码
mysql-master1 (3306) <-------> mysql-master2 (3308)  【双主互相同步】
       ↑                              ↑
       │                              │
       ↓                              ↓
mysql-slave1 (3307)            mysql-slave2 (3309) 【从库只读】
角色 容器名 主机端口 容器端口 server-id 角色说明
主库 1 mysql-master1 3306 3306 11 可写,双主之一
主库 2 mysql-master2 3308 3306 33 可写,双主之一
从库 1 mysql-slave1 3307 3306 22 只读,同步 master1
从库 2 mysql-slave2 3309 3306 44 只读,同步 master2

1. 创建 Docker 自定义网络并拉去mysql镜像

复制代码
[root@localhost ~]# docker network create mysql_net
[root@localhost ~]#docker pull mysql:8.4.8

代码解释

  • docker network create:创建 Docker 自定义网络
  • mysql_net:网络名称,用于让所有 MySQL 容器在同一网络下互通(容器间可通过容器名访问)

2. 配置 MySQL 主从节点配置文件(先把配置文件写好,而不是进入镜像写)

(1)master1 配置文件
复制代码
# mysql-master1
[mysqld]
server_id=11
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
log-bin=mysql-bin
auto-increment-increment=2
auto-increment-offset=1

代码解释

  • [mysqld]:MySQL 服务端配置段标识
  • server_id=11:数据库实例唯一标识(主从架构中所有节点 id 必须不同)
  • binlog-ignore-db=mysql:二进制日志忽略mysql系统库(不记录该库的操作)
  • binlog-ignore-db=information_schema:忽略information_schema系统库
  • log-bin=mysql-bin:开启二进制日志(主从同步的核心,记录数据变更),日志文件前缀为mysql-bin
  • auto-increment-increment=2:自增字段步长为 2(避免双主写入时主键冲突)
  • auto-increment-offset=1:自增字段起始偏移为 1(master1 的自增 id 为 1、3、5...)
(2)master2 配置文件
复制代码
# mysql-master2
[mysqld]
server_id=33
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
log-bin=mysql-bin
auto-increment-increment=2
auto-increment-offset=2

代码解释

  • server_id=33:master2 的唯一标识,与 master1 区分
  • auto-increment-offset=2:自增字段起始偏移为 2(master2 的自增 id 为 2、4、6...)
  • 其余参数与 master1 一致,核心是保证双主自增 id 不冲突
(3)slave1 配置文件
复制代码
# mysql-slave1
[mysqld]
server_id=22
log-bin=mysql-slave-bin
relay_log=mysql-relay
read_only=1

代码解释

  • server_id=22:slave1 唯一标识
  • log-bin=mysql-slave-bin:开启 slave 的二进制日志(可选,若 slave 需作为其他节点的主库则必须)
  • relay_log=mysql-relay:开启中继日志(主从同步时,slave 先将 master 的 binlog 写入中继日志,再重放)
  • read_only=1:设置 slave 为只读模式(仅对普通用户生效,root 仍可写)
(4)slave2 配置文件
复制代码
# mysql-slave2
[mysqld]
server_id=44
log-bin=mysql-slave-bin
relay_log=mysql-relay
read_only=1

代码解释

  • server_id=44:slave2 唯一标识
  • 其余参数与 slave1 一致

3. 创建目录并挂载配置文件(关键前置步骤)

复制代码
# 创建master1相关目录
mkdir -p /data/master1/log /data/master1/data /data/master1/conf
# 将上述master1配置写入/my.cnf
echo -e "[mysqld]\nserver_id=11\nbinlog-ignore-db=mysql\nbinlog-ignore-db=information_schema\nlog-bin=mysql-bin\nauto-increment-increment=2\nauto-increment-offset=1" > /data/master1/conf/my.cnf

# 创建master2相关目录
mkdir -p /data/master2/log /data/master2/data /data/master2/conf
echo -e "[mysqld]\nserver_id=33\nbinlog-ignore-db=mysql\nbinlog-ignore-db=information_schema\nlog-bin=mysql-bin\nauto-increment-increment=2\nauto-increment-offset=2" > /data/master2/conf/my.cnf

# 创建slave1相关目录
mkdir -p /data/slave1/log /data/slave1/data /data/slave1/conf
echo -e "[mysqld]\nserver_id=22\nlog-bin=mysql-slave-bin\nrelay_log=mysql-relay\nread_only=1" > /data/slave1/conf/my.cnf

# 创建slave2相关目录
mkdir -p /data/slave2/log /data/slave2/data /data/slave2/conf
echo -e "[mysqld]\nserver_id=44\nlog-bin=mysql-slave-bin\nrelay_log=mysql-relay\nread_only=1" > /data/slave2/conf/my.cnf

代码解释

  • mkdir -p:递归创建目录(不存在的父目录自动创建)
  • /data/master1/log:挂载容器内 MySQL 日志目录
  • /data/master1/data:挂载容器内 MySQL 数据目录(持久化数据)
  • /data/master1/conf:挂载容器内 MySQL 配置目录
  • echo -e:将配置内容写入my.cnf文件,-e支持换行符\n

4. 创建 master1 容器

复制代码
docker run --name mysql-master1 \
--restart=always \
-p 3306:3306 \
-v /data/master1/log:/var/log/mysql \
-v /data/master1/data:/var/lib/mysql \
-v /data/master1/conf/my.cnf:/etc/my.cnf \
-e MYSQL_ROOT_PASSWORD=123456 \
--network mysql_net \
-d mysql:8.4.8

可能会遇到端口被占用或者你失败后重新创建告诉已存在的问题

端口用lsof或者ss -tulnp去查看

已存在就用docker rm mysql-master1
查看所有容器用docker ps -a

代码解释

  • docker run:创建并运行容器
  • --name mysql-master1:指定容器名称为mysql-master1
  • --restart=always:容器退出时自动重启(保证服务高可用)
  • -p 3306:3306:端口映射(宿主机 3306 端口映射到容器内 3306 端口)
  • -v /data/master1/log:/var/log/mysql:目录挂载(宿主机目录:容器内目录),持久化日志
  • -v /data/master1/data:/var/lib/mysql:持久化 MySQL 数据(核心,容器删除后数据不丢失)
  • -v /data/master1/conf/my.cnf:/etc/my.cnf:挂载自定义配置文件覆盖容器默认配置
  • -e MYSQL_ROOT_PASSWORD=123456:设置 MySQL root 用户密码(环境变量)
  • --network mysql_net:将容器加入mysql_net自定义网络
  • -d:后台运行容器
  • mysql:8.4.8:指定使用的 MySQL 镜像版本

5. 创建 master2 容器

复制代码
docker run --name mysql-master2 \
--restart=always \
-p 3308:3306 \
-v /data/master2/log:/var/log/mysql \
-v /data/master2/data:/var/lib/mysql \
-v /data/master2/conf/my.cnf:/etc/my.cnf \
-e MYSQL_ROOT_PASSWORD=123456 \
--network mysql_net \
-d mysql:8.4.8

代码解释

  • --name mysql-master2:容器名称mysql-master2
  • -p 3308:3306:宿主机 3308 端口映射容器内 3306(避免与 master1 端口冲突)
  • 其余参数与 master1 一致,仅挂载目录和容器名不同

6. 创建 slave1 容器

复制代码
docker run --name mysql-slave1 \
--restart=always \
-p 3307:3306 \
-v /data/slave1/log:/var/log/mysql \
-v /data/slave1/data:/var/lib/mysql \
-v /data/slave1/conf/my.cnf:/etc/my.cnf \
-e MYSQL_ROOT_PASSWORD=123456 \
--network mysql_net \
-d mysql:8.4.8

代码解释

  • --name mysql-slave1:容器名称mysql-slave1
  • -p 3307:3306:宿主机 3307 端口映射容器内 3306
  • 其余参数逻辑与 master 一致

7. 创建 slave2 容器

复制代码
docker run --name mysql-slave2 \
--restart=always \
-p 3309:3306 \
-v /data/slave2/log:/var/log/mysql \
-v /data/slave2/data:/var/lib/mysql \
-v /data/slave2/conf/my.cnf:/etc/my.cnf \
-e MYSQL_ROOT_PASSWORD=123456 \
--network mysql_net \
-d mysql:8.4.8

代码解释

  • --name mysql-slave2:容器名称mysql-slave2
  • -p 3309:3306:宿主机 3309 端口映射容器内 3306
  • 其余参数逻辑与 master 一致

8. 配置 master1 同步用户(进入 master1 容器执行)

复制代码
docker exec -it mysql-master1 /bin/bash

这里可能会遇见刚进去就被卡出来的情况

极大的原因是因为虚拟机内容不够

复制代码
[root@test1 ~]# docker ps
CONTAINER ID   IMAGE         COMMAND                  CREATED         STA             NAMES
e7a75084cfd2   mysql:8.4.8   "docker-entrypoint.s..."   4 minutes ago   Up ->3306/tcp   mysql-slave2
f38f3d38387d   mysql:8.4.8   "docker-entrypoint.s..."   4 minutes ago   Res             mysql-slave1
a720712b96a3   mysql:8.4.8   "docker-entrypoint.s..."   5 minutes ago   Res             mysql-master2
0901a808a614   mysql:8.4.8   "docker-entrypoint.s..."   5 minutes ago   Up  33060/tcp   mysql-master1

up说明容器正常跑起来了,而另外两个陷入无限重启,因为oom

关闭虚拟机加大内存

代码解释

  • docker exec -it:进入运行中的容器(-it为交互式终端)
  • mysql-master1:目标容器名
  • /bin/bash:执行 bash 终端

进入容器后执行 MySQL 命令:

刚进入容器相当于一个小型linux,要先进入mysql

复制代码
bash-5.1# mysql -uroot -p123456
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.4.8 MySQL Community Server - GPL

Copyright (c) 2000, 2026, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> 

mysql> create user 'repl_user'@'%' identified by '123456';

代码解释

  • create user 'repl_user'@'%':创建用户repl_user%表示允许从任意主机连接

  • identified by '123456':设置密码为 123456

    mysql> create user 'slave_sync_user'@'%' identified by '123456';

代码解释

  • 创建专用于 slave 同步的用户slave_sync_user

    mysql> grant replication slave on . to 'repl_user'@'%';

代码解释

  • grant replication slave on *.*:授予repl_user复制权限(主从同步必需),*.*表示所有库所有表

  • to 'repl_user'@'%':将权限赋予该用户

    mysql> grant replication slave on . to 'slave_sync_user'@'%';

代码解释

  • slave_sync_user授予同样的复制权限

    mysql> flush privileges;

代码解释

  • 刷新权限表(使权限配置立即生效)

    mysql> show binary log status;

    mysql> show binary log status;
    +------------------+----------+--------------+--------------------------+-------------------+
    | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
    +------------------+----------+--------------+--------------------------+-------------------+
    | mysql-bin.000031 | 1448 | | mysql,information_schema | |
    +------------------+----------+--------------+--------------------------+-------------------+
    1 row in set (0.00 sec)

代码解释

  • 查看二进制日志状态,输出包含:
    • File:当前二进制日志文件名(如mysql-bin.000031
    • Position:当前日志偏移量(如 1448)
    • 这两个值是配置从库同步的关键参数

9. 配置 slave1 同步 master1

进入 slave1 容器:

复制代码
docker exec -it mysql-slave1 /bin/bash
mysql -uroot -p123456

这里可能会遇到slave1登入不进去mysql的情况,可能是原本文件残留的问题,建议删除原本挂载文件中产生的数据后重新创建一个容器

rm -rf /data/slave1/data/*

也有可能目录残留旧数据,还有-e没有生效,这里直接mysql就可以进入,root根本没有对应的密码

代码解释

  • mysql -uroot -p123456:使用 root 用户登录 MySQL,密码 123456

执行同步配置:

复制代码
mysql> change replication source to \
    -> source_host='mysql-master1',
    -> source_user='slave_sync_user',
    -> source_password='123456',
    -> source_log_file='mysql-bin.000031',
    -> source_log_pos=1448,
    -> get_source_public_key=1;

代码解释

  • change replication source to:MySQL 8.0 + 版本配置主从同步的命令(替代旧版change master to

  • source_host='mysql-master1':主库地址(容器名,因在同一网络可直接解析)

  • source_user='slave_sync_user':同步使用的用户

  • source_password='123456':同步用户密码

  • source_log_file='mysql-bin.000031':主库当前二进制日志文件名(需与 master1 的show binary log status结果一致)

  • source_log_pos=1448:主库当前日志偏移量(需与 master1 的结果一致)

  • get_source_public_key=1:MySQL 8.0 默认使用 caching_sha2_password 认证,需获取主库公钥

    mysql> start replica;

代码解释

  • 启动从库同步进程(替代旧版start slave

    mysql> show replica status\G

代码解释

  • 查看从库同步状态(\G按行格式化输出)
  • 关键检查项:
    • Replica_IO_Running: Yes:IO 线程运行正常(负责读取主库 binlog)
    • Replica_SQL_Running: Yes:SQL 线程运行正常(负责重放中继日志)
    • Replica_IO_State: Waiting for source to send event:IO 线程等待主库发送事件(同步正常状态)

10. 配置 master2 同步用户(与 master1 逻辑一致)

进入 master2 容器:

复制代码
docker exec -it mysql-master2 /bin/bash
mysql -uroot -p123456

执行用户创建与授权:

复制代码
mysql> create user 'repl_user'@'%' identified by '123456';
mysql> create user 'slave_sync_user'@'%' identified by '123456';
mysql> grant replication slave on *.* to 'repl_user'@'%';
mysql> grant replication slave on *.* to 'slave_sync_user'@'%';
mysql> flush privileges;
mysql> show binary log status;

mysql> show binary log status;
+------------------+----------+--------------+--------------------------+-------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB         | Executed_Gtid_Set |
+------------------+----------+--------------+--------------------------+-------------------+
| mysql-bin.000027 |     1447 |              | mysql,information_schema |                   |
+------------------+----------+--------------+--------------------------+-------------------+
1 row in set (0.00 sec)

代码解释

  • 与 master1 的用户配置逻辑完全一致,最后查看 master2 的 binlog 状态(获取 File 和 Position)

11. 配置 slave2 同步 master2

进入 slave2 容器:

复制代码
docker exec -it mysql-slave2 /bin/bash
mysql -uroot -p123456

执行同步配置:

复制代码
mysql> change replication source to \
    -> source_host='mysql-master2',
    -> source_user='slave_sync_user',
    -> source_password='123456',
    -> source_log_file='mysql-bin.000027',
    -> source_log_pos=1447,
    -> get_source_public_key=1;
mysql> start replica;
mysql> show replica status\G

代码解释

  • source_host改为mysql-master2source_log_filesource_log_pos使用 master2 的show binary log status结果,其余逻辑与 slave1 一致

12. 配置双主同步(master2 同步 master1)

在 master2 容器内执行:

复制代码
mysql> change replication source to \
    -> source_host='mysql-master1',
    -> source_user='repl_user',
    -> source_password='123456',
    -> source_log_file='mysql-bin.000031',
    -> source_log_pos=1448,
    -> get_source_public_key=1;
mysql> start replica;
mysql> show replica status\G

代码解释

  • 双主架构中,master2 作为 master1 的从库,使用repl_user同步
  • 参数逻辑与 slave 同步一致,仅用户为repl_user

13. 配置双主同步(master1 同步 master2)

先在 master2 容器内查看 binlog 状态:

复制代码
mysql> show binary log status;

再进入 master1 容器执行:

复制代码
mysql> change replication source to \
    -> source_host='mysql-master2',
    -> source_user='repl_user',
    -> source_password='123456',
    -> source_log_file='mysql-bin.000027',
    -> source_log_pos=1447,
    -> get_source_public_key=1;
mysql> start replica;
mysql> show replica status\G

代码解释

  • master1 作为 master2 的从库,完成双主互相同步配置
  • source_log_filesource_log_pos使用 master2 的 binlog 状态结果

14. 数据同步测试

(1)master1 创建数据库测试

在 master1 容器内执行:

复制代码
mysql> show databases;
mysql> create database mydb;
mysql> show databases;

代码解释

  • show databases:查看当前数据库列表
  • create database mydb:创建测试库mydb

分别在 master2、slave1、slave2 容器内执行:

复制代码
mysql> show databases;

预期结果 :均能看到mydb库,说明 master1 的变更同步到所有节点

(2)master2 创建表并插入数据测试

在 master2 容器内执行:

复制代码
mysql> use mydb;
mysql> create table t1(id int);
mysql> insert into t1 values(1),(2),(3);
mysql> select * from t1;

代码解释

  • use mydb:切换到mydb
  • create table t1(id int):创建测试表t1
  • insert into t1 values(1),(2),(3):插入测试数据
  • select * from t1:查看插入结果

分别在 master1、slave1、slave2 容器内执行:

复制代码
mysql> select * from mydb.t1;

预期结果:均能查询到插入的 3 条数据,说明 master2 的变更同步到所有节点

三、核心原理总结

  1. Docker 网络 :自定义网络mysql_net保证容器间通过名称互通,无需手动配置 IP
  2. 二进制日志(binlog):主库开启 binlog 记录数据变更,从库通过 IO 线程读取主库 binlog 写入中继日志,SQL 线程重放中继日志实现同步
  3. 双主自增策略auto-increment-increment=2+auto-increment-offset不同值,避免双主写入主键冲突
  4. 权限控制 :同步用户仅授予replication slave权限,最小权限原则保证安全
  5. 只读从库read_only=1防止从库被误写入,保证数据一致性

四、常见问题排查

  1. Replica_IO_Running: No
    • 检查主库地址 / 端口 / 用户密码是否正确
    • 检查主库 binlog 文件名和偏移量是否正确
    • 检查容器网络是否互通(ping mysql-master1在 slave 容器内测试)
  2. Replica_SQL_Running: No
    • 主从数据不一致(可重新初始化主库数据后同步)
    • 从库执行了主库未执行的操作(导致日志重放失败)
  3. MySQL 8.0 认证问题
    • 必须添加get_source_public_key=1参数,或修改用户认证方式为mysql_native_password
相关推荐
Polar__Star4 小时前
如何结合计划任务实现自动定时备份任务配置_全自动化运维管理
jvm·数据库·python
java资料站6 小时前
Docker 快速部署 MySQL 主从复制(一主一从)
mysql·adb·docker
weixin_580614009 小时前
如何提取SQL日期中的年份_使用YEAR或EXTRACT函数
jvm·数据库·python
Alex艾力的IT数字空间9 小时前
在 Kylin(麒麟)操作系统上搭建 Docker 环境
大数据·运维·缓存·docker·容器·负载均衡·kylin
2301_813599559 小时前
SQL生产环境规范_数据库使用最佳实践
jvm·数据库·python
嵌入式学习和实践9 小时前
虚拟机 Ubuntu 磁盘扩容完全指南:从原理到实践,一步到位
linux·ubuntu·磁盘扩容
a9511416429 小时前
Go 中通过 channel 传递切片时的数据竞争与深拷贝解决方案
jvm·数据库·python
qq_189807039 小时前
如何修改RAC数据库名_NID工具在集群环境下的改名步骤
jvm·数据库·python
aXin_ya9 小时前
Redis 高级篇(最佳实践)
数据库·redis·缓存