show profile 的 io 数据不准且已废弃,应改用 performance_schema 查真实磁盘 io;需启用等待事件采集,筛选 wait/io/file/innodb/ 相关事件,结合状态变量与 os 工具综合分析。

MySQL 的 SHOW PROFILE 确实能查单条语句的 IO 消耗,但默认关闭、仅对当前会话有效、且在 8.0 中已被标记为废弃——真要定位高 IO SQL,别只靠它。
如何开启并读取 SHOW PROFILE 的 IO 数据
执行前必须先打开 profiling 开关,且只能看到最近执行的语句(最多 15 条,默认):
-
SET profiling = 1;—— 启用当前会话的 profiling(注意:全局变量profiling已被移除,不能设GLOBAL) - 执行目标 SQL,比如
SELECT * FROM orders WHERE created_at > '2026-04-01'; -
SHOW PROFILES;—— 查出刚执行语句的Query_ID(如 1) -
SHOW PROFILE BLOCK IO FOR QUERY 1;—— 关键:必须显式指定BLOCK IO,否则不显示磁盘读写次数;输出中Block_ops列是系统调用次数,Block_gets和Block_writes才是真实 IO 次数
⚠️ 注意:Block_gets 不等于“从磁盘读了多少页”——它统计的是 read() 系统调用次数,哪怕数据已在 page cache 里,也会计数;真正反映物理读盘的是 performance_schema.events_waits_history_long 中匹配 wait/io/file/innodb/ 的事件行数。
为什么 SHOW PROFILE 的 IO 值经常不准或为空
它依赖 MySQL 内部采样,而 IO 成本实际发生在存储引擎层(尤其是 InnoDB),SHOW PROFILE 拿不到底层文件等待细节:
- 事务提交时刷 redo log 的
fsync不计入Block_writes,但占大量磁盘带宽 -
Handler_read_rnd_next高 ≠ 磁盘 IO 高,它只是“按指针取下一行”,取的可能是 Buffer Pool 里缓存好的页 - 如果 SQL 触发了临时表(
Creating tmp table)或排序(Sorting result),IO 会发生在磁盘临时目录(tmpdir),这部分也不归入Block_*统计 - MySQL 8.0.22+ 默认禁用 profiling,
SHOW PROFILE返回空结果是常态,不是你没开对
替代方案:用 performance_schema 查真实磁盘 IO
这才是定位物理读盘的可靠路径,尤其适合事后回溯或压测复盘:
- 确认已启用:
SELECT @@performance_schema;必须返回 1 - 打开等待事件采集:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('events_waits_current', 'events_waits_history_long'); - 执行目标 SQL 后,立刻查它的线程等待:
SELECT EVENT_NAME, TIMER_WAIT/1000000000 AS ms FROM performance_schema.events_waits_history_long WHERE THREAD_ID = (SELECT THREAD_ID FROM performance_schema.threads WHERE PROCESSLIST_ID = CONNECTION_ID()) AND EVENT_NAME LIKE 'wait/io/file/innodb/%' ORDER BY TIMER_START DESC LIMIT 10; - 重点关注
wait/io/file/innodb/innodb_data_file(表空间读)和wait/io/file/innodb/log_file(redo log 写)两类事件;每行代表一次系统调用,即至少一次 16KB 物理读或写
这个方法绕过了 SQL 解析层,直接抓内核级 IO 行为,不会漏掉刷脏页、预读、log flush 这些关键动作。
容易被忽略的 IO 隐藏点
很多高 IO 并不来自慢查询本身,而是由配置或模式引发的隐性开销:
-
innodb_flush_log_at_trx_commit = 1时,每个小事务都强制fsyncredo log,IO 峰值可能飙到 200MB/s;设为 2 可降 10 倍,但需接受宕机丢秒级数据 -
innodb_buffer_pool_size小于热数据总量时,Innodb_buffer_pool_reads会持续上涨,但Handler_read_*可能很低——说明 SQL 很简单,但每页都要单独拉盘 - 使用
SELECT *+ 大字段(如TEXT、BLOB)会触发额外的“大字段离页读”,这类 IO 不体现在主索引扫描的等待事件里,得查wait/io/file/innodb/innodb_undo_file或innodb_data_file的二级读
真要压准 IO 瓶颈,得把 performance_schema 的等待事件、状态变量(Innodb_buffer_pool_reads、Innodb_data_reads)、以及 OS 层的 iotop -p $(pidof mysqld) 三者对照着看——单靠一个 SHOW PROFILE,连门都没摸到。











