确认磁盘IO是瓶颈需满足:MySQL iowait持续>10%且iostat显示某盘%util≥95%、await>10ms(SSD应<1ms);缓冲池命中率<95%、Innodb_data_reads突增、iotop显示mysqld刷写、INNODB STATUS有大量pending reads、Created_tmp_disk_tables持续上涨。

怎么确认磁盘 IO 真是瓶颈?
别一慢就猜 IO,得用数据说话。最直接的信号是:MySQL 进程的 iowait 持续高于 10%,同时系统级 iostat -x 1 显示某块盘 %util 长期 ≥95%、await 超过 10ms(SSD 应该在 1ms 内)。这时再查 MySQL 内部指标:Innodb_buffer_pool_reads / Innodb_buffer_pool_read_requests 算出的缓冲池命中率若低于 95%,说明大量请求被迫落盘;Innodb_data_reads 每秒突增几千甚至上万,基本坐实物理读爆炸。
-
iotop -o能立刻看到是不是mysqld在刷写,而不是备份或日志轮转抢了 IO -
SHOW ENGINE INNODB STATUS\G里 “FILE I/O” 段若出现大量pending reads,代表后台线程已跟不上请求节奏 -
Created_tmp_disk_tables持续上涨,说明排序/分组被迫写磁盘,不是内存能扛住的
哪些 SQL 最容易把磁盘 IO 打爆?
不是慢查询才危险,而是“扫得多、写得勤、临时多”的语句——它们不一定会超 long_query_time,但每秒都在制造随机 IO。
- 全表扫描(
EXPLAIN中type=ALL)且rows> 10 万:哪怕只执行一次,也可能触发数 GB 物理读 -
ORDER BY+LIMIT深度分页(如LIMIT 100000,20):MySQL 得先扫完前 10 万行再取 20 行,中间全走磁盘 -
GROUP BY或DISTINCT大结果集:内存不够时自动写/tmp,IO 峰值常被忽略 - 批量
INSERT/UPDATE未分批:一次塞 10 万行,会密集刷 redo log 和脏页,IO 曲线像心电图
开启 log_queries_not_using_indexes = ON,比只依赖慢日志更能揪出这类“不慢但很脏”的语句。
配置和硬件上怎么快速止损?
优化 SQL 是治本,但业务压过来时得先稳住 IO。优先动这几项:
- 把
innodb_buffer_pool_size设为物理内存的 70%~80%:不够大,热数据留不住,等于天天重读磁盘 - 将
innodb_flush_log_at_trx_commit从 1 改成 2:单机可靠性可接受时,能砍掉约 70% 的 redo log 同步 IO(注意:崩溃可能丢 1 秒事务) - 把
redo log文件(ib_logfile*)和binlog单独放到 SSD 分区,别和数据文件挤在一起 - 检查
innodb_log_file_size:太小(如默认 48MB)会导致频繁 checkpoint,产生大量脏页刷盘;建议总和设为 1~2GB(需停库调整)
机械硬盘还没换 SSD?至少把 undo tablespace 和 temp tablespace 移到更快的盘上——它们是 IO 黑洞高发区。
为什么加了索引,IO 还没降下来?
索引不是万能解药。常见失效场景:
- 查询条件用了函数:
WHERE DATE(create_time) = '2026-01-01'→ 索引失效,必须改成create_time BETWEEN '2026-01-01' AND '2026-01-01 23:59:59' - 复合索引顺序错:
INDEX(a,b)支持WHERE a=1 AND b=2,但不支持WHERE b=2单独查询 - 返回字段太多:
SELECT *导致回表次数暴增,尤其当text/blob字段存在时,每次回表都可能触发额外 IO - 统计信息过期:
ANALYZE TABLE没跑过,优化器误判走索引比全表扫描还贵,干脆放弃
用 EXPLAIN FORMAT=JSON 看 used_columns 和 key_length,比光看 key 字段更准。
真正卡住的地方,往往不在最显眼的慢查询里,而在那些每秒执行几百次、每次扫几万行、又没进慢日志的“温吞水”SQL——它们安静地把 IO 吞噬殆尽。










