从库备份前必须确认复制静止:slave_io_running和slave_sql_running均为yes,seconds_behind_master≤5且不为null,gtid模式下retrieved_gtid_set=executed_gtid_set,非gtid模式下relay_master_log_file和exec_master_log_pos稳定不变。

从库备份前必须确认的三个复制状态
直接在从库跑 mysqldump 能否无锁,不取决于命令本身,而取决于复制是否真正“静止”。哪怕只差 1 秒延迟,mysqldump --single-transaction 导出的数据也可能漏掉刚写入 relay log 但还没执行的事务。务必在从库执行:
-
SHOW SLAVE STATUS\G中Slave_IO_Running和Slave_SQL_Running都为Yes -
Seconds_Behind_Master≤ 5(不是 “0” 就够了,但不能是 NULL 或几十秒) - GTID 模式下,
Retrieved_Gtid_Set必须完全等于Executed_Gtid_Set;非 GTID 模式下,Relay_Master_Log_File和Exec_Master_Log_Pos必须稳定不变(连续查两次不跳变)
mysqldump 无锁导出必须加的关键参数组合
--single-transaction 是 InnoDB 表免锁的核心,但它不是万能开关——漏掉配套参数,照样会触发锁或导出失效:
- 必须显式加
--skip-lock-tables:虽然--single-transaction默认禁用--lock-tables,但某些 MySQL 版本或客户端环境仍可能 fallback,加这层保险能杜绝FLUSH TABLES WITH READ LOCK被意外触发 - 必须加
--set-gtid-purged=OFF:否则导出会写死SET @@GLOBAL.GTID_PURGED,还原到已有 GTID 历史的实例时直接报错GTID_PURGED can only be set when GTID_EXECUTED is empty - 禁用
--master-data:它为主库设计,记录的是主库 binlog 位点;从库上用它没意义,还可能因未开启log_slave_updates导致位点为空或错误 - 推荐加
--dump-slave=2:它输出的是从库当前的CHANGE MASTER TO语句(含Relay_Master_Log_File和Exec_Master_Log_Pos),这才是从库备份后恢复复制的正确起点
为什么不能边 dump 边让 SQL 线程跑着
很多人以为只要 Seconds_Behind_Master = 0 就能放心 dump,其实危险在于“动态一致性”:SQL 线程仍在运行,mysqldump 的 MVCC 快照读可能遇到三种情况:
- 刚被 SQL 线程插入的行,尚未提交,快照不可见 → 数据丢失
- SQL 线程正在执行 DDL(如
ALTER TABLE),表结构处于中间态,mysqldump可能读到部分列或报错Table definition has changed - 长事务 dump 过程中,purge 线程清理了 undo 日志,导致快照读失败,报错
Cannot execute SELECT on table xxx: Snapshot too old
真正安全的做法是:先 STOP SLAVE SQL_THREAD,再立刻执行 mysqldump,dump 完立即 START SLAVE SQL_THREAD。IO 线程保持运行,确保不丢新 binlog。
备份文件里藏着两个容易被忽略的坑
导出的 .sql 文件不是纯数据快照,它自带元信息和控制指令,稍不注意就会在还原时引发复制错乱:
- 检查文件开头是否有
START SLAVE;或SLAVE START;—— 如果有,说明没加--skip-slave-start,还原后会自动启动复制,但位置已错,大概率报Could not execute Write_rows event - 检查是否包含
mysql.slave_master_info和mysql.slave_relay_log_info表的INSERT语句 —— 如果有,还原会覆盖目标实例的复制元数据,导致指向错误 master 或 relay log 位置;应加--ignore-table=mysql.slave_master_info --ignore-table=mysql.slave_relay_log_info
最稳妥的验证方式:用 head -n 50 backup.sql | grep -E "(START SLAVE|slave_master_info)" 快速扫一遍。别等恢复失败了才回头翻日志。











