You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MySQL 8.3.0执行SELECT ... FOR UPDATE时出现死锁问题求助

问题原因与解决方案

这不是MySQL的bug,是InnoDB默认的**Next-Key Locking(临键锁)**机制导致的,具体逻辑如下:

死锁触发原因

InnoDB在REPEATABLE READ(默认隔离级别)下,为防止幻读,执行SELECT ... FOR UPDATE这类加锁查询时,不仅会锁定匹配的行,还会锁定行所在的索引间隙(Gap Lock)。当表为空时,product_sku索引无任何现有值,此时两个会话的SELECT ... FOR UPDATE查询(分别针对'abc'和'def')都会锁定整个索引的间隙范围(从负无穷到正无穷)。

后续插入操作需要申请插入意向锁(Insert Intention Lock),这是一种特殊间隙锁,用于标记插入到某间隙的意向:

  • AA插入'abc'时,申请的插入意向锁与BB持有的全局间隙锁冲突,AA挂起等待
  • BB插入'def'时,申请的插入意向锁与AA持有的全局间隙锁冲突,形成循环等待,触发死锁

可行解决方案

1. 给product_sku添加唯一索引(推荐)

将product_sku设为唯一索引后,InnoDB会针对不存在的唯一键值使用**记录锁(Record Lock)**而非间隙锁——因为唯一索引可保证不会有重复值插入,无需通过间隙锁防止幻读。

修改表结构的SQL:

ALTER TABLE example ADD UNIQUE INDEX idx_unique_product_sku (product_sku);

修改后,两个会话针对不同product_sku的加锁查询只会锁定各自对应的虚拟记录,插入时不会产生锁冲突,既满足“阻止同一分组并发插入”的需求,又允许不同分组并行插入。

2. 降低事务隔离级别至READ COMMITTED

该隔离级别下InnoDB不会使用间隙锁,仅使用记录锁,避免了间隙锁导致的锁范围过大问题。但此级别无法防止幻读,需根据业务场景判断是否可接受。

设置会话级隔离级别:

SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;

3. 调整业务逻辑(按需选择)

如果业务允许,可以先尝试插入记录(利用唯一索引的唯一性约束阻止重复插入),再对已插入的行进行加锁操作,跳过前置的加锁查询步骤。

内容的提问来源于stack exchange,提问作者sowiq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 06:27:24