source 命令单线程逐行执行、无并发与进度反馈,百万级 insert 易卡死且无法中断恢复;不校验语法、不跳过注释、不处理字符集转换;建议分片导入并加 -v 参数定位报错。

source 命令本身不支持并发或进度反馈
直接在 MySQL 客户端里执行 source /path/to/large.sql 看似简单,但实际是单线程逐行解析执行,遇到百万级 INSERT 会卡住无响应,且无法中断恢复。它不校验语法、不跳过注释、不处理字符集隐式转换——你看到的“卡住”,大概率是某条语句在等待锁,或因默认 max_allowed_packet 太小被截断。
实操建议:
- 先用
mysql --defaults-file=/etc/my.cnf -u root -p 替代 <code>source,绕过客户端解析开销 - 确保服务端
max_allowed_packet≥ 文件中最大单条语句(常见设为512M) - 导入前手动删掉 SQL 文件头部的
SET NAMES utf8mb4类语句——它们在非交互式导入中可能失效,改用连接参数指定:--default-character-set=utf8mb4
禁用唯一性检查和自动提交能提速 3–10 倍
默认情况下,每条 INSERT 都触发索引更新 + 事务日志写入,对大文件就是灾难。关掉它们不是“偷懒”,而是必要前置操作——只要确认数据本身无主键/唯一键冲突即可。
实操建议:
- 在 SQL 文件开头插入:
SET unique_checks=0; SET foreign_key_checks=0; SET autocommit=0; - 结尾补上:
COMMIT; SET unique_checks=1; SET foreign_key_checks=1; - 若文件本身含
CREATE TABLE,记得把ENGINE=InnoDB显式写全,避免低版本默认用 MyISAM 导致后续锁表问题
分卷导入比硬扛更可控
一个 2GB 的 SQL 文件出错就得重来;而拆成 50MB/份后,失败只需重试其中一份。关键是别用 split 硬切——它不管 INSERT 边界,极易切坏语句。
实操建议:
- 用
sed -n '/^INSERT INTO/,/^;/p' large.sql | split -l 50000 - chunk_按 INSERT 块切分(需预估每块行数) - 更稳的方式:用
awk '/^INSERT INTO/{n++} {print > "chunk_" int((n-1)/100) ".sql"}' large.sql,按每 100 个 INSERT 分一份 - 导入时加
-v参数观察报错位置:mysql -v -u root -p db_name
innodb_buffer_pool_size 和 log_file_size 必须调大
默认配置下,InnoDB 缓冲池只有 128MB,面对千万行数据,磁盘 IO 成瓶颈。同时,innodb_log_file_size 过小会导致频繁 checkpoint,大幅拖慢批量写入。
实操建议:
- 临时调大(导入完可改回):
innodb_buffer_pool_size = 2G(物理内存 50% 左右) -
innodb_log_file_size = 512M(注意:修改此值需先停库、删旧日志文件、再启库) - 确认
innodb_flush_log_at_trx_commit = 2(牺牲极小安全性换速度,导入后可改回 1)
真正卡住的时候,往往不是 SQL 写得不对,而是这些底层参数还躺在默认值里没动过。











