最直接有效的解法是批量提交事务+关闭autocommit:每条INSERT默认独立事务,导致10万次重复解析、加锁、写日志、刷盘,引发I/O瓶颈和锁争用;必须配合多值INSERT与START TRANSACTION/COMMIT显式控制,单批1000–5000行,并调大max_allowed_packet。

批量提交事务 + 关闭 autocommit 是最直接有效的解法,单靠调参数(如 innodb_flush_log_at_trx_commit)收效有限,还可能丢数据。
为什么每条 INSERT 都自动提交会卡死?
MySQL 默认 autocommit = 1,意味着每条 INSERT 都是独立事务:解析 SQL、加行锁、写 redo log、刷盘、释放锁——全都要走一遍。10 万条就是 10 万次磁盘 I/O 和日志刷写,不是慢,是硬扛系统瓶颈。
常见错误现象:SHOW PROCESSLIST 看到大量线程卡在 query end 或 commit;监控里 Innodb_log_waits > 0;插入耗时随数据量线性上涨。
- 确认当前状态:
SELECT @@autocommit;—— 生产环境几乎总是1 - 别依赖 ORM 框架的事务注解(比如 Spring 的
@Transactional),它不覆盖纯数据导入场景 - 显式关闭:
SET autocommit = 0;必须在插入前执行,且后续必须配对COMMIT或ROLLBACK
怎么组合批量 INSERT 和事务控制?
两个动作要一起做:一是把多行塞进一条 INSERT INTO ... VALUES (), (), (),二是用 START TRANSACTION 包住这批语句再 COMMIT。只做一半效果打对折。
- 单条
INSERT最多塞 1000–5000 行,取决于字段长度和max_allowed_packet值(默认 4MB,可用SHOW VARIABLES LIKE 'max_allowed_packet'查) - 建议先调大:
SET GLOBAL max_allowed_packet = 67108864;(64MB),或改my.cnf永久生效 - 每批事务控制在 100–1000 条 DML 操作内,太小没收益,太大易触发锁升级、undo 日志膨胀、主从延迟加剧
- 避免在事务中混
SELECT或其他 DML,否则锁范围可能意外扩大
innodb_autoinc_lock_mode = 2 能不能开?
能开,但有前提:你的批量插入是 INSERT INTO ... VALUES 形式,且主键值单调递增(比如按时间或自增 ID 正序插入)。否则不仅无效,还可能让性能更差。
- 默认值
1对批量插入加表级 AUTO-INC 锁,所有并发插入串行排队 - 设为
2后改用轻量 mutex 分配 ID 段,支持并发获取,3 万条插入可从 20 秒压到 3 秒内 - 副作用:自增 ID 不再严格连续(中间跳号),业务必须接受这点
- 修改方式:
SET GLOBAL innodb_autoinc_lock_mode = 2;,或写入配置文件永久生效 - 注意:对
INSERT ... SELECT或带子查询的插入无效
容易被忽略的细节
批量提交不是“一设就灵”,很多问题出在配套动作没跟上:
- 没关非必要索引:二级索引越多,每插一行更新成本越高;导入前
DROP INDEX,导入完CREATE INDEX - 主键乱序插入(如倒序时间戳)导致 B+ 树频繁分裂,吞掉一半提速收益
-
innodb_flush_log_at_trx_commit = 1下,哪怕批量提交,每次COMMIT仍强制刷盘;临时设为0可再提速 30%,但断电会丢最近 1 秒事务 - 事务没提交就断开连接,会导致长事务堆积在
INFORMATION_SCHEMA.INNODB_TRX,锁不释放、连接卡死


















