MySQL带JOIN的UPDATE语句并发执行死锁问题解决求助
MySQL死锁问题解决方案
死锁核心诱因
当前多表关联UPDATE触发死锁,本质是InnoDB行锁机制下,并发事务加锁顺序不一致、锁范围过大导致的循环等待,对应此SQL场景的常见诱因如下:
- 多表关联更新时,不同事务对多张表的加锁顺序不统一,如事务1先锁
MultipleChoiceAnswer再锁Section,事务2先锁Section再锁MultipleChoiceAnswer,形成循环等待 - 更新
MultipleChoiceAnswer复制行时,无固定行加锁顺序,不同事务按不同顺序对多行加锁,形成交叉等待 - 关联字段缺少索引导致InnoDB触发不必要的gap锁/全表锁,放大锁冲突概率
可落地解决建议
- 补全关联字段索引
优先确认以下字段是否已创建索引,索引可大幅缩小InnoDB锁范围,避免全表扫描带来的多余锁:MultipleChoiceAnswer.copy_of_multiple_choice_answer_idQuizQuestion.section_id- 区分度较高时可新增
Section.quiz_auto_update联合索引
- 拆分多表关联UPDATE为「查询+单表更新」,固定加锁顺序
取消直接多表关联更新,先通过只读查询获取待更新目标行主键,按主键排序后再执行单表更新,保证所有事务按相同顺序加锁:
第一步执行只读查询获取排序后的待更新ID列表:
第二步执行单表更新:SELECT DISTINCT mca_copy.id FROM MultipleChoiceAnswer mca_copy INNER JOIN MultipleChoiceAnswer mca_original ON mca_original.id = mca_copy.copy_of_multiple_choice_answer_id AND mca_original.id = ? INNER JOIN QuizQuestion qq_copy ON qq_copy.id = mca_copy.question_id INNER JOIN Section s_copy ON s_copy.id = qq_copy.section_id AND s_copy.quiz_auto_update = 1 ORDER BY mca_copy.id ASC;UPDATE MultipleChoiceAnswer mca_copy INNER JOIN MultipleChoiceAnswer mca_original ON mca_original.id = mca_copy.copy_of_multiple_choice_answer_id AND mca_original.id = ? SET mca_copy.answer = mca_original.answer, mca_copy.correct = mca_original.correct WHERE mca_copy.id IN (/* 第一步返回的排序后ID列表 */) - 串行处理同一条原始记录的更新任务
如果存在多请求同时同步同一条原始MultipleChoiceAnswer记录的场景,可在查询前先对原始行加排他锁,保证同一时间只有一个事务处理同一条原始记录的同步:SELECT id FROM MultipleChoiceAnswer WHERE id = ? FOR UPDATE; - 高并发场景下异步化改造
同步操作并发量较高时,可将同步逻辑改为异步实现,用消息队列按原始记录ID分片消费,保证同一个原始ID的更新任务串行执行,从根源上消除锁竞争。
排查辅助技巧
可执行SHOW ENGINE INNODB STATUS查看最新死锁日志,确认两个冲突事务分别持有的锁、等待的锁,可更精准定位具体冲突点。
内容的提问来源于stack exchange,提问作者Zach
相关产品推荐
相关产品推荐

