--single-transaction 仅对 innodb 表生效且需满足 repeatable read 隔离级、无显式/隐式锁表操作;遇 myisam 表或长事务 ddl 会降级为 flush tables with read lock。

mysqldump 加 --single-transaction 为什么有时还是锁表?
不是加了 --single-transaction 就一定不锁表——它只对 InnoDB 表生效,且要求事务隔离级别是 REPEATABLE READ(MySQL 5.7 默认就是),同时整个 dump 过程中不能有显式 LOCK TABLES、不能有长事务正在执行 DDL(比如 ALTER TABLE)、也不能有其他会隐式升级为表级锁的操作(如对 MyISAM 表的访问)。
常见错误现象:mysqldump 执行时,业务查询变慢甚至卡住,SHOW PROCESSLIST 看到大量 Waiting for table metadata lock。
- 检查当前库是否混用存储引擎:运行
SELECT TABLE_NAME, ENGINE FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_db';,只要存在MyISAM或MEMORY表,--single-transaction对它们无效,dump 时会自动触发FLUSH TABLES WITH READ LOCK - 确认没有活跃的 DDL:执行
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(TIMEDIFF(NOW(), trx_started)) > 60;查看是否有运行超 1 分钟的事务;DDL 操作(如ALTER)会阻塞single-transaction的快照建立 - 避免在 dump 命令里混用冲突参数:例如同时指定
--lock-all-tables或--lock-tables,它们会直接覆盖--single-transaction的行为
如何验证 --single-transaction 是否真正生效?
关键看 dump 开始时是否跳过了全局读锁。最直接的方式是开启 general log 或抓包观察 mysqldump 发送的语句序列:
生效时典型流程:SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ → START TRANSACTION /*!40108 WITH CONSISTENT SNAPSHOT */ → 逐表 SELECT;
失效时会出现:FLUSH TABLES WITH READ LOCK → SHOW MASTER STATUS → … → UNLOCK TABLES。
- 临时启用 general log:执行
SET GLOBAL general_log = ON;,然后跑一次 dump,再查SELECT * FROM mysql.general_log ORDER BY event_time DESC LIMIT 20; - 如果看到
FLUSH TABLES WITH READ LOCK,说明触发了降级逻辑,大概率是遇到了非 InnoDB 表或并发 DDL - 注意:general log 会影响性能,验证完记得关掉:
SET GLOBAL general_log = OFF;
备份大库时 --single-transaction 的隐性代价
它不锁表,但会持有一个一致性快照,这意味着从 START TRANSACTION 开始,InnoDB 必须保留所有被修改页的 undo 日志,直到 dump 结束。对于写入频繁、单表超 10 GB 的库,可能引发两个问题:
- undo 表空间暴涨,甚至填满磁盘(尤其当
innodb_undo_log_truncate=OFF或innodb_max_undo_log_size设得过大) - dump 进程本身变慢,因为 MVCC 可见性判断开销随活跃事务数线性上升;若 dump 耗时超过 1 小时,建议拆分表或用
--where分批导出 -
mysqldump --single-transaction不保证主从延迟下的“逻辑一致”:它只对 dump 开始那一刻的 InnoDB 数据打快照,但 binlog position 是执行SHOW MASTER STATUS时取的——这两者有微小时间差,做 PITR(基于时间点恢复)时需留意
替代方案:什么情况下不该硬扛 --single-transaction?
当库中有少量 MyISAM 表,又不想停写,可以考虑绕过锁表,而不是强行改存储引擎:
- 用
--ignore-table=db.myisam_table排除问题表,单独处理它们(比如停写后用mysqlhotcopy或直接 cp .MYD/.MYI 文件) - 对整库做物理备份更稳妥:Percona XtraBackup 5.7 支持
--parallel和--rsync,对 InnoDB 自动用 crash-safe 备份,对 MyISAM 也能在短暂 flush 后拷贝,总锁表时间远低于 mysqldump - 如果只是想避免业务影响,可结合
--skip-lock-tables+--single-transaction,但必须确保无 DDL 且无 MyISAM,否则 dump 数据可能不一致
真正麻烦的从来不是参数怎么写,而是你没意识到那个被遗忘的 archive 表还躺在 ENGINE=MyISAM 上,等 dump 一启动就悄悄锁了全库。











