当 SQL 执行计划显示一切正常,但系统整体却显得迟钝,或者你想知道“到底是哪条 SQL 拖垮了数据库”“哪些索引从未被使用过”“连接数和锁等待情况如何”时,EXPLAIN 已经不够用了。MySQL 提供了三个系统内置的“数据宝库”,专门用来查看数据库的结构元数据、运行时行为以及经过汇总的专家诊断信息。它们分别是 information_schema、performance_schema 和 sys 库。
11.5.1 information_schema:数据库的“数据字典”
information_schema 是一个不断更新、由 MySQL 自动维护的虚拟库。它存储的是所有数据库对象的元数据,如表结构、字段、索引、权限、字符集、参数设置等。你可以像查询普通表一样 SELECT 它,但它不占磁盘空间,所有内容都是内存映射而来。
它最实用的表及场景:
- 查看表和索引大小 – 用于容量规划和碎片分析
SELECT
TABLE_SCHEMA AS `库名`,
TABLE_NAME AS `表名`,
ROUND(DATA_LENGTH/1024/1024, 2) AS `数据大小(MB)`,
ROUND(INDEX_LENGTH/1024/1024, 2) AS `索引大小(MB)`
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'your_db'
ORDER BY DATA_LENGTH DESC;
DATA_LENGTH 和 INDEX_LENGTH 为近似值,可用来快速找出占用空间最大的表以及数据与索引的比例关系。
- 定位“可能失效”的索引
SELECT
TABLE_NAME, INDEX_NAME, CARDINALITY
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = 'your_db'
AND CARDINALITY IS NOT NULL
AND CARDINALITY <= 10; -- 选择性极低的索引
如果某个索引的 CARDINALITY(基数,即索引列上不同值的估计数量)非常小,比如只有 1 或者 10,那么优化器几乎不可能选择它,很可能属于可清理的冗余索引。但需要注意,STATISTICS 的数据来源于统计信息,并非实时精确,可先执行 ANALYZE TABLE 更新统计信息后再查看。
- 查看当前连接和事务 –
PROCESSLIST衍生版
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE
FROM information_schema.PROCESSLIST
WHERE COMMAND != 'Sleep'
ORDER BY TIME DESC;
这比 SHOW PROCESSLIST 返回结果更方便过滤和排序。结合 INNODB_TRX、INNODB_LOCK_WAITS 表还可以看到当前活跃事务、正在等待的锁信息,用于快速排查阻塞源头。
information_schema 属于“轻量级”视图,查询消耗较低,但在高并发时频繁访问会导致元数据锁竞争,不建议在繁忙时段大量轮询。
11.5.2 performance_schema:运行时性能的“仪表盘”
如果说 information_schema 是一本数据库的“说明书”,performance_schema 则是它的 “实时监控仪表盘”。它通过一系列底层探针(instrument)记录事件,比如每一条 SQL 的耗时、每一步 I/O 等待的时间、每一个互斥锁的争用次数等。这些数据会被聚合到丰富的表中,是定位性能问题的终极手段。
开启情况和检查:
-- 查看是否启用
SHOW VARIABLES LIKE 'performance_schema';
-- 查看当前启用了哪些消费者(consumer)
SELECT * FROM performance_schema.setup_consumers;
MySQL 8.0 默认启用,且会启用常见的消费者,但早期版本或某些云环境可能关闭。性能开销通常低于 5%,但若开启所有 Instrument 和 Consumer,高负载下会有微小影响,一般保持默认足够。
最核心的三张表:
events_statements_summary_by_digest
这是排查“哪条 SQL 最费时/执行次数最多”的神器。它按 SQL 摘要(去除常量值后的标准化 SQL 文本)聚合统计。
SELECT
DIGEST_TEXT,
COUNT_STAR AS `执行次数`,
ROUND(SUM_TIMER_WAIT/1000000000000, 2) AS `总耗时(s)`,
ROUND(AVG_TIMER_WAIT/1000000000000, 4) AS `平均耗时(s)`,
ROUND(SUM_LOCK_TIME/1000000000000, 2) AS `锁等待总耗时(s)`,
ROUND(SUM_ROWS_EXAMINED/COUNT_STAR, 0) AS `平均扫描行数`,
ROUND(SUM_ROWS_SENT/COUNT_STAR, 0) AS `平均返回行数`,
SUM_SELECT_FULL_JOIN AS `全表联接次数`
FROM performance_schema.events_statements_summary_by_digest
WHERE SCHEMA_NAME = 'your_db'
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;
重点关注 平均扫描行数远大于返回行数 的语句(可能缺少好索引),以及 锁等待总耗时 很高的语句(事务期间锁持有过长或死锁前兆)。
table_io_waits_summary_by_table
统计每张表的 I/O 等待(读/写次数、延时),精准定位哪个表最“热”。
SELECT
OBJECT_SCHEMA, OBJECT_NAME,
COUNT_READ, COUNT_WRITE,
ROUND(SUM_TIMER_READ/1000000000000, 2) AS `读操作总耗时(s)`,
ROUND(SUM_TIMER_WRITE/1000000000000, 2) AS `写操作总耗时(s)`
FROM performance_schema.table_io_waits_summary_by_table
WHERE OBJECT_SCHEMA NOT IN ('mysql', 'sys', 'performance_schema', 'information_schema')
ORDER BY (SUM_TIMER_READ + SUM_TIMER_WRITE) DESC
LIMIT 10;
如果某张表的读操作延时异常高,配合慢日志和 EXPLAIN,往往能发现缺失索引或较差执行计划。
events_waits_current/events_waits_summary_global_by_event_name
当前等待和汇总等待事件,可用来分析锁等待、I/O 等待、网络等待等。例如排查全局锁等待热点:
SELECT EVENT_NAME, COUNT_STAR, SUM_TIMER_WAIT
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE EVENT_NAME LIKE '%lock%'
ORDER BY SUM_TIMER_WAIT DESC
LIMIT 10;
对于锁等待和死锁,结合 data_locks 和 data_lock_waits(8.0 新增)可以直接看到哪条 SQL 正在被哪条 SQL 阻塞,比 5.7 的 INNODB_LOCK_WAITS 直观很多。
11.5.3 sys 库:开箱即用的 DBA 诊断包
sys 库是在 performance_schema 和 information_schema 之上构建的一套视图、函数和存储过程,从 MySQL 5.7 开始内置。它的核心设计思想是:把复杂的底层性能数据包装成人类易读的报表,让你不需要记住各种 JOIN 和换算公式。
常用视图示例:
- 找出全表扫描最严重的表
SELECT * FROM sys.schema_tables_with_full_table_scans;
该视图列出每个库表上发生的全表扫描次数,可以直接作为添加索引的待办清单。
- 查找未使用的索引(谨慎使用)
SELECT * FROM sys.schema_unused_indexes;
这里列出的索引是自性能模式启动以来从未被任何查询使用过的索引。但需极其谨慎:如果某个索引只是偶尔使用,或在备份/归档脚本中使用,直接删除可能造成灾难。建议先设成“不可见索引”(ALTER TABLE ... ALTER INDEX idx_name INVISIBLE)观察一段时间,再决定是否物理删除。
- 查看 95% 分位数最慢的语句
SELECT * FROM sys.statements_with_runtimes_in_95th_percentile
WHERE db = 'your_db'
LIMIT 10;
它直接显示哪些 SQL 的响应时间在最差的 5% 范围,是优化优先级的直观指引。
- 主机资源汇总 –
host_summary
SELECT * FROM sys.host_summary ORDER BY statement_latency DESC;
显示按客户端主机分组的连接数、语句执行次数和延迟,快速发现异常请求来源。
- 用户资源消耗
SELECT * FROM sys.user_summary ORDER BY statement_latency DESC;
- 等待事件排行
SELECT * FROM sys.wait_classes_global_by_avg_latency;
除了视图,sys 库还提供诊断函数,例如 sys.format_statement() 可以将 performance_schema 中的长 SQL 文本截断为可读格式;sys.ps_thread_id() 可以将 PROCESSLIST ID 转换为 performance_schema 的线程 ID。
11.5.4 三者协作与使用边界
在实际性能诊断中,这三者并不是相互替代的关系,而是不同层级的数据源:
| 库名 | 主要提供 | 数据时效 | 性能影响 | 使用门槛 |
|------|---------|----------|---------|--------|
| information_schema | 元数据、统计信息 | 近似实时 | 低 | 低 |
| performance_schema | 运行时事件、等待与资源消耗 | 实时(内存表) | 中等,可通过参数控制 | 高(需要了解表结构和事件时序) |
| sys | 封装好的诊断报表 | 基于前两者,略有延迟 | 极低(只读视图) | 低,开箱即用 |
实战心法:
- 日常巡检推荐用
sys库的视图快速扫描异常,尤其是schema_unused_indexes和慢语句类视图。 - 深入分析某个具体性能瓶颈时,直接查询
performance_schema进行自定义聚合,获取更细粒度的指标。 information_schema则主要用于元数据查询、容量评估和索引基数预判,记住它的数据并非百分之百精确,但足以指导决策。- 在配置
performance_schema时,要根据服务器负载调整performance_schema_max_digest_length和消费者的启用状态。如果确实资源紧张,可暂时关闭部分consumer以减少开销,但不建议直接关闭整个performance_schema,因为它对排查问题太重要了。 - 不要在生产环境反复对
sys或performance_schema的表执行SELECT *大查询,尤其是在高负载下。可适当加上LIMIT或时间范围过滤。
掌握了这三个库,你就像拥有了 X 光机、动态心电图和专家诊断报告,性能优化再也无须“盲猜”。它们是 EXPLAIN 之后,每个对性能负责的开发者都该放在工具箱里的利器。