怎么死锁排查[MySQL]:从日志到问题定位

0 阅读3分钟

排查死锁

目录

  • 一、排查死锁的核心思路
  • 二、排查流程
  • 三、复盘(死锁排查步骤)
  • 四、小结

一、排查死锁的核心思路

死锁排查的本质,就是通过死锁日志,还原出两个事务的加锁顺序和等待关系,找到循环等待的环路

核心步骤:

  1. 看日志:通过 SHOW ENGINE INNODB STATUS; 拿到死锁日志。

  2. 拆日志:把日志拆成两部分:

    • 事务 1:持有什么锁?在等什么锁?
    • 事务 2:持有什么锁?在等什么锁?
  3. 画流程:按时间线还原两个事务的执行顺序,找到循环等待的环路。

  4. 定原因:根据环路,定位到是哪两条 SQL、哪种锁导致了死锁。

二、排查流程

1、拿到死锁日志

执行命令:

SHOW ENGINE INNODB STATUS;

重点关注 LATEST DETECTED DEADLOCK 部分。

2、拆解日志(关键!)

配合本节内容食用

image-20260307005535884.png

死锁形成流程

步骤事务 1事务 2
1begin;
2delete from test where a = 2;成功,在索引 a 上获得 a=2X 型记录锁
3begin;
4delete from test where a = 2;阻塞,尝试申请 a=2 的 X 锁,但事务 2 已经持有 X 锁,X 锁互斥,所以等待
5insert into test (id,a) values (10,2);阻塞,因为 a 是唯一索引,插入前需要申请 S 锁 做重复检查,但事务 1 正在等待 X 锁,S 锁和 X 锁互斥,形成循环等待,死锁发生。事务 1 权重小,被回滚。

事务 1(TRANSACTION 2A8BD)

  • 执行的 SQLdelete from <test> where a = 2;
  • 当前状态:在等待一个锁(WAITING FOR THIS LOCK TO BE GRANTED)
  • 等待的锁:在索引 a 上的 X 锁(记录锁) ,锁的是 a=2 的记录。

事务 2(TRANSACTION 2A8BC)

  • 执行的 SQLinsert into <test> (id,a) values (10,2);
  • 当前状态:持有一个锁(HOLDS THE LOCK (S)),同时在等待另一个锁
  • 持有的锁:在索引 a 上的 X 锁(记录锁) ,锁的是 a=2 的记录。
  • 等待的锁:在索引 a 上的 S 锁(共享锁)

3、 定位死锁原因

直接原因:两个事务交叉申请了 X 锁和 S 锁,形成了循环等待。

三、复盘(死锁排查步骤)

  1. 获取死锁日志

    SHOW ENGINE INNODB STATUS;
    

    找到 LATEST DETECTED DEADLOCK 部分。

  2. 分析每个事务

    • 事务 ID、执行时间
    • 执行的 SQL 语句
    • 持有哪些锁(HOLDS THE LOCK(S)
    • 在等待哪些锁(WAITING FOR THIS LOCK TO BE GRANTED
    • 锁的类型:X 锁、S 锁、记录锁、间隙锁、临键锁
  3. 画出等待关系图

    • 事务 A 持有锁 L1,等待锁 L2
    • 事务 B 持有锁 L2,等待锁 L1
    • 这样就形成了循环等待,死锁就发生了。
  4. 定位问题 SQL 和场景

    • 是交叉加锁?
    • 是间隙锁 + 插入意向锁?
    • 是唯一索引检查导致的 S 锁和 X 锁冲突?
  5. 给出解决方案

    • 调整事务内 SQL 的执行顺序
    • 降低隔离级别(从 RR 到 RC)
    • 优化索引,避免全表扫描导致的大范围锁
    • 让事务更短,尽快提交释放锁

四、小结

从死锁日志里找出“谁持有什么锁、在等什么锁”,然后按时间线还原出循环等待的环路,最后定位到问题 SQL 和场景