运维,数据库,mysql··约 20 分钟读完

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

MySQL Docker 主从复制灾备方案 本文详细介绍了在 Docker 环境下搭建 MySQL 主从复制的灾备方案。主要内容包括: 环境准备 - 在两台服务器上分别部署主库和从库容器 主库配置 - 设置 binlog、创建复制用户并导出初始数据 从库配置 - 导入主库数据并建立复制链路 验证同步 - 通过创建测试数据验证复制功能正常 故障切换 - 当主库宕机时,将从库提升为新主库的操作步骤 方案特点: 使用 Docker 容器化部署 数据目录持久化存储 提供详细的复制状态检查方法 包含主库故障时的应急切
docker数据库mysql

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

场景:两台服务器,各跑一个 MySQL Docker 容器 目的:主库挂了从库顶上,防止数据丢失


🏗️ 环境

项目主库服务器从库服务器
IP192.168.1.100192.168.1.101
容器名mysql_mastermysql_slave
数据目录/data/mysql/master/data/mysql/slave

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

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

# 主库服务器上执行 docker pull mysql:5.6.51 # 创建数据持久化目录(容器删了数据还在) mkdir -p /data/mysql/master

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

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 挂载数据目录 = 容器删了重跑数据还在

验证

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

正常输出:

+------------------+----------+--------------+------------------+ | File | Position | ... | | +------------------+----------+--------------+------------------+ | mysql-bin.000001 | 120 | | | +------------------+----------+--------------+------------------+

步骤 3:建复制用户

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

docker inspect mysql_master --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'

记下这个 IP(例如 172.17.0.2)。


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

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

# 从库服务器上执行 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:把主库数据复制到从库

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

在主库服务器上导出

# 主库服务器执行 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;

传到从库服务器

# 主库服务器执行 scp /tmp/master_dump.sql root@192.168.1.101:/tmp/

在从库服务器上恢复

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

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

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:验证复制状态

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: 空 → 没有包裹损坏

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

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

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

关键检查项

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

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


第三部分:验证同步

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

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, '数据已同步');"

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

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

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


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

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

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

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

找这句:Slave_SQL_Running_State: Slave has read all relay log → 说明所有数据已追平

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

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 注意事项

容器重启策略

docker update --restart always mysql_master docker update --restart always mysql_slave

数据备份

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;"
进入 MySQLdocker 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

原因:从库连不上主库

排查步骤

# 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:跳过这条错误(紧急恢复复制)

STOP SLAVE; SET GLOBAL SQL_SLAVE_SKIP_COUNTER = 1; START SLAVE;

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

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

# 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 万行)
  • 网络带宽不够

解决

# 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 网络重建)

解决

# 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 没开),导致主键冲突

解决

# 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 没设或设太大

解决

# 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

解决

# 从库重新指向当前 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 说了什么?