数据库死锁排查的核心是“定位死锁SQL + 分析锁竞争逻辑”,需结合数据库自带工具和日志快速定位,不同数据库(以MySQL、Oracle为例)排查流程如下:
一、通用排查逻辑:先“抓现场”,再“溯根源”
1.优先捕获死锁现场 死锁发生时会立即释放资源,需第一时间获取“谁在竞争锁、竞争什么锁”,避免现场丢失。
2.分析锁竞争链路 明确死锁的SQL语句、事务执行顺序,判断是“资源循环等待”(如A占锁1等锁2,B占锁2等锁1)还是“锁粒度不合理”(如用表锁导致大面积阻塞)。
3.验证并优化 复现死锁场景(可选),调整SQL、事务或锁策略,解决根本问题。
二、分数据库快速排查方案
1. MySQL 死锁排查(最常用)
MySQL 提供 show engine innodb status 和死锁日志,是排查核心工具。
步骤1:实时捕获死锁现场
执行以下命令,在输出结果中找到 “LATEST DETECTED DEADLOCK” 段落,该部分会显示最近一次死锁的完整信息:
show engine innodb status;
关键信息解读(需重点关注):
TRANSACTIONS FOR EACH SESSION:两个死锁事务的ID(如 TRANSACTION 12345);
WAITING FOR THIS LOCK TO BE GRANTED:事务等待的锁类型(如行锁 RECORD LOCKS、表锁 TABLE LOCKS)及关联的表、索引、行数据(如 index PRIMARY of table test.user);
HOLDS THE LOCK(S):事务已持有的锁;
SQL STATEMENT:触发死锁的具体SQL语句(如 update user set name='a' where id=1)。
步骤2:开启死锁日志持久化(长期监控)
若死锁偶发,可开启MySQL死锁日志,将死锁信息写入日志文件,便于后续分析:
1.临时开启(重启失效):
set global innodb_print_all_deadlocks = 1;
2.永久开启(修改配置文件 my.cnf):[mysqld]
innodb_print_all_deadlocks = 1
log-error = /var/log/mysql/error.log # 日志路径,需确保权限
之后可通过查看日志文件获取历史死锁记录:
cat /var/log/mysql/error.log | grep -A 50 "DEADLOCK"
步骤3:辅助查看锁等待状态 若死锁未触发日志(或需实时监控锁等待),可执行以下SQL查看当前阻塞的事务和锁:
-- 查看所有事务及锁等待情况
select * from information_schema.innodb_trx;
-- 查看锁等待关系(谁阻塞了谁)
select * from information_schema.innodb_locks;
select * from information_schema.innodb_lock_waits;
2. Oracle 死锁排查
Oracle 死锁会自动记录到 告警日志(alert log),并可通过系统视图查询。
步骤1:通过视图快速定位死锁事务
执行以下SQL,找到死锁的会话ID(SID)、事务ID和对应的SQL:
-- 1. 查看死锁的会话和事务
select s.sid, s.serial#, t.xid, s.username, s.program
from v$session s
join v$transaction t on s.taddr = t.addr
where s.status = 'ACTIVE' and s.blocking_session is not null;
-- 2. 根据SID查询死锁SQL
select sql_text
from v$sql
where sql_id = (select sql_id from v$session where sid = 死锁SID);
步骤2:查看告警日志中的死锁详情
Oracle 死锁会自动写入告警日志,日志路径可通过以下命令查询:
show parameter background_dump_dest;
进入路径找到 alert_数据库名.log 文件,搜索关键词 “DEADLOCK DETECTED”,日志会显示死锁事务的锁类型(如 TX 行锁、TM 表锁)、竞争的资源(如表、行ID)。
三、死锁排查关键结论(快速定位原因)
1.锁类型判断:
-
若为 行锁(InnoDB RECORD/TX):大概率是“事务1更新行A,事务2更新行B,随后事务1更新行B、事务2更新行A”导致循环等待;
-
若为 表锁(MyISAM/InnoDB TM):可能是SQL未走索引(导致行锁升级为表锁),或显式使用 lock table。
2.SQL共性:
- 死锁SQL通常是 更新/删除语句(UPDATE/DELETE),且筛选条件依赖同一索引(如都基于 user.id 更新)。
四、快速止损(死锁发生后)
1.优先 终止其中一个阻塞事务(通过事务ID/SID):
-
MySQL:kill 事务ID;(事务ID可从 innodb_trx.trx_id 获取);
-
Oracle:alter system kill session 'SID,serial#';(SID和serial#从 v$session 获取)。
2.后续优化方向:
- 统一事务内SQL的执行顺序(如都先更进行A,再更新行B);
- 缩小事务范围(避免事务内包含无关操作,减少锁持有时间);
- 确保更新/删除语句走索引(避免行锁升级为表锁)。