performance schema 真正启用需验证两件事:一是 @@performance_schema=1,二是 setup_instruments 中 statement/sql/% 和 setup_consumers 中 events_statements_history_long 均设为 yes,并执行 flush status。

确认 Performance Schema 是否真正启用并采集有效事件
光在 my.cnf 里写 performance_schema = ON 不等于就能查到慢 SQL。必须验证两件事:一是开关真开了,二是关键事件类型和消费者真启用了。
先执行:SELECT @@performance_schema; —— 返回 1 才算基础开启成功。
但更常见的情况是返回 1 却查不到数据,原因在下面两点:
-
setup_instruments中statement/sql/select、statement/sql/insert等默认是OFF,得手动打开:UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'statement/sql/%'; -
setup_consumers中events_statements_history_long默认是OFF,而它才是存“最近大量 SQL”的核心表:UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_statements_history_long';
改完必须执行 FLUSH STATUS; 或重启采集器,否则缓存里还是旧数据,新 SQL 不进表。
查 CPU 消耗最高的 SQL:别只看执行时间,要关联 OS 线程
高 CPU 往往不是某条 SQL “慢”,而是它在反复执行、锁等待、或没走索引导致 CPU 空转。单纯按 TIMER_WAIT 排序可能漏掉这类语句。
正确路径是:OS 层定位高 CPU 线程 → 映射到 MySQL 内部线程 → 找出对应 SQL。
- 用
pidstat -t -p $(pgrep mysqld) 1抓持续占用 CPU >80% 的线程 ID(比如4445) - 查
performance_schema.threads找THREAD_OS_ID = 4445对应的PROCESSLIST_ID - 再查
information_schema.PROCESSLIST,用那个id拿到当前正在跑的INFO字段,就是活的 SQL
注意:THREAD_OS_ID 在 MySQL 5.7+ 才有;如果查不到,说明该线程已退出,得结合 events_statements_history_long 回溯最近 10000 条中该线程执行过的语句(用 THREAD_ID 关联)。
查执行次数最多或平均耗时最离谱的 SQL:用 DIGEST_TEXT 聚类,别信原始 SQL
直接查 SQL_TEXT 没意义——SELECT * FROM users WHERE id = 1 和 SELECT * FROM users WHERE id = 2 是两条不同记录,但本质是同一类问题。
Performance Schema 提供了归一化字段 DIGEST_TEXT,把参数值全替换成 ?,让同类语句自动聚类:
SELECT DIGEST_TEXT, COUNT_STAR, SUM_TIMER_WAIT / 1000000000 AS sum_sec, AVG_TIMER_WAIT / 1000000000 AS avg_sec FROM performance_schema.events_statements_summary_by_digest WHERE DIGEST_TEXT IS NOT NULL AND DIGEST_TEXT NOT LIKE 'SELECT DIGEST%' ORDER BY COUNT_STAR DESC LIMIT 10;
关键点:
-
COUNT_STAR高,说明高频低效,比如循环里查单条; -
AVG_TIMER_WAIT高(单位皮秒,除以10^9得秒),说明单次执行就卡,大概率缺索引或大表扫描; -
DIGEST_TEXT为空?说明 SQL 被截断了(默认长度 1024 字节),得调大performance_schema_max_sql_text_length参数再重启。
为什么查不到 INSERT/UPDATE,却看到一堆 COMMIT?
因为默认不采集事务级事件,COMMIT 是单独记为一条语句,但它本身几乎不耗时。真正吃资源的是它前面那几条 DML。
想看到完整事务链路,必须开两类东西:
-
setup_instruments中开transaction和statement/abstract/*(否则 DML 不进历史表) -
setup_consumers中开events_transactions_history_long
然后用 NESTING_EVENT_ID 关联事务表和语句表:SELECT t.EVENT_ID, t.END_EVENT_ID, s.DIGEST_TEXT FROM performance_schema.events_transactions_history_long t JOIN performance_schema.events_statements_history_long s ON s.EVENT_ID BETWEEN t.EVENT_ID AND t.END_EVENT_ID WHERE t.EVENT_NAME = 'transaction';
这个查询结果不稳定——events_transactions_history_long 容量极小,默认仅 10 行,且被其他 consumer 抢占严重,生产环境建议优先调大 performance_schema_events_transactions_history_long_size。











