排查 MySQL 性能问题,很多人的第一反应是 SHOW PROCESSLIST 看一眼线程状态,再去翻慢查询日志。这两样工具我用了很多年,它们能解决一部分问题,但都有一个共同的短板:回答的是"发生了什么",回答不了"时间到底花在哪"。
一条 SQL 执行了 5 秒,是在等锁,还是在等磁盘 I/O,还是卡在排序阶段?慢日志只能告诉你它慢,SHOW PROCESSLIST 只能给你 Sorting result 这样一个粗粒度的状态快照。想再往下挖一层,传统手段就到头了。

Performance Schema(下文简称 P_S)的思路完全不同。它把数据库内部的运行情况变成了一个可查询的数据源:事件采集下来,存进内存表,你用 SQL 去查。用 SQL 查性能数据这件事本身,就是思路的一次升级。
原理:插桩点 + 内存表 + SQL 查询
P_S 的工作流可以拆成三段。
第一段是采集。MySQL 源码里埋了大量插桩点(instrument),任何"耗时"的服务器操作,比如等一把锁、读一个文件、执行一个语句,经过插桩点时都会记下一个事件。
第二段是存储。事件写进 P_S 自己的内存表,由一个专用存储引擎支撑,不落盘。
第三段是查询。所有数据用普通 SQL 就能取出来。默认开启,开销很小,生产环境放心用。
想用好 P_S,先得读懂 instrument 的名字。命名是分层的,像文件路径,用 / 分隔,从左到右由粗到细。前缀表示事件类型:
-
wait:等待事件,比如等 I/O、等锁
-
stage:语句执行阶段,和 SHOW PROCESSLIST 里的状态对应
-
statement:语句事件
-
transaction:事务事件
-
memory/
idle:内存事件、空闲事件
后缀表示代码区域。同一个组件在不同上下文里含义不同,比如:
wait/io/file/innodb/innodb_log_file -- 等 InnoDB 日志文件的 I/Owait/synch/mutex/sql/LOCK_delete -- 等一个源码里的互斥锁stage/sql/Sorting result -- 排序阶段
wait/io/file/innodb/... 里的 InnoDB 是文件 I/O 的属主,wait/synch/cond/innodb/... 里的 InnoDB 是同步机制的一部分。读懂名字,才读得懂事件。
配置:按需采集,控制开销
P_S 的配置集中在几张 setup_* 表里,各管一件事:
-
setup_instruments:开哪些采集点。
ENABLED管采集与否,TIMED管是否计时 -
setup_consumers:哪些事件表真正落数据
-
setup_objects:按库、表对象过滤
-
setup_actors:按用户、主机过滤前台线程
consumer 是有层级的。最顶上 global_instrumentation 是总开关,关掉它,下面所有配置都不生效。往下是 thread_instrumentation,再往下才是 events_waits_current、events_statements_history 这类具体的事件表。statements_digest 比较特殊,管的是 SQL 归一化聚合,慢查询分析靠它。
核心原则一句话:只采你需要的。默认配置对日常诊断已经够用,事件采得越多开销越大。需要深挖等待细节时,再按需打开 wait 和 stage 的 history:
UPDATE setup_instruments SET ENABLED='YES', TIMED='YES'WHERE NAME LIKE 'wait/%'; UPDATE setup_consumers SET ENABLED='YES'WHERE NAME LIKE 'events_waits_%';
查问题定位完,记得关回去。想看 P_S 自己占了多少内存:
SHOW ENGINE performance_schema STATUS;
它的内存模型是自动扩缩的,运行期只回收不释放,关闭服务才彻底归还。一般不用操心,心里有数就行。
查询:下钻靠事件层级,聚合靠 digest
P_S 的表分几类,记规律不用死记表名:
-
events_xxx_current:每个线程最近一条事件
-
events_xxx_history:每个线程最近 10 条
-
events_xxx_history_long:全局最近一万条
-
xxx_instances:被监控对象的实例,mutex、rwlock、file、socket、cond 各一张
-
events_xxx_summary_*:按 digest、用户、主机、线程等维度的聚合表
事件本身是有嵌套层级的:会话套事务,事务套语句,语句套 stage,stage 套 wait。每一层事件通过 NESTING_EVENT_ID 指向上层,这就是下钻的路径。从一条慢语句出发,沿着嵌套关系往下走,能一路定位到具体卡在哪把锁、哪个文件 I/O 上。
日常分析慢查询,主力是 events_statements_summary_by_digest。它把 SQL 归一化(字面量替换成 ?)后按 digest 聚合,一行就是一类语句的完整画像:
SELECT DIGEST_TEXT, COUNT_STAR, SUM_TIMER_WAIT, AVG_TIMER_WAIT, SUM_ROWS_EXAMINED, SUM_NO_INDEX_USED, SUM_LOCK_TIME, FIRST_SEEN, LAST_SEENFROM events_statements_summary_by_digestORDER BY SUM_TIMER_WAIT DESCLIMIT 10;
COUNT_STAR 高、SUM_TIMER_WAIT 大、SUM_NO_INDEX_USED 不为零,又慢又频繁还不走索引的语句,一眼现形。这比翻慢日志高效得多。
一个必须记住的坑:原始事件表里的计时单位是皮秒(10⁻¹² 秒)。看到 TIMER_WAIT 是一串天文数字别慌,先确认单位:
SELECT * FROM setup_timers;
stage 事件用的是纳秒,wait 事件默认用 CYCLE 计时器。算不准的时候,用 sys 的 format_time() 转一下。
再给一个随手可用的例子,找出最近没走索引的 SELECT:
SELECT SQL_TEXT FROM events_statements_historyWHERE NO_INDEX_USED != 0 AND EVENT_NAME = 'statement/sql/select';
sys schema:把门槛踏平
P_S 原始表有几十张,列名也不友好,直接查的学习成本不低。sys schema 就是干这个的:一层封装好的视图加存储过程,视图直接给结论,不用自己拼复杂查询。
视图大多成对出现:statement_analysis 是人类可读版,时间格式化成 16.75 s 这种;x$statement_analysis 保留原始数值,给工具和脚本做二次加工用。
几张我日常用得最多的:
-- 按语句聚合的完整画像:总/平均/最大延迟、扫描行数、临时表、全表扫描标记SELECT * FROM sys.statement_analysis LIMIT 5; -- 延迟排进前 5% 的语句SELECT * FROM sys.statements_with_runtimes_in_95th_percentile; -- 做全表扫描的语句SELECT * FROM sys.statements_with_full_table_scans; -- 建了之后从没被用过的索引SELECT * FROM sys.schema_unused_indexes; -- InnoDB 锁等待的完整链条:谁等谁、等了多久、各自的 SQLSELECT * FROM sys.innodb_lock_waits;
sys.processlist 也值得换掉 SHOW PROCESSLIST。它走 P_S 的 threads 表,非阻塞,而且附带 last_wait、trx_latency、current_memory 这些原生 processlist 没有的信息。
配置方面 sys 也给了快捷方式。不用去 UPDATE setup 表,一键开关采集:
CALL sys.ps_setup_enable_instrument('wait');CALL sys.ps_setup_enable_instrument('stage');CALL sys.ps_setup_enable_consumer('history_long');
排查完想恢复出厂状态,CALL sys.ps_setup_reset_to_default(TRUE); 一条搞定。皮秒换算交给 sys.format_time(342342342342345),返回 00:05:42,救眼神。
喜欢用图形界面的话,MySQL Workbench 里也有 P_S 的配置页和现成报表,数据同样来自这套体系,顺手一提。
收尾:P_S 的正确姿势
我的用法是三层节奏。P_S 默认常开,这部分开销可以忽略,不用纠结。日常巡检靠 sys 视图,statement_analysis 和 waits_global_by_latency 扫一遍,异常基本有数。真出了问题再按需开 wait、stage 的深采集,定位完关回去。
最后一点要记牢:P_S 的数据全在内存里,实例重启即清空。它是诊断工具,不是历史库。想看长期趋势,得定期把关键聚合表落盘,或者交给监控系统常态化采集。把它当"案发时的现场记录仪"用,别指望它记得上个月的事。
今天话题就聊到这,欢迎留言交流。觉得内容有用,别忘了点赞转发给有需要的朋友,回见!