MySQL自增主键为何持有间隙锁并引发死锁?技术问询
自增主键间隙锁引发死锁的问题解析
核心疑问解答:为何主键索引上加间隙锁而非记录锁
INSERT IGNORE的锁行为特性
因为表存在唯一键uk_lock_table_01(user_id,order_code),INSERT IGNORE执行时会先对每条待插入记录做唯一键冲突检查:- 若检查到记录已存在,直接忽略插入;
- 若未命中记录,InnoDB在RR隔离级别下,会对唯一索引的对应间隙加锁(防止幻读和并发插入重复键),同时该锁会关联到主键索引的对应间隙(二级索引叶子节点存储主键值,锁会同步到主键索引)。
自增主键的间隙锁逻辑
自增主键id按顺序分配,批量插入时InnoDB会预先分配一批自增ID,为了保证自增ID的连续性和插入顺序,会在主键索引的目标插入间隙(待插入记录的主键值所在区间)加间隙锁,而非针对已存在的记录加记录锁——毕竟待插入的主键记录还不存在,自然不会有记录锁。
死锁触发的具体流程
结合死锁日志来看:
- 会话2先执行批量
INSERT IGNORE,对部分待插入的(user_id,order_code)做冲突检查,未命中记录后,在主键索引的某个间隙加了X型间隙锁; - 会话1执行自己的批量
INSERT IGNORE,其中某条记录的插入间隙被会话2持有,于是等待该间隙的插入意向锁; - 会话2继续处理后续批量插入记录,发现需要申请的间隙锁被会话1持有(会话1在之前的冲突检查中也加了其他间隙锁),同样进入等待;
- 双方互相持有对方需要的锁,形成循环等待,触发死锁。
优化建议
- 拆分批量插入为单条插入,缩小单次操作的锁范围,降低冲突概率;
- 若业务允许,改用
INSERT ... ON DUPLICATE KEY UPDATE替代INSERT IGNORE,其锁行为更可控; - 确保
uk_lock_table_01索引统计信息准确,避免InnoDB因索引失效执行全表扫描,导致锁范围扩大; - 调整
innodb_autoinc_lock_mode参数(默认值为1),批量插入时减少锁的持有时间,但需注意自增ID可能出现不连续的情况。
内容的提问来源于stack exchange,提问作者linlowa
相关产品推荐
相关产品推荐

