Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL死锁类deadlock排查

MySQL死锁类deadlock问题排查

作者:长路 ㅤ

本文主要介绍了MySQL死锁类deadlock问题排查,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

认识常用命令

SHOW ENGINE INNODB STATUS;

介绍

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

  1. BACKGROUND THREAD: 后台主线程(如刷新脏页)的活动信息。
  2. SEMAPHORES: 信号量信息,用于诊断线程间争用和锁等待。如果系统存在大量锁等待,这里会有体现。
  3. LATEST DETECTED DEADLOCK: 这是你最关心的部分。它记录了最近一次发生的死锁的详细信息,包括:
  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;命令

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

SHOW ENGINE INNODB STATUS;

方式二:死锁日志记录

初始化

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

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

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

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

[mysqld]
innodb_print_all_deadlocks = ON

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

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

SET GLOBAL innodb_print_all_deadlocks = ON;

2、配置并监控错误日志
找到你的 MySQL 错误日志文件路径(可以通过 SHOW VARIABLES LIKE 'log_error'; 查看)。
之后,每当发生死锁,你都可以去这个日志文件里搜索 "DEADLOCK" 关键字,找到完整的死锁报告。

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

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

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

*** (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

步骤三:解读死锁报告并定位业务代码

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

涉及的事务:两个(或多个)事务分别在执行什么 SQL?

示例中,两个事务都在执行 DELETE,但条件不同 (a=1a=2)。

  1. 锁的持有和等待关系
  1. 死锁成因
    这就形成了一个典型的“循环等待”:事务1等事务2,事务2又在等事务1。数据库为了打破僵局,选择回滚其中一个(这里是事务2)。

排查过程

1、立即捕获死锁现场

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

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

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

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

# 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、快速分析核心问题

在死锁日志中,重点关注以下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条件是否走了合适索引无索引导致锁表

紧急应对策略:

-- 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

的过程

相关排查思路文章

[1]. Mysql死锁日志分析:事务逻辑冲突的排查技巧 

[2]. MySQL 故障案例分析:从死锁到数据丢失的全面诊断指南

到此这篇关于MySQL死锁类deadlock问题排查的文章就介绍到这了,更多相关MySQL死锁类deadlock排查内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文