MySQL 8.0 中锁等待超时是事务长期持锁未提交的明确信号,必须使用 performance_schema.data_locks 和 data_lock_waits 替代已废弃的 INNODB_LOCKS 表,结合 sys.innodb_lock_waits 快速定位等待链与阻塞源。

锁等待超时不是随机事件,而是某个事务在某张表、某几行上长期持锁未提交的明确信号。MySQL 8.0 中必须跳过已废弃的 INNODB_LOCKS 表,直接用 performance_schema.data_locks 和 data_lock_waits 定位真实锁对象和等待链。
查当前活跃锁等待:优先看 sys.innodb_lock_waits
这个视图是 MySQL 8.0 最省力的入口,它已经帮你 JOIN 好了等待方、阻塞方、线程 ID 和 SQL:
- 执行
SELECT * FROM sys.innodb_lock_waits\G,有输出就说明正在发生锁等待;没输出 ≠ 没锁,只是没形成等待链 -
waiting_query是你应用报错那条 SQL(比如UPDATE order SET status = 'paid' WHERE id = 123) -
blocking_query很可能为空——这不是数据缺失,而是阻塞源可能来自触发器、预编译语句或未显式记录的后台操作 - 重点记下
blocking_trx_id和blocking_pid,下一步要靠它们反查真实执行状态
定位阻塞源头:用 blocking_trx_id 查 INNODB_TRX + events_statements_history
INNODB_TRX 能告诉你事务是否真在“运行”,但它的 trx_query 字段在触发器或长事务中经常为空;这时得靠 performance_schema 补全上下文:
- 先查
SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_id = 'xxx';—— 若trx_state是RUNNING且trx_started是几分钟前,大概率是忘了COMMIT - 若
trx_query为空,立刻用trx_mysql_thread_id去查performance_schema.events_statements_history:SELECT SQL_TEXT FROM performance_schema.events_statements_history WHERE THREAD_ID = xxx ORDER BY EVENT_ID DESC LIMIT 5; - 重点过滤含
NEW.、OLD.、INSERT INTO ... SELECT或无 WHERE 条件的UPDATE—— 这些极可能是触发器或隐式全表扫描导致锁扩大
确认锁范围是否失控:对比 EXPLAIN 和 data_locks 的 LOCK_DATA
很多锁等待本质是“本该只锁 1 行,结果锁了 5000 行”,原因几乎全是索引失效。不能只信 EXPLAIN,得看实际加锁记录:
- 对报错 SQL 执行
EXPLAIN FORMAT=JSON,检查key是否为NULL、type是否为ALL或index、rows是否远超预期 - 再查
SELECT LOCK_TRX_ID, LOCK_MODE, LOCK_TYPE, LOCK_TABLE, LOCK_INDEX, LOCK_DATA FROM performance_schema.data_locks WHERE LOCK_TRX_ID = 'xxx'; - 如果
LOCK_DATA出现大量重复值(如全是1,2,3,...,4999)或跨度极大(如1和5000同时存在),说明发生了全表/范围扫描加锁,不是并发高,是 SQL 写错了 - 常见诱因:WHERE 中用了函数(
DATE(create_time))、隐式类型转换(id = '123'但字段是 INT)、OR 条件混入非索引列
别忽略 MDL 锁和触发器嵌套:SHOW PROCESSLIST 里出现 Waiting for table metadata lock
这种锁不走 InnoDB 行锁体系,INNODB_LOCK_WAITS 查不到,但会卡死所有 DML。一旦看到这个状态,立刻查:
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND LOCK_STATUS = 'PENDING';- 再关联
performance_schema.threads查出对应线程正在执行什么——通常是ALTER TABLE、DROP INDEX或长时间未结束的SELECT(尤其带FOR UPDATE的大查询) - 触发器问题更隐蔽:若阻塞源 SQL 看似简单(如
INSERT INTO orders),但events_statements_history显示后续执行了多条UPDATE points_log,就要立刻查information_schema.TRIGGERS确认是否定义了AFTER INSERT触发器,且目标表无索引
真正难的不是查到哪条 SQL 在等锁,而是判断“为什么这条 SQL 非要等”。锁等待超时背后,90% 是索引设计缺陷或触发器逻辑失控,剩下 10% 才是业务并发模型问题。别急着调大 innodb_lock_wait_timeout,先让那条 SQL 走对索引路径。


















