--single-transaction 并非真正“不锁表”,仅对InnoDB有效,遇MyISAM表、长事务、并发DDL或未加--skip-lock-tables时会降级为FLUSH TABLES WITH READ LOCK全局锁。

--single-transaction 并不能“自动不锁表”,它只对 InnoDB 表生效,且极易因环境条件退化为全局锁表。 是否真正避开 FLUSH TABLES WITH READ LOCK,得看你的库是否干净——有没有 MyISAM 表、长事务、中途 DDL,以及参数组合是否冲突。
为什么加了 --single-transaction 还是锁表
常见错误现象:mysqldump 执行时业务写入卡住,SHOW PROCESSLIST 看到大量 Waiting for table metadata lock;或者备份日志里出现 FLUSH TABLES WITH READ LOCK —— 这说明它已经降级了。
- 只要库中存在一张
MyISAM表,整个 dump 就会放弃事务快照,强制启用全局读锁 - 备份开始前已有运行超 60 秒的事务(
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW()) - TIME_TO_SEC(TRX_STARTED) > 60),会导致快照无法建立,mysqldump 卡住或报ERROR 1205 (HY000) - 备份过程中执行
ALTER TABLE、DROP TABLE等 DDL,会阻塞元数据锁,间接导致--single-transaction失效 - 误加了
--lock-tables(默认开启)或没显式加--skip-lock-tables,参数互相覆盖
如何验证 --single-transaction 是否真生效
临时打开 general log 是最直接的办法,但别忘了关:
- 执行
SET GLOBAL general_log = ON - 跑一次
mysqldump --single-transaction ... - 查日志:
SELECT * FROM mysql.general_log ORDER BY event_time DESC LIMIT 20 - 如果结果里出现
FLUSH TABLES WITH READ LOCK,就确认已降级 - 验证完立刻关掉:
SET GLOBAL general_log = OFF(否则严重拖慢性能)
稳妥的备份命令怎么写
不是加个参数就完事,关键在组合与前置检查:
- 先确认全是 InnoDB:
SELECT table_name, engine FROM information_schema.tables WHERE table_schema = 'your_db' - 清掉长事务,或协调业务低峰期执行
- 命令必须包含
--skip-lock-tables,否则--single-transaction被忽略 - 加上
--no-autocommit,避免恢复时逐行提交拖慢导入 - 导出存储过程和触发器:显式加
--routines --triggers --events(默认不导) - 示例:
mysqldump --single-transaction --skip-lock-tables --no-autocommit --routines --triggers --events -u root -p your_db > backup.sql
大库备份时容易被忽略的隐性代价
--single-transaction 不锁表,但会持有一个从 START TRANSACTION 开始的一致性快照。这意味着:
- InnoDB 必须保留所有被修改页对应的 undo 日志,直到 dump 结束
- 备份耗时越长,undo 日志膨胀越严重,可能撑爆
ibdata1或触发 purge 延迟 - 若备份中途失败,残留的长事务快照仍会阻碍 purge,需人工干预
KILL对应线程 - 客户端版本不能低于服务端(例如 MySQL 8.0 服务端用 5.7 客户端 dump,可能报
Unknown column type 'json')
真正决定是否锁表的,从来不是参数本身,而是你库的实时状态和参数之间的配合关系。一个 MyISAM 表、一个忘记 COMMIT 的事务、一次没避开的 ALTER,都足以让 --single-transaction 形同虚设。


















