insert在写入前加锁是为了校验唯一性约束,需先加插入意向锁和间隙锁以锁定可能插入位置,防止幻读和重复插入。

INSERT 为什么会在没插之前就加锁?
因为 MySQL 要保证唯一性约束不被破坏,INSERT 在真正写入前,必须先检查目标位置是否已有相同主键或唯一键值。这个检查过程会触发 insert intention lock(插入意向锁)和 gap lock(间隙锁),哪怕那行还不存在——它锁的是“可能插入的位置”。多个事务同时插同一个值(比如订单号 'ORD-20260827-123'),第一个拿到锁,其余全卡在 lock_mode X locks gap before rec insert intention waiting 状态。
ON DUPLICATE KEY UPDATE 比 INSERT IGNORE 更容易卡住
ON DUPLICATE KEY UPDATE 不是“先查再决定”,而是“先锁再查再可能更新”。只要唯一键匹配,它就会对目标行(或间隙)加 X lock,哪怕 SET id = id 这种伪更新也照锁不误。而 INSERT IGNORE 遇到冲突直接跳过,不加 X lock,也不触发行更新逻辑,开销小得多。
- 用
ROW_COUNT()判断结果:返回1是新增,0是被忽略 - 它也会静默吞掉字段超长、类型不匹配等错误,不是只忽略重复键
- 建表时必须有明确的
PRIMARY KEY或UNIQUE KEY,普通索引无效
批量 INSERT 乱序是死锁温床
InnoDB 的间隙锁机制对顺序极度敏感。一批 INSERT ... VALUES 中如果主键值无序(比如 (101, 'a'), (99, 'b'), (100, 'c')),事务 A 锁 99–100 间隙,事务 B 锁 100–101 间隙,互相等待——这不是传统死锁,但会触发 Lock wait timeout exceeded。
- 应用层生成 ID 后必须内存排序,再拼 SQL;不能依赖
INSERT SELECT ORDER BY - 单批建议 ≤ 500 行;超量必须拆成多条,每条内部严格升序
- 禁用
UUID()或MD5()直接作主键;雪花 ID 必须保序生成+排序
自增锁和外键检查也会拖慢 INSERT
即使没唯一键冲突,AUTO_INCREMENT 列在高并发下仍可能成为瓶颈:innodb_autoinc_lock_mode=1(默认)会用表级 AUTO-INC lock,串行分配自增值。另外,若表有外键,INSERT 还要检查引用表是否存在对应记录,这步也会加锁并等待。
- 确认
innodb_autoinc_lock_mode=2(交错模式),适用于大多数场景 - 外键字段务必建索引,否则检查过程可能升级为全表扫描+锁表
- 避免在事务中混入网络调用或用户等待,否则锁持有时间被意外拉长
INSERT,背后涉及间隙锁、插入意向锁、自增锁、外键检查锁四层机制——漏控任何一层,都可能让请求在毫秒级内卡死。










