MySQL死锁类deadlock问题排查

文章目录

前言

博主介绍:✌目前全网粉丝4W+,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领域。

涵盖技术内容:Java后端、大数据、算法、分布式微服务、中间件、前端、运维等。

博主所有博客文件目录索引:博客目录索引(持续更新)

CSDN搜索:长路

视频平台:b站-Coder长路

认识常用命令

SHOW ENGINE INNODB STATUS;

介绍

SHOW ENGINE INNODB STATUS; 是 InnoDB 存储引擎提供的一个诊断工具,它会输出一份非常详细的报告,包含 InnoDB 内部多个维度的状态信息。这份报告主要包含以下几个部分:

  1. BACKGROUND THREAD: 后台主线程(如刷新脏页)的活动信息。
  2. SEMAPHORES: 信号量信息,用于诊断线程间争用和锁等待。如果系统存在大量锁等待,这里会有体现。
  3. LATEST DETECTED DEADLOCK : 这是你最关心的部分 。它记录了最近一次发生的死锁的详细信息,包括:
    • 导致死锁的事务:涉及死锁的多个事务的线程ID、正在执行的SQL语句(可能不是完整的业务SQL,而是锁等待相关的内部SQL)。
    • 事务持有的锁和等待的锁:清晰展示了每个事务已经持有什么锁,以及它正在尝试获取什么锁。
    • 回滚的事务:InnoDB 选择哪个事务作为"牺牲品"来回滚以打破死锁。
  1. TRANSACTIONS: 当前活跃的事务信息。
  2. FILE I/O: I/O 相关线程信息。
  3. BUFFER POOL AND MEMORY: 缓冲池和内存的使用统计。
  4. ROW OPERATIONS: 行操作相关的统计信息。

为什么没查到你的死锁信息?

核心原因: **LATEST DETECTED DEADLOCK** 只记录最近一次死锁。

想象一下,它是一个只能容纳一条记录的"死锁日志表"。当发生一个新的死锁时,旧的记录就会被覆盖。

所以,可能的情况是:

  1. 在你的程序报错和你在数据库执行 SHOW ENGINE INNODB STATUS; 之间的这段时间内,系统又发生了新的死锁,把你遇到的那个死锁信息给覆盖掉了。
  2. 数据库可能在你查询之前重启过,因为这份报告是存储在内存中的,重启后会清空。

方式汇总

方式一:执行SHOW ENGINE INNODB STATUS;命令

直接执行该命令查看最近的一次死锁日志记录:

sql 复制代码
SHOW ENGINE INNODB STATUS;

方式二:死锁日志记录

初始化

步骤一:开启并配置永久的死锁日志记录

这是最重要的一步,能让你在死锁发生后从容地分析。

1、开启 InnoDB 死锁日志打印到错误日志

确保你的 MySQL 配置文件中(如 my.cnfmy.ini)有以下配置:

plain 复制代码
[mysqld]
innodb_print_all_deadlocks = ON

这个配置会让 InnoDB 将每一次死锁的详细信息 都记录到 MySQL 的错误日志(Error Log)中,而不是仅仅在 SHOW ENGINE INNODB STATUS; 中保留最近一次。

修改后需要重启 MySQL 服务,或者动态设置(需要有权限):

java 复制代码
SET GLOBAL innodb_print_all_deadlocks = ON;

2、配置并监控错误日志

找到你的 MySQL 错误日志文件路径(可以通过 SHOW VARIABLES LIKE 'log_error'; 查看)。

之后,每当发生死锁,你都可以去这个日志文件里搜索 "DEADLOCK" 关键字,找到完整的死锁报告。

步骤二:捕获并分析死锁现场

当你的应用程序(例如,日志中、监控系统)报告死锁错误时(MySQL 通常会返回 Error 1213),立即去检查错误日志。

你会看到类似这样的报告(这是一个简化示例):

plain 复制代码
*** (1) TRANSACTION:
TRANSACTION 123456, ACTIVE 10 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 4 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 100, OS thread handle 0x..., query id 1000 ... updating
DELETE FROM t1 WHERE a = 1

*** (1) HOLDS THE LOCK(S): -- 事务1持有的锁
RECORD LOCKS space id 100 page no 10 n bits 80 index PRIMARY of table `test`.`t1` trx id 123456 lock_mode X locks rec but not gap
Record lock, heap no 5 PHYSICAL RECORD: ...

*** (1) WAITING FOR THIS LOCK TO BE GRANTED: -- 事务1在等待的锁
RECORD LOCKS space id 100 page no 11 n bits 80 index sec_idx of table `test`.`t1` trx id 123456 lock_mode X locks rec but not gap waiting
Record lock, heap no 6 PHYSICAL RECORD: ...

*** (2) TRANSACTION:
TRANSACTION 123457, ACTIVE 15 sec starting index read
mysql tables in use 1, locked 1
4 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 1
MySQL thread id 101, OS thread handle 0x..., query id 1001 ... updating
DELETE FROM t1 WHERE a = 2

*** (2) HOLDS THE LOCK(S): -- 事务2持有的锁
RECORD LOCKS space id 100 page no 11 n bits 80 index sec_idx of table `test`.`t1` trx id 123457 lock_mode X locks rec but not gap
Record lock, heap no 6 PHYSICAL RECORD: ...

*** (2) WAITING FOR THIS LOCK TO BE GRANTED: -- 事务2在等待的锁
RECORD LOCKS space id 100 page no 10 n bits 80 index PRIMARY of table `test`.`t1` trx id 123457 lock_mode X locks rec but not gap waiting
Record lock, heap no 5 PHYSICAL RECORD: ...

*** WE ROLL BACK TRANSACTION (2) -- 数据库选择回滚了事务2
步骤三:解读死锁报告并定位业务代码

分析上面的报告,关键是找到:

  1. 涉及的事务:两个(或多个)事务分别在执行什么 SQL?
    • 示例中,两个事务都在执行 DELETE,但条件不同 (a=1a=2)。
  1. 锁的持有和等待关系
    • 事务1 :持有了 PRIMARY 索引上某行的 X 锁,正在等待 sec_idx 索引上某行的 X 锁。
    • 事务2 :持有了 sec_idx 索引上某行的 X 锁,正在等待 PRIMARY 索引上某行的 X 锁。
  1. 死锁成因
    这就形成了一个典型的"循环等待":事务1等事务2,事务2又在等事务1。数据库为了打破僵局,选择回滚其中一个(这里是事务2)。

排查过程

1、立即捕获死锁现场

shell 复制代码
# 1. 立即查询最新死锁信息(趁还没被覆盖)
SHOW ENGINE INNODB STATUS\G

# 2. 检查死锁记录是否已开启
SHOW VARIABLES LIKE 'innodb_print_all_deadlocks';

# 3. 如果未开启,立即开启(避免后续死锁丢失)
SET GLOBAL innodb_print_all_deadlocks = ON;

2、定位日志中的死锁情况

shell 复制代码
# 1. 找到错误日志位置
mysql -e "SHOW VARIABLES LIKE 'log_error';"

# 2. 实时监控错误日志中的死锁(推荐)
tail -f /var/log/mysql/error.log | grep -A 50 -B 5 "DEADLOCK"

# 3. 或者搜索历史死锁记录
grep -A 50 "DEADLOCK" /var/log/mysql/error.log

3、快速分析核心问题

shell 复制代码
在死锁日志中,重点关注以下4个部分:

text
LATEST DETECTED DEADLOCK
------------------------
*** (1) TRANSACTION:     [关键点1:事务1信息]
TRANSACTION 1234, ACTIVE 10 sec updating
mysql tables in use 1, locked 1
[看这里→] UPDATE table_x SET ... WHERE id = 1  [关键SQL1]

*** (1) HOLDS THE LOCK(S):    [关键点2:事务1持有的锁]
RECORD LOCKS index `idx_name` of table `db`.`table` trx id 1234 lock_mode X

*** (1) WAITING FOR THIS LOCK(S): [关键点3:事务1等待的锁]
RECORD LOCKS index `primary` of table `db`.`table` trx id 1234 lock_mode X waiting

*** (2) TRANSACTION:     [关键点4:事务2信息]
TRANSACTION 1235, ACTIVE 8 sec updating
[看这里→] UPDATE table_x SET ... WHERE id = 2  [关键SQL2]

4、快速诊断模板

对照这个模板分析:

检查项 要问的问题 常见原因
死锁类型 不同索引 冲突还是相同资源争用? 跨索引死锁常见
SQL语句 两个事务执行的具体SQL是什么? UPDATE/DELETE 容易死锁
资源顺序 事务访问资源的顺序是否一致 顺序不一致是主因
事务大小 事务中是否包含多个SQL 大事务容易死锁
索引使用 WHERE条件是否走了合适索引 无索引导致锁表

紧急应对策略:

sql 复制代码
-- 1. 查看当前锁等待情况(辅助分析)
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;

-- 2. 查看当前活跃事务
SELECT * FROM information_schema.INNODB_TRX ORDER BY trx_started DESC;

死锁解决方案

1、分析是否事务过大导致。

2、有无涉及到两段代码为:

A B

B A

的过程

3、分析出现死锁的日志记录,来根本上确认问题

参考: https://dtstack.yuque.com/rd-center/sm6war/aglda8cxlhvzk691

相关排查思路文章

1\]. Mysql死锁日志分析:事务逻辑冲突的排查技巧 原创:https://blog.51cto.com/jima/14056286 \[2\]. MySQL 故障案例分析:从死锁到数据丢失的全面诊断指南:https://blog.csdn.net/qq_43414012/article/details/152271786 ## 资料获取 大家点赞、收藏、关注、评论啦\~ 精彩专栏推荐订阅:在下方专栏👇🏻 * [长路-文章目录汇总(算法、后端Java、前端、运维技术导航)](https://blog.csdn.net/cl939974883/category_11568291.html?spm=1001.2014.3001.5482):博主所有博客导航索引汇总 * [开源项目Studio-Vue---校园工作室管理系统(含前后台,SpringBoot+Vue)](https://changlu.blog.csdn.net/article/details/125295334):博主个人独立项目,包含详细部署上线视频,已开源 * [学习与生活-专栏](https://blog.csdn.net/cl939974883/category_10700595.html):可以了解博主的学习历程 * [算法专栏](https://blog.csdn.net/cl939974883/category_11403550.html?spm=1001.2014.3001.5482):算法收录 更多博客与资料可查看👇🏻获取联系方式👇🏻,🍅文末获取开发资源及更多资源博客获取🍅