根本原因是每条INSERT实时维护索引、刷日志、校验约束;索引越多、事务越碎、日志越严,性能下降越显著。

导入大型 SQL 文件时速度越来越慢,根本原因不是数据量变大,而是每条 INSERT 都在实时维护索引、刷日志、校验约束——索引越多、事务越碎、日志越严,拖得越狠。
为什么 INSERT 越插越慢?不是数据量问题,是索引和日志在卡脖子
MySQL 插入一行,实际要干这些事:写主键 B+ 树 + 写所有二级索引 B+ 树 + 写 binlog + 刷 redo log + 检查外键 + 检查唯一性。索引从 2 个涨到 5 个,单行耗时可能翻 2–3 倍;如果每条 INSERT 都单独提交(默认 autocommit=1),那还要额外付出刷盘开销。
常见错误现象:
- 前 10 万行 2 分钟,后 10 万行要 8 分钟
-
SHOW PROCESSLIST显示大量updating状态,但磁盘%util不高(iostat -x 1可验证) - 导入中途 MySQL 连接断开,报错
Lost connection to MySQL server during query(通常是max_allowed_packet不够)
导入前必须关掉的三个检查项:unique_checks、foreign_key_checks、autocommit
这三项不关,等于边盖楼边拆墙——索引重建和约束校验会全程伴随每条语句。
-
SET unique_checks = 0:停用唯一索引(含主键)的重复校验(注意:确保数据本身无冲突) -
SET foreign_key_checks = 0:跳过外键引用检查(避免逐行查父表) -
SET autocommit = 0:禁用自动提交,后续靠显式COMMIT控制事务粒度 - 导入完成后必须立刻恢复:
SET unique_checks = 1、SET foreign_key_checks = 1、COMMIT
别用 DROP INDEX 代替——重建索引比导入还慢,且无法回滚。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
关键 InnoDB 参数临时调优:innodb_flush_log_at_trx_commit 和 bulk_insert_buffer_size
这两个参数对批量写入影响最直接,且支持动态修改(无需重启)。
-
SET GLOBAL innodb_flush_log_at_trx_commit = 0:让 redo log 每秒刷一次盘,而非每次提交都刷。提速显著,但崩溃可能丢最多 1 秒数据(仅限导入期间) -
SET GLOBAL bulk_insert_buffer_size = 64 * 1024 * 1024:专为INSERT ... VALUES (),(),()类批量插入优化的缓冲区,设太小会退化成单行处理 -
SET GLOBAL max_allowed_packet = 1024 * 1024 * 64:防止大批次INSERT被截断(尤其含BLOB字段时) - 注意:
innodb_buffer_pool_size对写入提速有限,它主要加速读;若内存充足可设大些,但别指望它解决“越插越慢”
SQL 文件本身结构决定 70% 的速度上限
再好的参数也救不了单行 INSERT 拼成的 5GB 文件。真正快的导入,靠的是减少语句解析和网络交互次数。
- 确认文件是否用了
--extended-insert(mysqldump 默认开启):应看到INSERT INTO t VALUES (1,'a'),(2,'b'),(3,'c');,而不是每行一个INSERT - 如果原始文件是单行格式,可用
sed或 Python 脚本合并(例如每 1000 行合成一条INSERT) - 绝对避免用 phpMyAdmin、Navicat 导入:它们会把整条语句拆成多轮请求,额外增加连接/解析开销
- 优先走命令行:
mysql -u user -p db_name ,加--quick防客户端缓存爆内存
真正容易被忽略的一点:DISABLE KEYS 在 InnoDB 中效果有限(5.7+ 仅对部分批量路径生效),不如老老实实关 unique_checks 来得直接可靠;而 LOAD DATA INFILE 虽然最快,但它要求文件在数据库服务器本地,且格式必须是纯文本(CSV/TSV),不适用于 mysqldump 生成的 SQL 文件。










