explain format=json 中的 disk_reads 字段在 mysql 8.0.22+ 中直接预估物理读页数,是优化器基于统计信息计算出的 i/o 量,数值大于 100 即需警惕全表或全索引扫描风险。

EXPLAIN FORMAT=JSON 里的 disk_reads 字段最直接
MySQL 8.0.22+ 版本中,EXPLAIN FORMAT=JSON 会返回预估的物理读页数,字段名就是 disk_reads。它不是猜测,而是优化器基于统计信息和访问路径计算出的「预计磁盘 I/O 量」,比 Rows_examined 更贴近真实 IO 压力。
执行示例:
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 'pending' AND created_at > '2026-08-01';
在输出 JSON 的
"execution_plan" 节点下找 "disk_reads" 值。若显示 "disk_reads": 12400,基本可判定该语句大概率触发全表/全索引扫描,而非靠缓存扛住。注意:disk_reads 是预估值,不等于实际发生值;但只要没用到覆盖索引、又没走主键等值查找,数值 > 100 就值得警惕。
慢日志里必须开 log_slow_extra=ON 才看得到真实读页数
默认慢日志只记 Query_time 和 Lock_time,完全不反映 I/O 强度。要看到真实物理读行为,必须开启 log_slow_extra=ON(MySQL 8.0.26+ 支持)。
开启后,每条慢日志行会多出这些关键字段:
- InnoDB_IO_r_ops:实际发生的 InnoDB 层读页次数
- InnoDB_IO_r_bytes:对应读取的字节数
- InnoDB_pages_distinct:去重后的页面数,用于判断缓存局部性
常见误判场景:
- Query_time=0.15s 但 InnoDB_IO_r_ops=8900 → 不是“快”,是“短平快地扫了磁盘”
- InnoDB_pages_distinct=42 而 InnoDB_IO_r_ops=7600 → 同一批页面被反复读,说明 innodb_buffer_pool_size 过小或查询范围过大
Performance Schema 查 SUM_DISK_READS 定位高频小查询
很多吃 I/O 的 SQL 并不慢——比如每秒执行 200 次的 SELECT COUNT(*) FROM user_session WHERE expired_at ,单次 <code>Query_time 只有 0.003s,根本进不了慢日志,但 SUM_DISK_READS 累积起来可能高达每秒上万次物理读。
查法:
SELECT DIGEST_TEXT, SUM_TIMER_WAIT, SUM_ROWS_EXAMINED, SUM_DISK_READS<br>FROM performance_schema.events_statements_summary_by_digest<br>WHERE SUM_DISK_READS > 1000<br>ORDER BY SUM_DISK_READS DESC<br>LIMIT 10;
要点:
- SUM_DISK_READS 是自上次清零以来的累计值,需配合 RESET 或定时采集对比
- 优先筛 DIGEST_TEXT 相同但 SUM_ROWS_EXAMINED 高的语句,大概率缺索引或没走对索引
- 若某语句 SUM_DISK_READS 高但 SUM_ROWS_EXAMINED 很低,可能是频繁读小结果集却无法缓存(如临时表、未命中 buffer pool)
SHOW ENGINE INNODB STATUS 里的 FILE I/O 段是系统级验证入口
当上面三招都指向某类查询有 I/O 问题,但不确定是不是 MySQL 在“背锅”,就进 SHOW ENGINE INNODB STATUS\G 看 FILE I/O 小节:
重点关注:
- pending normal aio reads/writes:非零且持续不归零 → 底层 I/O 已排队,不是 SQL 慢,是设备或内核调度跟不上
- os file reads 数值随时间是否突增:配合业务操作(如导入、报表)看是否同步跳升
- Innodb_buffer_pool_read_requests 和 Innodb_buffer_pool_reads 的比值:前者高 + 后者也高 → 缓冲池没拦住多少请求,得看是不是 innodb_buffer_pool_size 设置过低,或者查询本身无法利用缓存(如大范围 ORDER BY)
容易忽略的一点:FILE I/O 段里的计数是全局累加的,不能直接换算成“某条 SQL 贡献了多少”,但它能告诉你当前 I/O 压力是否真的来自用户查询,还是来自刷脏页、写 redo 等后台动作。











