直接看 SHOW ENGINE INNODB STATUS
最新死锁日志
MySQL 默认只保留**最近一次**死锁的完整现场,不存历史记录。所以出问题后第一反应就是立刻执行:
SHOW ENGINE INNODB STATUS\G然后在输出里搜
LATEST DETECTED DEADLOCK—— 这段内容会覆盖之前所有死锁日志,稍慢一步就丢了。
常见错误是等业务报错后再去查,结果发现日志已更新为下一次死锁(甚至没再发生),导致“明明出了错却看不到现场”。建议在监控告警触发时,立刻人工或脚本拉取该命令结果。
仅对当前连接生效,无需权限,但需有PROCESS权限(RDS 通常默认开放) 输出中事务 ID(如
TRANSACTION 12345)、持有锁(
HOLDS THE LOCK(S))、等待锁(
WAITING FOR THIS LOCK)三块必须对照着看 注意末尾那句被回滚的 SQL,它不是“罪魁祸首”,而是压垮骆驼的最后一根稻草——真正的问题藏在两个事务访问资源的**顺序差异**里
让所有死锁都落盘:开启 innodb_print_all_deadlocks
靠手动抓
SHOW ENGINE太被动,尤其线上偶发、低频死锁根本来不及响应。必须改配置,把每次死锁都写进错误日志:
在
my.cnf的
[mysqld]段加上:
innodb_print_all_deadlocks = ON<br>log_error = /var/log/mysql/error.log重启 MySQL 生效(RDS 可通过参数模板修改,无需重启) 日志会追加到
log_error指定路径,用
grep -A 20 -B 5 "Deadlock" /var/log/mysql/error.log快速提取 ⚠️ 别忽略磁盘空间:高频死锁时日志增长快,建议配合 logrotate 或云服务的日志轮转策略
从日志定位“谁在锁谁”:看 space id + page no + index
死锁日志里这类行最烧脑也最关键:
RECORD LOCKS space id 88 page no 7 index `idx_account`它不告诉你哪条数据,但给出了精确物理位置:
space id对应表空间(一般等于
INFORMATION_SCHEMA.INNODB_TABLES.TABLE_ID)
page no是页号,结合
index名可反查冲突行所在索引的范围 例如两个事务分别卡在
idx_name和
PRIMARY上,基本能断定是“先更新非主键索引,再删主键行” vs “先删主键行,再更新非主键索引”这种经典反序操作
真要精确定位某一行?用
SELECT * FROM table WHERE id = ?查主键值不靠谱——因为锁可能落在间隙(gap lock)上。更稳的方式是用
SELECT * FROM INFORMATION_SCHEMA.INNODB_BUFFER_PAGE WHERE SPACE = 88 AND PAGE_NUMBER = 7(需开启
innodb_buffer_page监控,生产慎用)。
云数据库别只盯命令:善用控制台“死锁分析”页签
华为云 RDS、GaussDB、TaurusDB 等托管服务都内置了可视化死锁分析能力,比手撕日志快得多:
进控制台 → 找到目标实例 → “锁&事务” → “死锁分析”页签 “最近死锁分析”展示最后一次死锁的图形化等待链(事务 A → B → C → A) “全量死锁分析”需手动开启开关,支持查最近 7 天全部死锁(注意:未开启前的历史数据不可恢复) 关键字段如happen_time、
Object(死锁内容 JSON)、
SQL原始语句都直接可点开
容易被忽略的是:这些页面显示的 SQL 是“被回滚的那条”,而真正引发竞争的前置操作(比如一个没加
FOR UPDATE的
SELECT引发了隐式锁升级)并不会出现在这里——它只记录最终碰撞点,不记录全过程。
