直接查看SHOW ENGINE INNODB STATUS\G中的LATEST DETECTED DEADLOCK区块即可准确定位死锁,它完整呈现两个事务的SQL、线程ID、持锁与等待锁关系、索引名及锁类型;需立即执行并交叉比对(1)(2)事务的HOLDS THE LOCK(S)和WAITING FOR THIS LOCK TO BE GRANTED以还原循环等待环。

直接看 SHOW ENGINE INNODB STATUS\G 输出里的 LATEST DETECTED DEADLOCK 区块,就能准确定位互锁的两条 SQL、被回滚方、所涉索引和锁类型——不需要翻错误日志,也不用查应用报错堆栈。
怎么看 LATEST DETECTED DEADLOCK 里的两个事务块
输出中真正有用的部分被包裹在 ------------------------ LATEST DETECTED DEADLOCK ------------------------ 分隔符之间。每个事务以 *** (1) TRANSACTION 和 *** (2) TRANSACTION 开头,编号不表示执行先后,只表阅读顺序。
- 记下每段开头的
TRANSACTION xxxID和末尾的query id xxx对应的 SQL(如UPDATE t SET c = c + 1 WHERE id = 2),这是冲突源头 -
MySQL thread id 12可用来关联information_schema.PROCESSLIST,确认该连接的用户、DB、命令类型 - 若 SQL 显示为
UPDATE ... WHERE ?这类带问号的模板,说明用了预编译语句,需同步比对应用日志中的实际参数
怎么从 HOLDS 和 WAITING 推出锁冲突链
死锁本质是闭环等待:A 等 B 持有的锁,B 又等 A 持有的锁。必须交叉比对两段日志:
安装、修复、更新或打包一个基于 Python 和 PyMySQL 实现的 Windows `mysql` 命令。适用于用户希望在 PowerShell/CMD 中输入 `mysql` 时重定向到 Python 实现、需要交互式输入 MySQL 连接信息、需要同步到当前用户本地 bin 目录,或需要将该能力封装成可公开复用的 Codex skill。
- 事务 (1) 的
WAITING FOR THIS LOCK TO BE GRANTED内容,要和事务 (2) 的HOLDS THE LOCK(S)是否匹配(比如都指向index `idx_account`) - 事务 (2) 的
WAITING FOR THIS LOCK同样要和事务 (1) 的HOLDS THE LOCK(S)对得上(比如都落在PRIMARY) - 看到
lock_mode X locks gap before rec,基本可断定是 RR 隔离级别下范围查询 + 无索引引发的间隙锁交叉;全是rec but not gap,更可能是多表更新顺序不一致
怎么验证 WHERE 条件是否走索引导致锁扩大
90% 的死锁根源不在并发本身,而在一条本该走索引却全表扫描的 SQL。它会让 UPDATE 锁住成百上千行,大幅提高冲突概率:
- 对报错 SQL 执行
EXPLAIN,重点看type是否为ALL或index,key是否为NULL,rows是否远超预期 - 再查
performance_schema.data_locks,过滤LOCK_TYPE = 'RECORD'且LOCK_DATA大量重复或跨度极大(如从 1 到 5000) - 常见诱因:
WHERE条件用了函数(DATE(create_time))、隐式类型转换(phone = 13800138000)、OR中混入非索引列、联合索引未满足最左前缀
为什么不能只依赖错误日志查死锁
MySQL 默认只把最后一次死锁写进 SHOW ENGINE INNODB STATUS,错误日志里只有开启 innodb_print_all_deadlocks = ON 后才持续记录。但即使开了,也容易漏掉关键上下文:
- 日志里没有事务启动时间、SQL 参数、客户端 IP、应用线程 ID,这些必须靠
INNODB_TRX和应用日志补全 - 云数据库(如阿里云 RDS)可能不支持动态修改该变量,得在控制台单独开启“死锁日志”开关
-
log_error路径权限不对时,日志会静默失效——MySQL 进程用户必须有写入权限,否则磁盘打满前你都发现不了










