mysqldump导出千万级表卡住或OOM,根本原因是默认mysql_store_result模式将整表结果全载入客户端内存,1000万行×2KB即20GB,触发OOM;必须用--quick启用流式读取,并配套--single-transaction防锁表、--skip-extended-insert防语句超限。

mysqldump 导出千万级表时为什么卡住或 OOM?
因为 mysqldump 默认逐行读取并拼接 SQL,对单表超千万行、平均行宽 >1KB 的场景,客户端内存极易爆掉;同时未加锁或锁表时间过长,会导致业务写入阻塞甚至超时。这不是工具不行,而是默认行为没适配大表场景。
关键不是“能不能导”,而是“怎么控制内存、锁、网络和重试”。实操中必须绕开 --opt(它默认启用 --extended-insert + --lock-tables),并手动拆解控制粒度:
-
--single-transaction必选(InnoDB 下一致性快照,不锁表),但需确认 binlog_format=ROW 且事务中无 DDL - 禁用
--lock-tables(它会锁全库所有表),显式用--skip-lock-tables - 用
--skip-extended-insert换成单行 INSERT,避免单条语句过大拖慢解析和重试;但更推荐保留--extended-insert+--net-buffer-length=1048576控制每批数据大小 - 加
--compress减少网络传输量(服务端和客户端都需启用zlib)
如何分批次导出并避免主键冲突?
直接 mysqldump 全量导出再导入,目标库若已有部分数据,INSERT INTO ... VALUES 会因主键重复失败。不能依赖 --replace 或 --insert-ignore —— 前者会删再插,触发外键/触发器/日志;后者静默丢数据,不可审计。
安全做法是导出时带上条件,并在导入前清理或跳过已存在范围:
- 用
--where="id > 1000000 AND id 分段导出,配合自增主键或时间字段做切片 - 导出文件名带范围标识:
mysqldump -u u -p db tbl --where="created_at >= '2023-01-01' AND created_at tbl_202301.sql - 导入前在目标库执行
DELETE FROM tbl WHERE created_at >= '2023-01-01' AND created_at ,再 source,确保幂等 - 避免用
SELECT * INTO OUTFILE替代 —— 它不带建表语句、无字符集声明、无法跨版本兼容
导入时如何提速且不拖垮从库?
直接 mysql -u u -p db 在从库执行,会逐条回放 SQL,IO 和 CPU 压力陡增,可能触发 Seconds_Behind_Master 飙升甚至复制中断。
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
必须调整服务端参数 + 客户端行为:
- 导入前在目标实例执行:
SET SESSION unique_checks=0; SET SESSION foreign_key_checks=0;(导入完记得设回 1) - 用
mysql --max-allowed-packet=512M -u u -p db启动,防止大INSERT被截断 - 导入命令加
--force(跳过单条错误继续,避免整个文件中断),但要配合日志重定向:mysql -u u -p db import.err - 若目标库为从库,先
STOP SLAVE;,导入完成再START SLAVE;,比开着复制硬扛更稳
为什么不能只靠 mysqldump?哪些情况该换方案?
当单表超过 5000 万行、或平均行宽 >2KB、或字段含 TEXT/BLOB,mysqldump 的文本序列化瓶颈就非常明显:导出慢、网络易断、导入后二级索引重建耗时远超数据加载本身。
这时应切换路径,而非调参硬扛:
- 同机房迁移:用
Percona XtraBackup物理备份,恢复快、不锁表、支持流式压缩传输 - 跨版本或异构迁移:先导出为 CSV(
SELECT ... INTO OUTFILE),再用LOAD DATA INFILE或mysqlimport,绕过 SQL 解析层 - 有停机窗口:停写后
FLUSH TABLES WITH READ LOCK+cp /var/lib/mysql/db/tbl.*,但仅限同版本、同页大小、同大小端
真正容易被忽略的是字符集与排序规则的隐式转换——哪怕源库 utf8mb4_unicode_ci,导出时没加 --default-character-set=utf8mb4,导入后中文可能乱码或比较异常,这个细节在分批迁移中极难排查。










