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

MySQL 主从复制 — Docker 双机灾备方案 MySQL 主从复制 — Docker 双机灾备方案场景两台服务器各跑一个 MySQL Docker 容器目的主库挂了从库顶上防止数据丢失️ 环境项目主库服务器从库服务器IP192.168.1.100192.168.1.101容器名mysql_mastermysql_slave数据目录/data/mysql/master/data/mysql/slave第一部分主库服务器操作步骤 1拉镜像 建数据目录# 主库服务器上执行dockerpull mysql:5.6.51# 创建数据持久化目录容器删了数据还在mkdir-p/data/mysql/master步骤 2启动主库容器带 binlog 配置dockerrun-d\--namemysql_master\--restartalways\-p3306:3306\-v/data/mysql/master:/var/lib/mysql\-eMYSQL_ROOT_PASSWORD你的密码\mysql:5.6.51\--log-binmysql-bin\--server-id1\--binlog-formatROW\--expire-logs-days7--restart always 服务器重启后容器自动启动-v挂载数据目录 容器删了重跑数据还在验证dockerexecmysql_master mysql-uroot-p你的密码-eSHOW MASTER STATUS;正常输出------------------------------------------------------------ | File | Position | ... | | ------------------------------------------------------------ | mysql-bin.000001 | 120 | | | ------------------------------------------------------------步骤 3建复制用户dockerexecmysql_master mysql-uroot-p你的密码-e CREATE USER replicator% IDENTIFIED BY repl_password; GRANT REPLICATION SLAVE ON *.* TO replicator%; FLUSH PRIVILEGES;步骤 4确认主库容器 IPdockerinspect mysql_master--format{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}记下这个 IP例如172.17.0.2。第二部分从库服务器操作步骤 1建数据目录 启动从库容器# 从库服务器上执行dockerpull mysql:5.6.51mkdir-p/data/mysql/slavedockerrun-d\--namemysql_slave\--restartalways\-p3306:3306\-v/data/mysql/slave:/var/lib/mysql\-eMYSQL_ROOT_PASSWORD你的密码\mysql:5.6.51\--server-id2步骤 2把主库数据复制到从库⚠️重点从库开始复制之前数据必须和主库当时的状态一致在主库服务器上导出# 主库服务器执行dockerexecmysql_master mysqldump-uroot-p你的密码\--all-databases --master-data --single-transaction/tmp/master_dump.sql# 查看 dump 文件里记录的 binlog 位置grepMASTER_LOG_POS/tmp/master_dump.sql# 会输出类似: CHANGE MASTER TO MASTER_LOG_FILEmysql-bin.000001, MASTER_LOG_POS494;传到从库服务器# 主库服务器执行scp/tmp/master_dump.sql root192.168.1.101:/tmp/在从库服务器上恢复# 从库服务器执行dockerexec-imysql_slave mysql-uroot-p你的密码/tmp/master_dump.sql步骤 3配置从库连接主库dockerexecmysql_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不是容器内部 IPDocker 容器跨服务器通信用宿主机 IP 端口映射步骤 4验证复制状态dockerexecmysql_slave mysql-uroot-p你的密码-eSHOW 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: 空 → 没有包裹损坏要查具体某张表有没有同步需要单独查-- 主库查SELECTCOUNT(*)FROM你的表名;-- 从库查SELECTCOUNT(*)FROM你的表名;两条数一样 → 那张表同步好了。关键检查项字段必须等于含义Slave_IO_RunningYes从库在正常拉取主库日志Slave_SQL_RunningYes从库在正常回放日志Seconds_Behind_Master0延迟 0 秒实时同步Last_Error空没有报错如果有任何No查看Last_IO_Error和Last_SQL_Error看具体原因。第三部分验证同步主库写入数据在主库服务器上执行dockerexecmysql_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, 数据已同步);从库查询在从库服务器上执行dockerexecmysql_slave mysql-uroot-p你的密码-eSELECT * FROM test_sync.t1;如果输出1 | 数据已同步→主从复制正常工作✅第四部分主库故障切换最重要的操作场景主库服务器宕机了怎么办步骤 1确认从库已追上主库# 在从库服务器上执行dockerexecmysql_slave mysql-uroot-p你的密码-eSHOW SLAVE STATUS\G找这句Slave_SQL_Running_State: Slave has read all relay log→ 说明所有数据已追平步骤 2将从库提升为新主库dockerexecmysql_slave mysql-uroot-p你的密码-e STOP SLAVE; RESET SLAVE ALL;# 删掉容器重新以主库模式启动dockerstop mysql_slavedockerrmmysql_slavedockerrun-d\--namemysql_new_master\--restartalways\-p3306:3306\-v/data/mysql/slave:/var/lib/mysql\-eMYSQL_ROOT_PASSWORD你的密码\mysql:5.6.51\--log-binmysql-bin\--server-id2\--binlog-formatROW步骤 3切换应用连接把应用里的数据库连接 IP 从192.168.1.100改成192.168.1.101重启应用。第五部分Docker 注意事项容器重启策略dockerupdate--restartalways mysql_masterdockerupdate--restartalways mysql_slave数据备份cargorun--release--manifest-path mysql_backup/Cargo.toml -- backup\--userroot--password你的密码\--dockermysql_master\--keep-days30\--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第六部分常见问题与排错问题 1Slave_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 主库服务器IP3306# 或nc-zv主库服务器IP3306# 3. 检查主库容器是否在运行dockerps|grepmysql_master# 4. 检查主库是否监听了 0.0.0.0dockerexecmysql_master mysql-uroot-p密码-eSHOW VARIABLES LIKE bind_address;# 5. 检查复制用户和密码dockerexecmysql_master mysql-uroot-p密码-eSELECT user, host FROM mysql.user WHERE userreplicator;解决防火墙没开 3306 端口 → 开防火墙Docker 端口映射不对 →docker run时加-p 3306:3306MySQL 只绑了 127.0.0.1 → 改为bind_address 0.0.0.0密码错了 →ALTER USER replicator% IDENTIFIED BY 正确密码;问题 2Slave_SQL_Running: No现象Slave_SQL_Running: No Last_SQL_Error: Error Table xxx doesnt exist on query. ...原因从库缺了某张表通常是复制前没同步老数据解决方法 A跳过这条错误紧急恢复复制STOP SLAVE;SETGLOBALSQL_SLAVE_SKIP_COUNTER1;STARTSLAVE;重复直到错误消失。这会导致被跳过的数据丢失。方法 B重新同步最彻底# 1. 主库重新导出带 --master-data 自动记录位置dockerexecmysql_master mysqldump-uroot-p密码\--all-databases --master-data --single-transaction/tmp/full_dump.sql# 2. 从库重置dockerexecmysql_slave mysql-uroot-p密码-eSTOP SLAVE; RESET SLAVE ALL;# 3. 从库重新导入dockerexec-imysql_slave mysql-uroot-p密码/tmp/full_dump.sql# 4. 重新配置复制用 dump 文件自带的位置自动配置# --master-data 参数会在 dump 最前面写入 CHANGE MASTER 语句# 直接启动就行如果 dump 里带了 CHANGE MASTERdockerexecmysql_slave mysql-uroot-p密码-eSTART SLAVE;问题 3Seconds_Behind_Master 越来越大现象延迟从 0 秒慢慢涨到几秒、几分钟、几小时原因从库服务器性能比主库差CPU / 磁盘 IO 跟不上主库有大事务如一次性删 100 万行网络带宽不够解决# 1. 检查从库资源dockerstats mysql_slave --no-stream# 2. 检查主库是否有长时间运行的查询dockerexecmysql_master mysql-uroot-p密码-eSHOW PROCESSLIST;# 3. 临时方案从库追上来后会自己恢复# 4. 长期方案升级从库服务器配置问题 4主库容器重启后复制中断现象主库 Docker 重启后从库Slave_IO_Running: Connecting原因主库 Docker 重启后 IP 变了宿主机重启或 Docker 网络重建解决# 1. 查看主库新 IPdockerinspect mysql_master--format{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}# 2. 从库重新指向新 IPdockerexecmysql_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. 确保从库只读dockerexecmysql_slave mysql-uroot-p密码-eSET GLOBAL read_only ON;# 2. 跳过冲突STOP SLAVE;SET GLOBAL SQL_SLAVE_SKIP_COUNTER1;START SLAVE;问题 6binlog 写满磁盘现象主库磁盘满了MySQL 无法写入原因expire-logs-days没设或设太大解决# 1. 查看当前 binlogdockerexecmysql_master mysql-uroot-p密码-eSHOW BINARY LOGS;# 2. 手动清理已经同步过的 binlogdockerexecmysql_master mysql-uroot-p密码-ePURGE BINARY LOGS TO mysql-bin.000010;# 3. 预防容器启动时加 --expire-logs-days7问题 7GTID 相关错误现象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 位置跳到最新dockerexecmysql_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 说了什么