必须开启performance_schema才能查sql阶段级耗时;若show variables like 'performance_schema'返回off,则需在my.cnf中配置performance_schema=on并重启,且须显式启用stage/%等instrument,否则events_stages_*表为空。

performance_schema 必须开启才能查 SQL 阶段级耗时
MySQL 默认可能关闭 performance_schema,不开启就完全看不到语句在 parse、optimize、execute 等阶段的 CPU 时间分布。执行 SHOW VARIABLES LIKE 'performance_schema';,如果返回 OFF,后续所有阶段分析都无效。
必须在 my.cnf 的 [mysqld] 段落中显式配置并重启:
performance_schema = ON performance_schema_instrument = 'statement/%=ON,stage/%=ON,wait/%=ON'
注意两点:
-
performance_schema_instrument不是默认全开,stage/%=ON这一项漏掉,events_stages_summary_by_thread_by_event_name就始终为空 - 开启后内存占用会上升,但对现代服务器影响有限;若担心,可只开
stage/%=ON和statement/%=ON
查 SQL 各阶段耗时要用 events_stages_summary_by_thread_by_event_name
这个表记录每个线程在每个 stage(如 sql/locking、sql/creating sort index、sql/executing)的累计时间,单位是皮秒(ps),需除以 10⁹ 才得秒数。
常用查询(替换 thread_id 为你从 threads 表查到的目标线程 ID):
SELECT event_name,
TRUNCATE(SUM(timer_wait)/1000000000, 6) AS total_sec,
COUNT_STAR AS count
FROM performance_schema.events_stages_summary_by_thread_by_event_name
WHERE thread_id = 12345
AND event_name LIKE 'stage/sql/%'
GROUP BY event_name
ORDER BY total_sec DESC
LIMIT 10;
关键 stage 含义:
-
stage/sql/Waiting for query cache lock:说明 query cache 开着且争用严重(MySQL 8.0 已移除,5.7 要关) -
stage/sql/Creating sort index或stage/sql/Sorting result:ORDER BY 或 GROUP BY 触发了文件排序,sort_buffer_size不够或缺索引 -
stage/sql/Executing值异常高:可能是大结果集网络传输慢,或函数计算密集(如 JSON_EXTRACT、正则)
关联 threads 和 events_statements_current 定位具体 SQL
单看 stages 表只能知道“某个线程在排序上花了 2.3 秒”,但不知道它执行的是哪条 SQL。需要三表 JOIN:
SELECT s.thread_id,
t.processlist_id,
t.processlist_info AS sql_text,
s.event_name AS stage,
TRUNCATE(s.timer_wait/1000000000, 6) AS stage_sec
FROM performance_schema.events_stages_current s
JOIN performance_schema.threads t ON s.thread_id = t.thread_id
JOIN performance_schema.events_statements_current es
ON s.thread_id = es.thread_id
WHERE t.processlist_id IS NOT NULL
AND s.event_name LIKE 'stage/sql/%'
AND s.timer_wait > 1000000000 -- 耗时超 1 秒的 stage
ORDER BY s.timer_wait DESC
LIMIT 5;
注意坑点:
-
events_stages_current只存「当前正在执行」的 stage,不是历史;想查历史得用events_stages_history_long(需提前开对应 instrument) -
processlist_info在长 SQL 场景下会被截断(默认 1024 字符),不能依赖它识别完整逻辑 - 如果
processlist_info为NULL,说明该线程没在执行用户 SQL(比如在做刷脏页、purge 线程等后台操作)
stage 名称不等于执行逻辑,要结合 EXPLAIN 看真实行为
比如看到大量 stage/sql/Sending data,很多人以为是网络慢,其实 MySQL 文档明确写过:这个 stage 包含「读取行、应用 WHERE 条件、生成结果集」全过程。真正瓶颈往往在磁盘 IO 或 CPU 计算,而非发送本身。
验证方法:
- 查
rows_examined:如果一条Sending data耗时 3 秒,但rows_examined是 500 万,基本就是全表扫描 + 条件过滤慢 - 对比
EXPLAIN的type和Extra:若出现Using where; Using filesort,那stage/sql/Sorting result和stage/sql/Sending data往往会同时飙升 -
stage/sql/Opening tables高?可能是table_open_cache不够,频繁打开关闭 frm 文件
stage 统计只是线索,不是结论;最终得把 events_stages_*、events_statements_*、EXPLAIN 和实际表结构四者对齐,才敢说清 CPU 到底卡在哪一步。











