MySQL事务死锁排查的常用方法如下(以5.7版本为例):

一、检查死锁日志

1. 开启死锁日志记录

-- 查看当前设置
SHOW VARIABLES LIKE 'innodb_print_all_deadlocks';
SHOW VARIABLES LIKE 'innodb_status_output_locks';

-- 开启详细日志(临时)
SET GLOBAL innodb_print_all_deadlocks = ON;
SET GLOBAL innodb_status_output_locks = ON;

2. 查看死锁信息

-- 查看最近死锁信息
SHOW ENGINE INNODB STATUS\G;

-- 查看错误日志中的死锁记录
-- 错误日志位置:SHOW VARIABLES LIKE 'log_error';

二、分析死锁信息

死锁日志通常包含以下关键部分:

LATEST DETECTED DEADLOCK
------------------------
1) TRANSACTION: trx_id 12345
   Locks held: 记录持有的锁
   Locks waiting: 记录等待的锁
2) TRANSACTION: trx_id 67890
   Locks held: 记录持有的锁  
   Locks waiting: 记录等待的锁
*** WE ROLL BACK TRANSACTION trx_id 12345

三、监控工具

1. 查询死锁相关信息(MySQL 5.7)

-- 启用死锁监控
UPDATE performance_schema.setup_consumers 
SET ENABLED = 'YES' 
WHERE NAME LIKE '%events_transactions%';

-- 查询死锁相关信息
SELECT * FROM information_schema.INNODB_TRX;
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;

2. 查看锁等待关系(MySQL 5.7)

-- 查看正在执行的事务
SELECT * FROM information_schema.INNODB_TRX;

-- 查看锁等待关系
SELECT 
  r.trx_id waiting_trx_id,
  r.trx_mysql_thread_id waiting_thread,
  r.trx_query waiting_query,
  b.trx_id blocking_trx_id,
  b.trx_mysql_thread_id blocking_thread,
  b.trx_query blocking_query
FROM information_schema.INNODB_LOCK_WAITS w
INNER JOIN information_schema.INNODB_TRX b 
  ON b.trx_id = w.blocking_trx_id
INNER JOIN information_schema.INNODB_TRX r 
  ON r.trx_id = w.requesting_trx_id;

从 MySQL 8.0 开始,INFORMATION_SCHEMA.INNODB_LOCKS 和 INFORMATION_SCHEMA.INNODB_LOCK_WAITS 这两个表已经被移除了。以下是 MySQL 8.0+ 的替代方案:

3. 查询死锁相关信息(MySQL 8.0+)

3.1. 使用 performance_schema 相关表
-- 查看数据锁信息(替代 INNODB_LOCKS)
SELECT * FROM performance_schema.data_locks;

-- 查看数据锁等待信息(替代 INNODB_LOCK_WAITS)  
SELECT * FROM performance_schema.data_lock_waits;

-- 查看元数据锁信息
SELECT * FROM performance_schema.metadata_locks;
3.2. 使用 sys 系统库(更友好的视图)
-- 查看当前锁等待情况(推荐使用)
SELECT * FROM sys.innodb_lock_waits;

-- 查看详细的锁信息
SELECT * FROM sys.x$innodb_lock_waits;

-- 查看会话的锁信息
SELECT * FROM sys.session WHERE trx_state = 'LOCK WAIT'\G

4. 查看锁等待关系(MySQL 8.0+)

-- 方式1:使用 sys 库(最简洁)
SELECT 
  waiting_trx_id,
  waiting_pid,
  waiting_query,
  blocking_trx_id,
  blocking_pid,
  blocking_query,
  wait_age,
  sql_kill_blocking_query
FROM sys.innodb_lock_waits;

-- 方式2:使用 performance_schema
SELECT 
  r.trx_id waiting_trx_id,
  r.trx_mysql_thread_id waiting_thread,
  r.trx_query waiting_query,
  b.trx_id blocking_trx_id,
  b.trx_mysql_thread_id blocking_thread,
  b.trx_query blocking_query
FROM performance_schema.data_lock_waits w
INNER JOIN information_schema.INNODB_TRX b 
  ON b.trx_id = w.BLOCKING_ENGINE_TRANSACTION_ID
INNER JOIN information_schema.INNODB_TRX r 
  ON r.trx_id = w.REQUESTING_ENGINE_TRANSACTION_ID;

四、预防和解决策略

1. 事务设计优化

  • 保持事务简短

  • 按固定顺序访问多个表

  • 避免大事务

  • 使用合适的隔离级别

2. SQL优化

-- 1. 使用索引减少锁范围
CREATE INDEX idx_column ON table(column);

-- 2. 尽量减少锁定行数
-- 避免全表扫描的UPDATE/DELETE

-- 3. 合理使用锁提示
SELECT ... FOR UPDATE   -- 排他锁
SELECT ... LOCK IN SHARE MODE -- 共享锁

-- 4. 使用NOWAIT或SKIP LOCKED(MySQL 8.0+)
SELECT ... FOR UPDATE NOWAIT;
SELECT ... FOR UPDATE SKIP LOCKED;

3. 应用层策略

  • 实现重试机制

  • 设置合理的锁等待超时

SET innodb_lock_wait_timeout = 50; -- 设置锁等待超时(秒)

五、排查步骤总结

  1. 收集信息:查看错误日志和SHOW ENGINE INNODB STATUS

  2. 识别模式:分析死锁涉及的SQL、表和索引

  3. 重现问题:尝试在测试环境重现

  4. 优化方案

    • 调整事务顺序

    • 添加缺失索引

    • 修改SQL语句

    • 调整隔离级别

  5. 监控验证:部署后持续监控

六、常用命令汇总

# 查看当前死锁频率
mysql> SHOW STATUS LIKE 'innodb_row_lock%';

# 查看锁等待信息
mysql> SELECT * FROM sys.innodb_lock_waits;

# 查看线程信息
mysql> SHOW PROCESSLIST;
mysql> SELECT * FROM performance_schema.threads;

通过以上方法,可以系统性地排查和解决MySQL事务死锁问题。建议先从死锁日志入手,识别出具体的锁争用模式,然后针对性地优化事务设计和SQL语句。

更多推荐