MySQL INSERT慢主因是默认强一致性机制(autocommit=1、innodb_flush_log_at_trx_commit=1、实时索引更新)在批量场景下成为性能瓶颈;应关闭autocommit、用显式事务分批插入500–2000行、调优innodb_flush_log_at_trx_commit与sync_binlog、禁用非核心二级索引、优先使用LOAD DATA INFILE,并确保buffer pool足够大。

INSERT慢不是MySQL本身卡,而是你默认开着一堆“安全开关”在批量写时还硬扛着。 真正拖慢的,是每条语句都走完整事务、每秒强制刷redo日志、边插边更新所有索引——这些设计本为强一致性服务,但批量导入时反而成了累赘。
为什么ALTER TABLE ... DISABLE KEYS对InnoDB有时没用
这个命令只对MyISAM表真正生效;InnoDB从5.7开始部分支持,但仅限某些LOAD DATA路径,普通INSERT不触发索引禁用逻辑。常见错误现象是执行了ALTER TABLE t DISABLE KEYS,SHOW INDEX FROM t仍显示索引状态为enabled,插入速度毫无变化。
- 确认引擎类型:
SHOW CREATE TABLE t看ENGINE=InnoDB还是MyISAM - InnoDB下更可靠的做法是:先
DROP INDEX idx_name ON t,导入完再CREATE INDEX idx_name ON t (col) - 主键和唯一索引不能删——否则重复数据会静默失败或报
Duplicate entry错误 - 如果表有5个以上二级索引,删掉2–3个非核心的,导入后再建,通常能提速2倍以上
innodb_flush_log_at_trx_commit=0/2到底该选哪个
设成0最快,但风险最高:MySQL进程活着,OS崩溃或断电就可能丢1秒内所有未刷盘的事务;设成2是折中,log写进系统缓存即返回,OS每秒刷一次,实际丢数据概率低得多,性能提升也明显。
- 线上业务别直接改配置文件永久设0——用
SET GLOBAL innodb_flush_log_at_trx_commit = 2临时生效即可 - 如果开了binlog(比如主从复制),必须同步调
sync_binlog,否则redo日志快了,binlog还在排队刷盘,照样卡住 - 常见安全组合:
innodb_flush_log_at_trx_commit = 2+sync_binlog = 1000,适用于ETL中间表、日志归档等可重跑场景 - 设完立刻验证:
SELECT @@innodb_flush_log_at_trx_commit, @@sync_binlog
为什么关了autocommit、调了参数,INSERT还是单条执行
ORM框架或脚本里用循环拼INSERT INTO t VALUES (...); INSERT INTO t VALUES (...);,MySQL解析器仍当多条独立语句处理,每次都会走一遍日志写入+索引校验流程。
- 必须用显式事务包裹:
BEGIN; INSERT INTO t VALUES (...),(...),(...); COMMIT; - 单条
INSERT带多行值(bulk insert)比100条单行INSERT快5倍以上,但前提是它们在一个事务里 - 如果数据在文件里,优先用
LOAD DATA INFILE——它跳过SQL解析层,直通InnoDB缓冲池,速度通常是INSERT的8倍左右 - 检查是否被ORM“拆包”:开general log或用
SHOW PROCESSLIST观察是不是真的一次提交了上千行
最容易被忽略的点:buffer pool太小会导致频繁刷脏页,哪怕把innodb_flush_log_at_trx_commit设成0,磁盘IO仍在瓶颈处堵着。导入前先查SHOW ENGINE INNODB STATUS\G里的“Buffer pool hit rate”,低于990/1000就得调大innodb_buffer_pool_size。这不是锦上添花,是让前面所有优化真正落地的前提。











