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.xxxxxxMASTER_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_Error 和 Last_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 说了什么?