因为MySQL 8.0默认关闭等待事件采集,events_waits_current表仅保留线程最新等待状态且不存历史,必须先启用events_waits_current消费者及wait/lock/%类instruments才能查到数据。

直接查 events_waits_current 为什么总是空?
因为 MySQL 8.0 默认关闭等待事件采集,performance_schema.events_waits_current 表本身不存历史,只保留每个线程当前最新一条等待状态。如果没开采集,它就是空的,不是你查得不对。必须分两步激活:
- 启用消费者:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_waits_current'; - 启用相关 instruments:
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'wait/lock/%';(只开锁类就够了,不用全开wait/%)
注意:这两条 UPDATE 需要 root 或具备 PERFORMANCE_SCHEMA 权限的用户执行,且修改后立即生效,无需重启。
怎么定位“行锁升表锁”发生的瞬间?
MySQL 不会把“锁升级”作为一个独立事件记录,它体现为两个连续行为:先有行级锁等待(如 wait/lock/innoDB/record),紧接着出现表级锁请求(如 wait/lock/table/sql/handler 或 wait/lock/metadata/sql/mdl)。关键不是看单条记录,而是看线程的等待链:
- 先查正在 WAITING 的线程:
SELECT THREAD_ID, EVENT_NAME, TIMER_WAIT, OBJECT_NAME FROM performance_schema.events_waits_current WHERE STATE = 'WAITING' AND EVENT_NAME LIKE 'wait/lock/%'; - 再关联该线程最近的锁申请:
SELECT * FROM performance_schema.data_locks WHERE OWNER_THREAD_ID = 123 ORDER BY LOCK_TRX_ID DESC LIMIT 5;(把 123 换成上一步查到的THREAD_ID) - 如果发现同一事务里,
LOCK_TYPE从RECORD突然变成TABLE,且OBJECT_NAME是整张表名,基本就是锁升级了
data_locks 里看到一堆锁,但不知道对应哪条 SQL?
这是最常踩的坑:只查 data_locks,看到 LOCK_MODE = 'X'、LOCK_TYPE = 'TABLE' 就以为是锁升级,结果发现是某条 DDL(比如 ALTER TABLE)自己申请的元数据锁。必须回溯到原始 SQL:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 从
data_locks拿到OWNER_THREAD_ID(比如 456) - 去
performance_schema.threads查:SELECT PROCESSLIST_ID, PROCESSLIST_INFO FROM performance_schema.threads WHERE THREAD_ID = 456; -
PROCESSLIST_INFO字段里就是正在执行或刚执行完的语句——如果为空,说明事务已提交或连接断开,只能结合information_schema.INNODB_TRX的TRX_QUERY辅助判断
特别注意:PROCESSLIST_INFO 可能被截断,长 SQL 看不全;而 INNODB_TRX.TRX_QUERY 在事务卡住时更可靠,但只对 InnoDB 事务有效。
为什么 FOR UPDATE 有时直接锁全表?
这不是“升级”,是压根没走索引导致的隐式降级。InnoDB 行锁只加在索引上,如果 WHERE 条件无法命中任何索引(包括没有索引、类型不匹配、函数包裹字段等),优化器会退化为全表扫描,然后对**聚簇索引所有记录**加 X 锁——效果等同于表锁,但底层仍是行锁集合。
- 验证方式:
EXPLAIN SELECT * FROM t WHERE non_indexed_col = 1 FOR UPDATE;如果type是ALL,就危险了 - 真正的锁升级(如从行锁主动升为表锁)只发生在显式
LOCK TABLES或某些特殊 DDL 场景,普通 DML 不会触发 - 所以排查重点永远是:先看执行计划,再看锁,最后才怀疑“升级”
真正难抓的不是“锁升级事件”,而是“本该走索引却没走”的那条 SQL —— 它不会报错,但会让整个表动弹不得。










