必须启用performance_schema及data_locks等锁相关采集器,再JOIN data_locks与threads表按THREAD_ID分组统计锁实例数并排序取Top 10,WHERE过滤NULL PROCESSLIST_ID以排除后台线程,GROUP BY需包含所有非聚合字段,结果lock_count为锁实例数而非锁对象数。

直接查 performance_schema.data_locks 按线程分组计数,就能知道谁持锁最多——但前提是锁采集已启用,否则结果为空或严重缺失。
确认 performance_schema 和锁采集是否真正开启
很多用户执行查询返回 0 行或空结果,根本原因不是 SQL 写错,而是锁数据压根没被采集。必须同时满足以下三点:
-
performance_schema系统变量值为ON(查SELECT @@performance_schema;) -
setup_consumers中global_instrumentation和thread_instrumentation均为YES -
setup_instruments中所有wait/lock/innodb%相关项的ENABLED列必须是YES;若为NO,仅靠 SQL 动态更新无效,需在配置文件中加performance-schema-instrument='wait/lock/innodb%=ON'并重启
执行 Top 10 持锁线程查询(MySQL 8.0+)
核心逻辑是:对每个活跃会话(PROCESSLIST_ID IS NOT NULL)统计其在 data_locks 中的锁记录数。注意 COUNT(*) 统计的是锁实例数量(例如一行上多个索引锁、间隙锁都会单独计数),不是“锁对象”个数。
标准查询如下:
SELECT t.PROCESSLIST_ID AS `id`, t.PROCESSLIST_USER AS `user`, t.PROCESSLIST_HOST AS `host`, t.PROCESSLIST_DB AS `db`, COUNT(*) AS `lock_count` FROM performance_schema.data_locks dl JOIN performance_schema.threads t ON dl.THREAD_ID = t.THREAD_ID WHERE t.PROCESSLIST_ID IS NOT NULL GROUP BY t.THREAD_ID, t.PROCESSLIST_ID, t.PROCESSLIST_USER, t.PROCESSLIST_HOST, t.PROCESSLIST_DB ORDER BY lock_count DESC LIMIT 10;
关键点:
-
WHERE t.PROCESSLIST_ID IS NOT NULL过滤掉后台线程(如thread/innodb/io_thread),只保留客户端连接 -
GROUP BY必须包含所有非聚合字段,否则 MySQL 8.0 严格模式报错 - 若某事务刚执行完
UPDATE但尚未提交,锁可能延迟几十毫秒才出现在data_locks中,不是立刻可见
为什么不能只看 INNODB_TRX 或 SHOW PROCESSLIST?
INFORMATION_SCHEMA.INNODB_TRX 只告诉你事务在跑、持续多久、是否加了锁(TRX_STATE 是 LOCK WAIT 或 RUNNING),但不告诉你它拿了几个锁;SHOW PROCESSLIST 更只能看到状态(如 Updating 或 Locked),无法量化。真正要定位“谁拿锁最多”,必须依赖 data_locks 的逐条记录——它才是唯一暴露锁粒度的源头表。
容易忽略的延迟与采样边界
data_locks 不是实时镜像,而是 performance_schema 的采样快照。InnoDB 内部锁结构变更后,同步到该表存在微小延迟(通常











