mysqldump --single-transaction 并非绝对不锁表,仅对 innodb 表在 repeatable read 隔离级别下有效;若存在 myisam 表、长事务或冲突参数(如 --lock-tables),则自动退化为锁表备份。

mysqldump 能安全导出超大数据集而不锁表,但必须满足三个前提:InnoDB 引擎、事务隔离级别为 REPEATABLE READ、且不能混用 MyISAM 表。缺一不可,否则 --single-transaction 会退化为锁表行为。
为什么 --single-transaction 不等于“绝对不锁表”
很多人以为加了 --single-transaction 就万事大吉,结果导出中途发现写操作被阻塞——这是因为该参数只对 InnoDB 有效,且依赖事务快照机制。如果库中存在 MyISAM 表,mysqldump 会自动 fallback 到 --lock-tables 模式(哪怕你显式写了 --lock-tables=false),导致全表锁定。
验证方式很简单:
- 执行
SELECT ENGINE, COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'your_db' GROUP BY ENGINE; - 若结果里有
MyISAM,就不能依赖--single-transaction实现无锁导出
--quick 是内存安全的底线,不是可选项
--quick(或简写 -q)让 mysqldump 逐行 fetch 数据并直接写入文件,避免把千万行结果全 load 到客户端内存。没它,导出一张 800 万行的表极可能触发 OOM 或被系统 kill。
常见错误现象:
- 导出进程突然消失,日志里出现
Killed(Linux OOM killer 干的) - MySQL 客户端报错
Lost connection to MySQL server during query - 导出文件只有几 KB,实际数据没写完
注意:--quick 和 --single-transaction 必须同时使用,二者解决不同问题:前者防内存溢出,后者保一致性。
分批导出时 --where 的引号和转义容易翻车
用 --where="id BETWEEN 1000000 AND 2000000" 分片导出很常见,但 shell 环境下引号处理极易出错:
- 在脚本里拼接变量时,别写成
--where="id > $start"—— 如果$start为空或含空格,命令直接失效 - 远程执行时(如
ssh user@host "mysqldump ..."),外层双引号会让内层引号被 shell 提前解析,建议改用单引号包裹整个命令,或用\转义 - WHERE 条件里含单引号(如
name='O''Reilly')必须手动转义,mysqldump不做 SQL 注入防护,也不会帮你修语法
更稳妥的做法是用 --where 配合整数主键范围,避开字符串和特殊字符。
SELECT INTO OUTFILE 看似快,但权限和路径限制极严
想绕过 mysqldump 直接落盘 CSV?SELECT INTO OUTFILE 确实快,但它要求:
- MySQL 用户必须有
FILE权限(高危权限,生产环境常被禁用) - 输出路径必须是 MySQL 服务端本地路径,且受
secure_file_priv严格限制 —— 查看当前允许目录:执行SHOW VARIABLES LIKE 'secure_file_priv'; - 无法跨库 JOIN 导出,也不能导出建表语句,纯数据文件需额外维护 schema
所以它适合 DBA 在服务器上临时提取分析数据,不适合自动化、跨环境或权限受限场景。误配路径只会得到错误:The MySQL server is running with the --secure-file-priv option so it cannot execute this statement。
--single-transaction、哪张必须切 MyISAM 或改用物理拷贝;也不是写对 --where,而是确保分片边界不漏不重、且主键连续无空洞。这些细节不验就不知道,一跑就卡住。











