MySQL 主从复制 — Docker 双机灾备方案

MySQL 主从复制 --- Docker 双机灾备方案

场景:两台服务器,各跑一个 MySQL Docker 容器

目的:主库挂了从库顶上,防止数据丢失


🏗️ 环境

项目 主库服务器 从库服务器
IP 192.168.1.100 192.168.1.101
容器名 mysql_master mysql_slave
数据目录 /data/mysql/master /data/mysql/slave

第一部分:主库服务器操作

步骤 1:拉镜像 + 建数据目录

bash 复制代码
# 主库服务器上执行
docker pull mysql:5.6.51

# 创建数据持久化目录(容器删了数据还在)
mkdir -p /data/mysql/master

步骤 2:启动主库容器(带 binlog 配置)

bash 复制代码
docker run -d \
  --name mysql_master \
  --restart always \
  -p 3306:3306 \
  -v /data/mysql/master:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=你的密码 \
  mysql:5.6.51 \
  --log-bin=mysql-bin \
  --server-id=1 \
  --binlog-format=ROW \
  --expire-logs-days=7

--restart always = 服务器重启后容器自动启动

-v 挂载数据目录 = 容器删了重跑数据还在

验证

bash 复制代码
docker exec mysql_master mysql -uroot -p'你的密码' -e "SHOW MASTER STATUS;"

正常输出:

复制代码
+------------------+----------+--------------+------------------+
| File             | Position | ...          |                  |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 |      120 |              |                  |
+------------------+----------+--------------+------------------+

步骤 3:建复制用户

bash 复制代码
docker exec mysql_master mysql -uroot -p'你的密码' -e "
  CREATE USER 'replicator'@'%' IDENTIFIED BY 'repl_password';
  GRANT REPLICATION SLAVE ON *.* TO 'replicator'@'%';
  FLUSH PRIVILEGES;"

步骤 4:确认主库容器 IP

bash 复制代码
docker inspect mysql_master --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

记下这个 IP(例如 172.17.0.2)。


第二部分:从库服务器操作

步骤 1:建数据目录 + 启动从库容器

bash 复制代码
# 从库服务器上执行
docker pull mysql:5.6.51
mkdir -p /data/mysql/slave

docker run -d \
  --name mysql_slave \
  --restart always \
  -p 3306:3306 \
  -v /data/mysql/slave:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=你的密码 \
  mysql:5.6.51 \
  --server-id=2

步骤 2:把主库数据复制到从库

⚠️ 重点:从库开始复制之前,数据必须和主库当时的状态一致

在主库服务器上导出

bash 复制代码
# 主库服务器执行
docker exec mysql_master mysqldump -uroot -p'你的密码' \
  --all-databases --master-data --single-transaction > /tmp/master_dump.sql

# 查看 dump 文件里记录的 binlog 位置
grep "MASTER_LOG_POS" /tmp/master_dump.sql
# 会输出类似: CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=494;

传到从库服务器

bash 复制代码
# 主库服务器执行
scp /tmp/master_dump.sql root@192.168.1.101:/tmp/

在从库服务器上恢复

bash 复制代码
# 从库服务器执行
docker exec -i mysql_slave mysql -uroot -p'你的密码' < /tmp/master_dump.sql

步骤 3:配置从库连接主库

bash 复制代码
docker exec mysql_slave mysql -uroot -p'你的密码' -e "
  CHANGE MASTER TO
    MASTER_HOST = '主库服务器IP',
    MASTER_PORT = 3306,
    MASTER_USER = 'replicator',
    MASTER_PASSWORD = 'repl_password',
    MASTER_LOG_FILE = 'mysql-bin.xxxxxx',
    MASTER_LOG_POS = xxxx;

  START SLAVE;"

⚠️ 把以下 2 项替换成你主库 SHOW MASTER STATUS 输出的真实值:

  • MASTER_LOG_FILE → 替换 mysql-bin.xxxxxx
  • MASTER_LOG_POS → 替换 xxxx

⚠️ MASTER_HOST主库服务器的宿主机 IP (不是容器内部 IP)

Docker 容器跨服务器通信用宿主机 IP + 端口映射

步骤 4:验证复制状态

bash 复制代码
docker exec mysql_slave mysql -uroot -p'你的密码' -e "SHOW SLAVE STATUS\G"

⚠️ 常见误区SHOW SLAVE STATUS 不是查某张表的,而是查复制管道通不通

SHOW SLAVE STATUS 怎么看

复制代码
Slave_IO_Running: Yes        ← 从库到主库的网络连接正常吗?
Slave_SQL_Running: Yes       ← 从库在正常执行同步吗?
Seconds_Behind_Master: 0     ← 延迟几秒?(0 = 实时同步)
Last_Error:                  ← 有没有报错?

比喻理解

复制代码
主库 ────────→ 从库

Slave_IO_Running: Yes   → 快递员在正常取件
Slave_SQL_Running: Yes  → 快递员在正常派件
Seconds_Behind_Master: 0 → 当天件当天送完
Last_Error: 空           → 没有包裹损坏

要查具体某张表有没有同步,需要单独查

sql 复制代码
-- 主库查
SELECT COUNT(*) FROM 你的表名;

-- 从库查
SELECT COUNT(*) FROM 你的表名;

两条数一样 → 那张表同步好了。

关键检查项

字段 必须等于 含义
Slave_IO_Running Yes 从库在正常拉取主库日志
Slave_SQL_Running Yes 从库在正常回放日志
Seconds_Behind_Master 0 延迟 0 秒(实时同步)
Last_Error 没有报错

如果有任何 No,查看 Last_IO_ErrorLast_SQL_Error 看具体原因。


第三部分:验证同步

主库写入数据(在主库服务器上执行):

bash 复制代码
docker exec mysql_master mysql -uroot -p'你的密码' -e "
  CREATE DATABASE test_sync;
  USE test_sync;
  CREATE TABLE t1 (id INT, msg VARCHAR(50));
  INSERT INTO t1 VALUES (1, '数据已同步');"

从库查询(在从库服务器上执行):

bash 复制代码
docker exec mysql_slave mysql -uroot -p'你的密码' -e "SELECT * FROM test_sync.t1;"

如果输出 1 | 数据已同步主从复制正常工作


第四部分:主库故障切换(最重要的操作)

场景:主库服务器宕机了,怎么办?

步骤 1:确认从库已追上主库

bash 复制代码
# 在从库服务器上执行
docker exec mysql_slave mysql -uroot -p'你的密码' -e "SHOW SLAVE STATUS\G"

找这句:Slave_SQL_Running_State: Slave has read all relay log

→ 说明所有数据已追平

步骤 2:将从库提升为新主库

bash 复制代码
docker exec mysql_slave mysql -uroot -p'你的密码' -e "
  STOP SLAVE;
  RESET SLAVE ALL;"

# 删掉容器重新以主库模式启动
docker stop mysql_slave
docker rm mysql_slave

docker run -d \
  --name mysql_new_master \
  --restart always \
  -p 3306:3306 \
  -v /data/mysql/slave:/var/lib/mysql \
  -e MYSQL_ROOT_PASSWORD=你的密码 \
  mysql:5.6.51 \
  --log-bin=mysql-bin \
  --server-id=2 \
  --binlog-format=ROW

步骤 3:切换应用连接

把应用里的数据库连接 IP 从 192.168.1.100 改成 192.168.1.101,重启应用。


第五部分:Docker 注意事项

容器重启策略

bash 复制代码
docker update --restart always mysql_master
docker update --restart always mysql_slave

数据备份

bash 复制代码
cargo run --release --manifest-path mysql_backup/Cargo.toml -- backup \
  --user root --password 你的密码 \
  --docker mysql_master \
  --keep-days 30 \
  --output /backups/mysql

日常检查命令速查表

操作 命令
查看主库状态 docker exec mysql_master mysql -uroot -p密码 -e "SHOW MASTER STATUS\G"
查看从库状态 docker exec mysql_slave mysql -uroot -p密码 -e "SHOW SLAVE STATUS\G"
停止复制 docker exec mysql_slave mysql -uroot -p密码 -e "STOP SLAVE;"
恢复复制 docker exec mysql_slave mysql -uroot -p密码 -e "START SLAVE;"
进入 MySQL docker exec -it mysql_master mysql -uroot -p密码
查看日志 docker logs mysql_master --tail 50

第六部分:常见问题与排错

问题 1:Slave_IO_Running: No

现象

复制代码
Slave_IO_Running: No
Last_IO_Error: error connecting to master 'replicator@主库IP:3306' - retry-time: 60

原因:从库连不上主库

排查步骤

bash 复制代码
# 1. 在从库服务器上测试网络通不通
ping 主库服务器IP

# 2. 测试端口通不通
telnet 主库服务器IP 3306
# 或
nc -zv 主库服务器IP 3306

# 3. 检查主库容器是否在运行
docker ps | grep mysql_master

# 4. 检查主库是否监听了 0.0.0.0
docker exec mysql_master mysql -uroot -p密码 -e "SHOW VARIABLES LIKE 'bind_address';"

# 5. 检查复制用户和密码
docker exec mysql_master mysql -uroot -p密码 -e "SELECT user, host FROM mysql.user WHERE user='replicator';"

解决

  • 防火墙没开 3306 端口 → 开防火墙
  • Docker 端口映射不对 → docker run 时加 -p 3306:3306
  • MySQL 只绑了 127.0.0.1 → 改为 bind_address = 0.0.0.0
  • 密码错了 → ALTER USER 'replicator'@'%' IDENTIFIED BY '正确密码';

问题 2:Slave_SQL_Running: No

现象

复制代码
Slave_SQL_Running: No
Last_SQL_Error: Error 'Table 'xxx' doesn't exist' on query. ...

原因:从库缺了某张表(通常是复制前没同步老数据)

解决

方法 A:跳过这条错误(紧急恢复复制)

sql 复制代码
STOP SLAVE;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE;

重复直到错误消失。这会导致被跳过的数据丢失。

方法 B:重新同步(最彻底)

bash 复制代码
# 1. 主库重新导出(带 --master-data 自动记录位置)
docker exec mysql_master mysqldump -uroot -p密码 \
  --all-databases --master-data --single-transaction > /tmp/full_dump.sql

# 2. 从库重置
docker exec mysql_slave mysql -uroot -p密码 -e "STOP SLAVE; RESET SLAVE ALL;"

# 3. 从库重新导入
docker exec -i mysql_slave mysql -uroot -p密码 < /tmp/full_dump.sql

# 4. 重新配置复制(用 dump 文件自带的位置自动配置)
# --master-data 参数会在 dump 最前面写入 CHANGE MASTER 语句
# 直接启动就行(如果 dump 里带了 CHANGE MASTER)
docker exec mysql_slave mysql -uroot -p密码 -e "START SLAVE;"

问题 3:Seconds_Behind_Master 越来越大

现象:延迟从 0 秒慢慢涨到几秒、几分钟、几小时

原因

  • 从库服务器性能比主库差(CPU / 磁盘 IO 跟不上)
  • 主库有大事务(如一次性删 100 万行)
  • 网络带宽不够

解决

bash 复制代码
# 1. 检查从库资源
docker stats mysql_slave --no-stream

# 2. 检查主库是否有长时间运行的查询
docker exec mysql_master mysql -uroot -p密码 -e "SHOW PROCESSLIST;"

# 3. 临时方案:从库追上来后会自己恢复
# 4. 长期方案:升级从库服务器配置

问题 4:主库容器重启后复制中断

现象 :主库 Docker 重启后,从库 Slave_IO_Running: Connecting

原因:主库 Docker 重启后 IP 变了(宿主机重启或 Docker 网络重建)

解决

bash 复制代码
# 1. 查看主库新 IP
docker inspect mysql_master --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

# 2. 从库重新指向新 IP
docker exec mysql_slave mysql -uroot -p密码 -e "
  STOP SLAVE;
  CHANGE MASTER TO MASTER_HOST = '新IP';
  START SLAVE;"

预防 :如果是跨服务器,不要用容器 IP,用宿主机 IP + 端口映射

问题 5:主键冲突导致复制中断

现象

复制代码
Last_SQL_Error: Duplicate entry 'xxx' for key 'PRIMARY'

原因 :从库也写了数据(read_only 没开),导致主键冲突

解决

bash 复制代码
# 1. 确保从库只读
docker exec mysql_slave mysql -uroot -p密码 -e "SET GLOBAL read_only = ON;"

# 2. 跳过冲突
STOP SLAVE;
SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1;
START SLAVE;

问题 6:binlog 写满磁盘

现象:主库磁盘满了,MySQL 无法写入

原因expire-logs-days 没设或设太大

解决

bash 复制代码
# 1. 查看当前 binlog
docker exec mysql_master mysql -uroot -p密码 -e "SHOW BINARY LOGS;"

# 2. 手动清理已经同步过的 binlog
docker exec mysql_master mysql -uroot -p密码 -e "PURGE BINARY LOGS TO 'mysql-bin.000010';"

# 3. 预防:容器启动时加 --expire-logs-days=7

问题 7:GTID 相关错误

现象

复制代码
Last_IO_Error: The slave is connecting using CHANGE MASTER TO MASTER_AUTO_POSITION = 1, but the master has purged binary logs containing GTIDs that the slave requires.

原因:主库清理了从库还没同步的 binlog

解决

bash 复制代码
# 从库重新指向当前 binlog 位置(跳到最新)
docker exec mysql_slave mysql -uroot -p密码 -e "
  STOP SLAVE;
  CHANGE MASTER TO MASTER_AUTO_POSITION = 0;
  START SLAVE;"

排错检查清单

当复制出问题时,按顺序排查:

复制代码
□ 从库能 ping 通主库吗?
□ 主库 3306 端口能 telnet 通吗?
□ 主库容器在运行吗?(docker ps)
□ 主库 binlog 开启了吗?(SHOW MASTER STATUS)
□ 复制用户存在且密码正确吗?
□ 防火墙放行了 3306 吗?
□ 从库数据是否和主库一致?
□ Slave_IO_Running 和 Slave_SQL_Running 分别是什么?
□ Last_IO_Error 和 Last_SQL_Error 说了什么?
相关推荐
染翰1 小时前
生产级 MySQL 内存占用过高排查指南
数据库·mysql
一 乐1 小时前
网上订餐系统|基于springboot的网上订餐系统设计与实现(源码+数据库+文档)
java·数据库·spring boot·后端·论文·毕设·网上订餐系统
guslegend2 小时前
第3节:智能体配置表设计
数据库·人工智能
jiayong232 小时前
MySQL 排序规则冲突问题与 utf8mb4_general_ci 统一方案
android·mysql·ci/cd
ai产品老杨2 小时前
【架构实战】如何基于 Docker 与边缘计算构建企业级 AI 视频管理平台?打通 GB28181/RTSP 统一接入与异构算力调度,全量源码交付破解集成痛点
人工智能·docker·架构
雷工笔记2 小时前
SQL系列2:PostgreSQL 日期时间字段类型选择指南
数据库·sql·postgresql
IT策士2 小时前
Docker 从 0 到 1 再到 Kubernetes 实战:第18篇 从 Docker Compose 到 Kubernetes 的思考
docker·容器·kubernetes
SAP上海工博云署2 小时前
2026年中小企业SAP服务商选型技术解析
大数据·运维·数据库·人工智能·信息可视化·运维开发·信息与通信
RestCloud2 小时前
版本迭代丨谷云科技ETLCloud V4.2版本更新速览
数据库·doris·etl·etlcloud·数据集成平台·datahub·ftp处理