mysql死锁如何排查_mysql锁日志分析方法

5次阅读

mysql默认仅保留最近一次死锁日志,需立即执行show engine innodb status查看;开启innodb_print_all_deadlocks可将所有死锁写入错误日志;云数据库推荐使用控制台“死锁分析”页签快速定位。

mysql死锁如何排查_mysql锁日志分析方法

直接看 SHOW ENGINE INNODB STATUS 最新死锁日志

MySQL 默认只保留**最近一次**死锁的完整现场,不存历史记录。所以出问题后第一反应就是立刻执行:

SHOW ENGINE INNODB STATUSG

然后在输出里搜 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_namePRIMARY 上,基本能断定是“先更新非主键索引,再删主键行” 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_timeObject(死锁内容 json)、SQL 原始语句都直接可点开

容易被忽略的是:这些页面显示的 SQL 是“被回滚的那条”,而真正引发竞争的前置操作(比如一个没加 FOR UPDATESELECT 引发了隐式锁升级)并不会出现在这里——它只记录最终碰撞点,不记录全过程。

text=ZqhQzanResources