批量提交+设innodb_autoinc_lock_mode=2可将3万条insert从20秒降至3秒内,但需主键有序、禁用非必要索引等条件配合。

直接结论:批量提交 + 关闭 innodb_autoinc_lock_mode(设为 2)能显著降低大批量 INSERT 的锁竞争和事务开销,3万条插入从20秒压到3秒内常见,但必须配合主键有序、禁用非必要索引等条件才稳定生效。
为什么批量提交比单条提交快得多
每次 INSERT 默认开启隐式事务,MySQL 要写 redo log、binlog、更新索引、刷脏页——这些操作在单条提交时反复执行,IO 和日志刷盘压力极大。批量提交把多条语句包进一个显式事务,只做一次日志落盘和索引合并。
- 必须显式写
START TRANSACTION和COMMIT,不能依赖自动提交(autocommit=1) - 单个事务别塞超过 5000 条(InnoDB 行锁升级阈值附近),否则容易触发锁升级或长事务阻塞
- 避免在事务中混杂
SELECT或其他 DML,防止锁范围意外扩大
innodb_autoinc_lock_mode=2 是什么,为什么关它
MySQL 默认是 innodb_autoinc_lock_mode=1(连续模式),对自增主键的批量插入会加表级 AUTO-INC 锁,串行化分配 ID,彻底堵死并发插入。设为 2(交错模式)后,ID 分配改用轻量级 mutex,允许不同事务并发获取 ID 段,插入不再排队。
- 仅适用于
INSERT ... VALUES批量语句,不适用于INSERT ... SELECT或带子查询的插入 - 需确认业务能接受自增 ID 不严格连续(中间可能跳号),这是唯一副作用
- 修改方式:
SET GLOBAL innodb_autoinc_lock_mode = 2,或写入my.cnf永久生效
批量提交 + 自增锁优化的典型组合陷阱
这两项单独调优效果有限,合起来用才出真性能,但极易踩坑:
- 主键乱序插入(如按时间戳倒序)会导致二级索引频繁分裂,吞掉一半提速收益;务必保证
VALUES中的主键值单调递增 - 没关非主键索引时,每插一条仍要更新所有索引树;建议导入前
DROP INDEX,导入完CREATE INDEX -
innodb_flush_log_at_trx_commit=1(默认)下,即使批量提交,每次COMMIT仍强制刷盘;临时改成0可再提速 30%,但断电会丢最近 1 秒事务 - 应用层若用 ORM(如 MyBatis、Hibernate),注意它们可能自动拆分批量语句或强制每条都 flush,得关掉批量代理逻辑,直连 JDBC 执行原生 SQL
一个安全可用的实操模板
假设你要插 3 万条用户数据,主键为 id(自增),有 name、email 两个字段,且数据已按 id 升序排列:
SET autocommit = 0; SET innodb_autoinc_lock_mode = 2; -- 若允许丢数据,可加:SET innodb_flush_log_at_trx_commit = 0; <p>START TRANSACTION; INSERT INTO users (id, name, email) VALUES (1, 'a', 'a@x'), (2, 'b', 'b@x'), (3, 'c', 'c@x'), -- ... 每批最多 2000 行,避免单语句超长 (1998, 'y', 'y@x'), (1999, 'z', 'z@x'), (2000, 'aa', 'aa@x'); COMMIT;</p><p>-- 插完再建索引(如果之前删了) CREATE INDEX idx_email ON users(email);</p>
真正难的不是写这几行,而是确保数据源顺序、索引状态、配置变更、应用层行为全部对齐——漏掉任意一环,速度就卡回原点。











