最直接定位已形成锁等待的方式是查INNODB_TRX和INNODB_LOCK_WAITS,不依赖Performance Schema;若前者有LOCK WAIT但后者为空,说明尚未真正阻塞;执行关联查询可获取阻塞源SQL及等待时长,注意wait_time_sec从trx_started起算,blocking_query为NULL表明阻塞方是空闲事务;推荐用performance_schema.data_lock_waits查锁详情(需确保对应instrument和consumer已启用);MyISAM表锁争用应查SHOW STATUS LIKE 'Table_locks%'及SHOW OPEN TABLES;events_waits_history_long查不到锁等待事件通常因setup_consumers未开启。

查 INNODB_TRX 和 INNODB_LOCK_WAITS 看实时阻塞链
这是定位已形成锁等待最直接的方式,不依赖 Performance Schema 配置是否开启。只要事务卡住、已升级为阻塞,就能立刻看到谁在等谁。
常见错误现象:SELECT * FROM information_schema.INNODB_TRX 里有 trx_state = 'LOCK WAIT' 的记录,但 INNODB_LOCK_WAITS 为空——说明还没真正形成阻塞(比如只是短暂竞争),或事务刚启动还没走到加锁逻辑。
- 执行以下语句可直接拿到阻塞源 SQL:
SELECT r.trx_id waiting_trx_id, r.trx_mysql_thread_id waiting_thread, r.trx_query waiting_query, b.trx_id blocking_trx_id, b.trx_mysql_thread_id blocking_thread, b.trx_query blocking_query, TIMESTAMPDIFF(SECOND, r.trx_started, NOW()) wait_time_sec FROM information_schema.INNODB_TRX r INNER JOIN information_schema.INNODB_LOCK_WAITS w ON r.trx_id = w.requesting_trx_id INNER JOIN information_schema.INNODB_TRX b ON b.trx_id = w.blocking_trx_id;
- 注意
wait_time_sec是从trx_started开始算的,不是从锁等待开始;若值极大,大概率是事务没提交,而非锁本身慢 - 若
blocking_query为NULL,说明阻塞方是个空闲事务(trx_state = 'RUNNING'但没在执行 SQL),要重点查应用层是否漏了COMMIT或ROLLBACK
用 performance_schema.data_lock_waits 查锁等待详情(MySQL 5.7+)
相比旧的 INNODB_LOCKS(8.0 已移除),data_lock_waits 是当前推荐方式,能明确看到“谁在等哪把锁”以及“哪条索引记录被锁”。
使用前提:必须确保 performance_schema 已启用,且相关 instruments 没被手动关掉。很多人查不到结果,其实是 setup_instruments 里 wait/innodb/innodb_lock_wait 被设成了 'NO'(默认是 'YES',但有些运维脚本会批量关)。
- 确认是否启用:
SELECT NAME, ENABLED FROM performance_schema.setup_instruments WHERE NAME LIKE '%innodb_lock_wait%';
返回ENABLED = 'YES'才有效 - 查当前锁等待关系:
SELECT * FROM performance_schema.data_lock_waits;
输出中BLOCKING_ENGINE_LOCK_ID对应data_locks.LOCK_ID,可联查锁定的具体表、索引、主键值 -
data_locks.LOCK_TYPE是关键:'RECORD'表示行锁,'TABLE'表示表锁(InnoDB 下极少出现,除非 DDL 或全表扫描未走索引)
区分 MyISAM 表锁争用:看 SHOW STATUS LIKE 'Table_locks%'
MyISAM 没有事务、没有行锁,所有写操作都触发表级锁,争用会直接反映在状态变量上。别试图用 INNODB_TRX 查 MyISAM 锁——它压根不归 InnoDB 管。
典型错误现象:SHOW PROCESSLIST 里大量连接状态为 Locked,且都在操作同一张 MyISAM 表;同时 SELECT COUNT(*) FROM myisam_table 也变慢——说明读也被写锁堵住了。
- 运行:
SHOW STATUS LIKE 'Table_locks%';
关注两个值:Table_locks_waited(等待表锁的次数)、Table_locks_immediate(立即获得表锁的次数) - 如果
Table_locks_waited持续上涨,且与Table_locks_immediate比值 > 1%,基本可以判定存在严重表锁争用 - 进一步确认哪张表被锁:用
SHOW OPEN TABLES WHERE In_use > 0;,In_use值大于 1 就说明该表正被多个线程争抢
为什么 events_waits_history_long 查不到 wait/innodb/innodb_lock_wait 事件?
这不是 bug,而是配置缺失。Performance Schema 默认关闭该 instrument,且 consumers 也要手动开,否则事件根本不会被采集。
最容易被忽略的点:开了 setup_instruments 还不够,setup_consumers 中的 events_waits_history_long 和 events_waits_current 也得是 'YES',否则即使事件发生了,也进不了历史表。
- 一次性启用(需 SUPER 权限):
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME = 'wait/innodb/innodb_lock_wait'; UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('events_waits_history_long', 'events_waits_current'); - 生效后,用
SELECT * FROM performance_schema.events_waits_history_long WHERE EVENT_NAME = 'wait/innodb/innodb_lock_wait' ORDER BY TIMER_START DESC LIMIT 10;可查到最近的行锁等待事件,含等待时长、线程 ID、堆栈(如果stackinstrument 也开了) - 注意:该表默认只保留 10000 条,高频场景下可能被快速覆盖;若需长期留存,得配合外部采集或改大
performance_schema_events_waits_history_long_size参数
真正难的不是查到锁,而是判断“这把锁本不该存在”——比如明明查主键却锁了整段索引,或者 MyISAM 表上突然出现高并发写。这时候得倒回去看执行计划、索引定义、事务边界,而不是只盯着锁视图打转。


















