直接结论:mysql 5.7 大并发 insert 卡顿主因是自增锁、唯一键冲突检查与 rr 隔离级别下 gap lock 三者叠加,导致线程排队、吞吐断崖下跌;需结合语句类型、binlog_format=row、业务容忍度综合调优。

MySQL 5.7 在大并发下 INSERT 性能卡住,不是单一原因,而是多个锁机制和执行路径在高负载下叠加放大的结果。直接结论:最常踩中的坑是自增锁 + 唯一键冲突检查 + RR 隔离级别下的 Gap Lock 三者同时生效,导致插入线程排队等待,吞吐量断崖下跌。
为什么 INSERT 会等 AUTO-INC 锁?
只要表有 AUTO_INCREMENT 列,INSERT 就必须分配新 ID;而 MySQL 5.7 默认 innodb_autoinc_lock_mode = 1,对不确定行数的语句(如 INSERT INTO t SELECT ...、REPLACE INTO、甚至带 ON DUPLICATE KEY UPDATE 的批量插入)会升级为表级 AUTO-INC 锁,持续到语句结束。
- 哪怕只插 100 行,InnoDB 也会按“最坏情况”预估并锁住整段 ID 范围
- 多个并发
INSERT SELECT会形成串行队列,SHOW PROCESSLIST显示Waiting for auto-inc lock -
innodb_autoinc_lock_mode = 2虽可缓解,但要求binlog_format = ROW,否则主从 ID 不一致
为什么唯一键冲突检查会让 INSERT 卡住?
插入前必须检查主键或唯一索引是否已存在——这个动作会申请 Insert Intention Lock,并与当前区间上的 Gap Lock 或 Next-Key Lock 做兼容性判断。一旦冲突,就等。
- 两个事务同时插入相同
email(唯一索引),后一个必须等前一个提交才能确认冲突 - 即使没冲突,只要前一个事务在相同索引范围持有了 Gap Lock(比如刚执行过
SELECT ... FOR UPDATE WHERE email LIKE 'a%'),后续INSERT也会被阻塞 - 外键约束检查同理:需查引用表,可能触发对引用表记录的锁等待
为什么 REPEATABLE READ 隔离级别会拖慢插入?
RR 是 MySQL 5.7 默认隔离级别,它默认启用 Next-Key Lock(记录锁 + 间隙锁)。哪怕你只 INSERT 一条数据,InnoDB 也要确保不会产生幻读——于是会对插入位置前后间隙加锁。
-
UPDATE t SET x=1 WHERE id > 1000会锁住 id > 1000 的整个间隙,后续向该范围INSERT全部排队 -
SELECT ... FOR UPDATE即使没查到数据,也可能锁住空隙,让后续INSERT等待 - 降级到
READ COMMITTED可禁用 Gap Lock,但必须确认binlog_format = ROW,且业务不依赖“同一事务内多次读一致性”
真正卡住系统的,往往不是某一种锁单独起作用,而是自增锁等待期间,恰好又撞上唯一键冲突检查 + RR 下的间隙锁竞争——三重锁等待嵌套,延迟指数级放大。调优时不能只盯一个参数,得结合实际语句类型、binlog 格式、业务对 ID 连续性和事务一致性的容忍度,逐层验证。











