锁等待与死锁的 SQL 级分析是什么
InnoDB 的锁问题分两种:锁等待是有人先拿了锁,别人排队,超时报 1205;死锁是双方互相等对方持有的锁,InnoDB 主动检测并回滚代价较小的一方,报 1213。定位靠三样东西:sys.innodb_lock_waits 给出的阻塞链、performance_schema.data_locks 的持锁与等锁明细、以及 information_schema.INNODB_TRX 里的事务时长与语句。
基本信息
| 适用版本 | MySQL 5.7 / 8.0(8.0 用 data_locks / data_lock_waits 视图) |
|---|---|
| 核心视图 | sys.innodb_lock_waits、performance_schema.data_locks、INNODB_TRX |
| 超时参数 | innodb_lock_wait_timeout(默认 50 秒) |
| 错误码 | 1205 锁等待超时、1213 死锁被回滚 |
| 根治方向 | 统一加锁顺序、缩短事务、命中索引减少锁范围 |
典型使用场景
- 线上频繁报 1205:确认到底是哪条 SQL 在阻塞,是不是长事务没提交
- 应用报 1213 死锁:取 InnoDB 状态里的死锁日志,还原两个事务的加锁顺序
- 批量任务跑不动:自身分批提交的事务与其他业务互相等待
- 结构变更前的评估:DDL 需要等所有长事务结束,提前清理阻塞
- 压测与容量评估:观察高并发下的锁等待峰值是否可接受
阻塞链定位
阻塞链定位
-- 谁在等、被谁挡、等了多久
SELECT waiting_pid, waiting_query, blocking_pid, blocking_query, wait_age
FROM sys.innodb_lock_waits\G
-- 当前所有锁明细:看锁类型、模式、涉及的表与索引
SELECT * FROM performance_schema.data_locks LIMIT 20;
SELECT * FROM performance_schema.data_lock_waits LIMIT 20;
-- 未提交事务清单:重点看 trx_started 与 trx_rows_locked
SELECT trx_id, trx_state, trx_started, trx_rows_locked, trx_query
FROM information_schema.INNODB_TRX ORDER BY trx_started;处置与根治
处置与根治
-- 先看阻塞方的完整语句与开启时间,再决定处置方式
SELECT id, user, time, state, info FROM information_schema.PROCESSLIST WHERE time > 60;
-- 确认是失控的长事务再终止(会回滚该事务的全部修改)
KILL 12345;
-- 临时放宽等待时间,给批处理留出空间(仅限特定场景)
SET SESSION innodb_lock_wait_timeout = 120;
-- 查看最近一次死锁的详细记录
SHOW ENGINE INNODB STATUS\G # 只看 LATEST DETECTED DEADLOCK 段关键字段读法
| 字段 | 含义 | 怎么用 |
|---|---|---|
| waiting_pid | 正在等待锁的会话 | 通常是要救的对象,可能是业务关键请求 |
| blocking_pid | 持锁不让的会话 | 看它的 trx_started 与 SQL,找根因 |
| wait_age | 已等待时长 | 增长到接近超时阈值就该主动干预 |
| lock_type / lock_mode | 锁层次与模式(X、S、GAP 等) | 判断是行锁、间隙锁还是表级元数据锁 |
| trx_rows_locked | 事务已锁行数 | 数值大说明扫描范围过宽,多半缺索引 |
死锁不必恐慌:InnoDB 会自动检测并回滚代价小的一方,应用侧只要对 1213 做有界重试即可;真正必须立刻干预的信号是锁等待超时长时间积压,以及元数据锁导致的整体阻塞。
KILL 会话会回滚其未提交的整个事务,涉及大事务时可能引发长时间回滚并放大 IO;执行前先确认该事务的写入量与应用影响面,能等提交就优先等。