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

并发场景下SELECT FOR UPDATE SKIP LOCKED与UPDATE操作死锁原因排查

死锁原因分析(FOR UPDATE SKIP LOCKED + InnoDB READ_COMMITTED场景)

以下是导致你遇到死锁的几个核心原因:

  • 锁获取顺序不一致引发循环等待
    InnoDB死锁的本质是事务间形成了循环等待锁的局面。当并发事务通过FOR UPDATE SKIP LOCKED选取数据时,如果SQL没有指定明确的ORDER BY排序逻辑,不同事务选中的行顺序可能是随机的。比如事务A先锁定行1、再尝试锁定行2,而事务B先锁定行2、再尝试锁定行1,两者就会互相等待对方释放锁,直接触发死锁。SKIP LOCKED只是跳过已被锁定的行,但无法控制选中行的获取顺序,这种交叉锁的情况在高并发下很容易发生。

  • 事务边界异常导致锁提前释放
    你提到“事务似乎在UPDATE前提交”,这大概率是事务控制出现了问题:

    • 即便方法标注了@Transactional,如果存在内部方法调用(未通过Spring代理)、事务传播配置错误,或者JDBC驱动在执行原生SQL时触发了隐式自动提交,都会导致查询(加锁)和更新操作不在同一个事务内。此时查询加的行锁会在查询语句执行完毕后立即释放,后续更新时需要重新申请锁,这就会和其他事务的锁操作产生冲突,进而引发死锁。
    • 手动管理事务时,如果事务的提交/回滚时机控制不当(比如提前提交了部分操作),也会出现类似的锁提前释放问题。
  • 循环更新中的锁竞争叠加
    你是循环逐条将选中的TestObj设为WAITING并保存,也就是每条记录单独执行UPDATE语句。即便查询阶段通过FOR UPDATE SKIP LOCKED获取了行锁,在READ_COMMITTED隔离级别下,InnoDB的锁机制可能会因为语句执行的特性,导致后续UPDATE语句需要重新确认锁状态。如果此时有其他并发事务也在竞争同一批行的锁,就可能出现锁等待的叠加,最终触发死锁检测。

  • READ_COMMITTED隔离级别的锁特性影响
    在READ_COMMITTED隔离级别下,InnoDB会在语句执行结束后释放不需要的锁,但FOR UPDATE SKIP LOCKED获取的锁本应持续到事务结束。不过如果你的循环更新操作中,存在某些触发锁释放的隐性逻辑(比如中间有查询操作导致锁范围变化),也可能导致锁状态异常,增加死锁的概率。

另外,去掉FOR UPDATE SKIP LOCKED后没有死锁是因为没有行锁竞争,但同时也失去了排他性,所以会出现重复选数据的问题,这是锁机制缺失的必然结果。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 17:10:03