首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >MySQL 死锁排查现场解析流程

MySQL 死锁排查现场解析流程

原创
作者头像
学习........
修改2026-04-29 17:42:43
修改2026-04-29 17:42:43
840
举报

1. 查看死锁日志 show engine innodb status;

当业务系统报错 Deadlock found when trying to get lock 时,必须第一时间登录 MySQL 客户端执行命令提取日志。

执行命令:

代码语言:sql
复制
SHOW ENGINE INNODB STATUS\G

查看位置:

在输出的庞大文本中,直接向下滚动或搜索关键字,定位到 ------------------------ LATEST DETECTED DEADLOCK ------------------------ 这一段。此部分记录了系统检测到的最近一次死锁的完整上下文。

2. 找出死锁 SQL

在死锁日志中,InnoDB 会将冲突的事务标记为 (1) TRANSACTION(2) TRANSACTION。需要从中提取正在执行的 SQL 语句。

提取过程:

在日志的 (1) TRANSACTION 块中,寻找 query id 字段:

代码语言:txt
复制
*** (1) TRANSACTION:
TRANSACTION 4561, ACTIVE 10 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 12, OS thread handle 1407, query id 100 update t set c = c + 1 where id = 2

结论 1: 事务 1 发生死锁时正在执行的 SQL 是 update t set c = c + 1 where id = 2

(2) TRANSACTION 块中,进行同样的寻找:

代码语言:txt
复制
*** (2) TRANSACTION:
TRANSACTION 4562, ACTIVE 8 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 3 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 13, OS thread handle 1408, query id 105 update t set c = c + 1 where id = 1

结论 2: 事务 2 发生死锁时正在执行的 SQL 是 update t set c = c + 1 where id = 1

3. 分析 SQL 加锁情况

找到 SQL 后,必须结合日志中 HOLDS THE LOCK(S)(当前持有的锁)和 WAITING FOR THIS LOCK TO BE GRANTED(正在等待的锁)部分,明确锁的类型和级别。

分析事务 1 的日志细节:

代码语言:txt
复制
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 50 page no 4 n bits 72 index PRIMARY of table `test`.`t` trx id 4561 lock_mode X locks rec but not gap waiting
  • index PRIMARY:说明锁加在主键索引上。
  • lock_mode X:申请的是排他锁(X锁)。
  • locks rec but not gap:这是一个纯粹的记录锁(Record Lock),不包含间隙锁。
  • waiting:表示该锁正在排队等待获取。

分析事务 2 的日志细节:

代码语言:txt
复制
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 50 page no 4 n bits 72 index PRIMARY of table `test`.`t` trx id 4562 lock_mode X locks rec but not gap
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 50 page no 3 n bits 72 index PRIMARY of table `test`.`t` trx id 4562 lock_mode X locks rec but not gap waiting
  • HOLDS THE LOCK(S) 明确指出,事务 2 已经持有了某一行的排他记录锁(对应 id=2 的行)。
  • WAITING FOR 指出,事务 2 正在申请另一行的排他记录锁(对应 id=1 的行)。

4. 模拟死锁案发

根据上述 SQL 和加锁分析结果,可以完全反向推导出应用程序在死锁发生前的执行时序。假设表 t 的结构包含主键 id

时间轴还原:

时间节点

事务 1 (Session A)

事务 2 (Session B)

状态变化

T1

BEGIN;

BEGIN;

开启事务

T2

UPDATE t SET c=c+1 WHERE id=1;

成功。事务 1 获取 id=1 的排他记录锁。

T3

UPDATE t SET c=c+1 WHERE id=2;

成功。事务 2 获取 id=2 的排他记录锁。

T4

UPDATE t SET c=c+1 WHERE id=2;

阻塞。请求 id=2 的锁,被事务 2 占用,进入等待队列。

T5

UPDATE t SET c=c+1 WHERE id=1;

触发死锁。请求 id=1 的锁,被事务 1 占用。形成环路。

5. 分析死锁日志

将模拟现场与日志进行逻辑闭环验证。

  1. 闭环条件判定
    • 日志显示事务 1 等待 id=2 的锁,与模拟中 T4 节点完全吻合。
    • 日志显示事务 2 持有 id=2 的锁,等待 id=1 的锁,与模拟中 T3 和 T5 节点完全吻合。
  2. 底层机制体现
    • 在 T5 时刻,InnoDB 内部的死锁检测机制(innodb_deadlock_detect=ON)扫描到了锁等待图(Wait-for Graph)中存在有向环。
    • 事务 1 的节点指向事务 2 的节点,事务 2 的节点指向事务 1 的节点,死锁条件物理成立。

6. 分析死锁结果

在日志的最后一行,会记录系统的处理决定:

代码语言:txt
复制
*** WE ROLL BACK TRANSACTION (2)

结果剖析:

  1. 回滚机制:系统不会让两个事务永久僵持。InnoDB 强制回滚了事务 2,释放了其持有的 id=2 的锁,从而让事务 1 能够获取锁并继续执行。
  2. 选择依据:InnoDB 选择回滚事务 2,是因为内部维护了一个事务权重(trx_weight)。该权重取决于事务修改的行数和占用的锁数量。在此次案发中,系统判定事务 2 的回滚成本低于事务 1。
  3. 工程定论:此类死锁的根源在于并发事务未遵循一致的资源获取顺序。解决方案必须在应用代码层面对 UPDATE 涉及的 id 进行强制升序排序,确保所有事务均按照 id=1 -> id=2 的顺序加锁,即可从物理上切断循环等待链,彻底避免该死锁。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 查看死锁日志 show engine innodb status;
  • 2. 找出死锁 SQL
  • 3. 分析 SQL 加锁情况
  • 4. 模拟死锁案发
  • 5. 分析死锁日志
  • 6. 分析死锁结果
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档