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

单条更新语句执行后触发死锁错误的异常场景排查

带左连接的UPDATE语句更新成功后仍触发死锁的成因分析

问题背景

有一条在多微服务中并行执行的SQL更新语句,偶尔会触发死锁(符合预期),但出现边缘场景:某服务的语句更新记录状态后仍抛出死锁错误,而并行的另一个服务语句执行正常、未更新任何记录(目标记录已处于pending状态)。代码实现如下:

const reserveActionForExecution = async (id: number): Promise<boolean> => {
  try {
    const query = `
      UPDATE articles a1
      LEFT JOIN
        articles a2
        ON a1.linkedArticleId = a2._id
      SET a1.status = ?, a1.updated_time = ?
      WHERE a1._id = ?
        AND a1.status = ?
        AND a1.updated_time = ?
        AND (a2.status = 'open' OR a2.status IS NULL)
    `

    const result = await client.query(query, ['pending', Date.now(), id, 'open', originalUpdatedTime])

    return result
  } catch (err: any) {
    if (extra.code === 'ER_LOCK_DEADLOCK')) return false

    throw err
  }
}

核心成因分析

你观察到的“更新成功+死锁错误”看似矛盾,本质是InnoDB锁机制、死锁回滚策略,或代码/观察细节偏差导致的,具体拆解如下:

1. 单语句事务的锁执行逻辑

你的UPDATE语句属于隐式事务(自动提交模式),执行流程是:

  • 先对a1表中符合_id、status、updated_time条件的记录加排他锁(X锁)
  • 再对a2表中关联的记录加共享锁(S锁,仅用于读取a2.status做判断)
  • 所有锁获取完成后,执行a1的更新操作
  • 提交事务,释放所有锁

死锁只会发生在锁获取阶段(前两步),而非更新完成后。当死锁触发时,MySQL会选择一个事务回滚:

  • 若被回滚的是当前服务的事务:a1的更新会被撤销,记录保持open状态,代码捕获死锁错误返回false
  • 若被回滚的是并行的其他事务:当前事务会继续执行完成更新,不会抛出错误,代码返回受影响行数(true)

2. 你观察到的边缘场景的真实原因

你看到的“更新成功+死锁错误”大概率是以下两种情况之一:

  • 时间差导致的误判:当前服务的事务第一次执行触发死锁被回滚,代码返回false;此时并行服务的事务因死锁解除(当前事务释放锁),继续执行并成功更新了a1的状态为pending,你误以为是当前服务的事务完成了更新。
  • 代码错误处理的笔误:代码中extra.code应为err.code,变量名写错会导致死锁错误未被捕获而直接抛出;此时若死锁被回滚的是其他事务,当前事务已成功完成更新,但代码错误抛出异常,让你产生“更新成功后抛死锁”的错觉。

3. 左连接带来的锁范围扩大风险

你的左连接条件(a2.status = 'open' OR a2.status IS NULL)存在锁范围扩大的隐患:

  • 若a2.status未建立索引,InnoDB会扫描整个a2表,对所有a2记录加S锁,大幅提升锁冲突概率。
  • 当a1.linkedArticleId为NULL时,左连接会匹配所有a2的NULL行,导致InnoDB锁定大量无关记录,进一步增加死锁可能性。

验证与优化建议

  • 修正代码错误处理的变量名:将extra.code改为err.code,确保死锁错误被正确捕获。
  • 查看InnoDB死锁日志:执行SHOW ENGINE INNODB STATUS,日志会详细记录死锁发生时的事务、锁持有/等待情况,明确哪个事务被回滚。
  • 优化索引:给a2.status和a1.linkedArticleId建立联合索引,缩小锁的范围,减少死锁触发概率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 20:55:57