Threads_running 高表明MySQL执行队列拥堵,反映并发执行压力而非单条SQL慢;配合Innodb_row_lock_waits、Created_tmp_disk_tables等变量比例与增速分析,可精准定位锁争用、内存不足或索引失效等根因。

查 Threads_running 判断 SQL 是否卡在执行队列
当大量查询堆积在执行阶段但迟迟不返回,Threads_running 会明显高于平时基线(比如日常 5–10,突然跳到 50+),说明 MySQL 正在排队处理请求,而非网络或应用层问题。这通常意味着 CPU 或锁资源争用严重,而不是单条 SQL 慢。
执行:SHOW GLOBAL STATUS LIKE 'Threads_running';
- 若该值持续高位,配合
SHOW PROCESSLIST;看State列是否集中卡在executing、Sorting result或Sending data - 注意区分
Threads_connected:连接多 ≠ 执行忙;只有Threads_running高才代表真正“正在干活”的线程多 - 该变量无法反映单条 SQL 内部耗时,仅反映并发执行压力
盯 Innodb_row_lock_waits 和 Innodb_row_lock_time_avg 看是否被锁拖住
如果 SQL 执行时间忽高忽低,且 EXPLAIN 显示走索引、扫描行数合理,但实际响应慢,大概率是行锁等待。这两个变量直接暴露锁竞争程度。
执行:SHOW GLOBAL STATUS LIKE 'Innodb_row_lock%';
-
Innodb_row_lock_waits每秒增长 > 1,说明锁冲突频繁;结合Innodb_row_lock_time_avg(单位毫秒)看平均等待时长,> 50ms 就值得警惕 - 它不告诉你哪条 SQL 在等锁,但能确认瓶颈不在磁盘或 CPU,而在事务串行化控制上
- 需进一步查
SELECT * FROM performance_schema.events_waits_current WHERE EVENT_NAME LIKE 'wait/synch/mutex/innodb/%';定位具体等待事件
观察 Created_tmp_disk_tables 和 Sort_merge_passes 发现内存不足导致的降级
当 SQL 涉及 GROUP BY、ORDER BY 或大结果集 JOIN,MySQL 会先尝试在内存建临时表或排序;若内存不够,就写磁盘——性能断崖式下跌。
执行:SHOW GLOBAL STATUS LIKE 'Created_tmp%'; SHOW GLOBAL STATUS LIKE 'Sort_merge_passes';
-
Created_tmp_disk_tables/Created_tmp_tables比值 > 10% 表示频繁落盘;Sort_merge_passes持续上升说明排序开销过大 - 直接关联配置:
tmp_table_size和max_heap_table_size必须一致且足够(如 256M),否则以较小者为准 - 注意:即使
EXPLAIN显示Using filesort,也不一定慢;但若同时Created_tmp_disk_tables高,那基本就是内存撑不住了
用 Handler_read% 类变量反推索引使用效率
Handler_read_first、Handler_read_key、Handler_read_next 这组变量记录引擎层真实读取行为,比 EXPLAIN 更接近执行时事实。
执行:SHOW GLOBAL STATUS LIKE 'Handler_read%';
-
Handler_read_first高 → 大量全索引扫描(比如SELECT * FROM t ORDER BY pk LIMIT 1) -
Handler_read_key低 +Handler_read_rnd_next高 → 索引没覆盖查询字段,回表成本高,甚至可能退化为全表扫描 - 对比
Handler_read_rnd_next/Handler_read_next比值:> 0.1 就提示存在大量随机 I/O,往往是WHERE条件未命中索引或索引选择性差
真正卡在 SQL 执行阶段的瓶颈,往往藏在这些状态变量的「比例关系」和「变化速率」里,而不是绝对数值。比如单看 Created_tmp_disk_tables = 1200 没意义,但若过去 5 分钟涨了 800,而 Questions 只涨了 200,那就明确指向某类查询触发了磁盘临时表。别只盯着一个数,要让它动起来。


















