先用 iostat -x 1 看系统层真实 io 压力:重点关注 r_await/w_await(>10ms 表示磁盘响应慢)、aqu-sz(>4 显著排队)、r/s 或 w/s(超磁盘标称 iops 即达物理极限);%util 在 ssd/raid 上失效,勿轻信。

怎么用 iostat 快速确认 MySQL 是不是真在拖慢磁盘
别一上来就查 MySQL 内部,先看系统层有没有真实 IO 压力。运行 iostat -x 1,重点盯三列:
-
r_await和w_await:持续 >10ms 就说明单次读写响应变慢,不是 CPU 卡住,是磁盘在等 -
aqu-sz(平均队列深度):>2 表示请求已在排队,SSD 上这个值常被低估,但 >4 几乎肯定有瓶颈 -
r/s或w/s:对比你磁盘标称 IOPS(比如 SATA SSD 约 5k,NVMe 可达 50k+),超了就是物理极限
如果 %util 接近 100% 但 r_await 很低,大概率是监控误导——特别是用了 RAID 或 SSD,%util 已失效,别信它。
sys.schema_table_statistics 能看出哪些表在疯狂读写,但得先开收集
MySQL 自带的 sys 库里,schema_table_statistics 按表维度统计了 io_read_requests、io_write_requests,但它默认不采集——因为要开 performance_schema 的对应 instruments。
- 先确认开关开着:
SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE 'events_waits_history_long';,确保wait/io/table/sql/handler是ENABLED - 查热点表:
SELECT table_schema, table_name, io_read_requests + io_write_requests AS total_io FROM sys.schema_table_statistics ORDER BY total_io DESC LIMIT 10; - 注意:这个统计不含索引页读写,只算“用户表数据页”层面,所以高 IO 不一定对应慢查询,可能是大字段
SELECT *或频繁UPDATE大文本列
pt-ioprofile 抓的是 mysqld 进程真实发给内核的 IO 请求
和 iostat 看设备层、sys 看逻辑表不同,pt-ioprofile 用 strace 跟踪 mysqld 进程的 pread()/pwrite() 系统调用,能定位到具体文件和 offset。适合排查“为什么 buffer pool 足够大,还是天天刷盘”这类问题。
- 基本用法:
pt-ioprofile --pid $(pgrep mysqld) --cell=bytes --run-time=30 - 关键输出字段:
filename(常见如/var/lib/mysql/xxx.ibd或ib_logfile0)、total_bytes(读写总量)、count(IO 次数) - 典型线索:如果
ib_logfile*占比突增,说明写压力大,可能innodb_log_file_size太小或事务太重;如果某张.ibd文件 IO 特别高,再结合sys.schema_table_statistics对应上表,去查它的查询模式
别漏掉 InnoDB Buffer Pool 命中率这个隐性 IO 开关
很多人的 IO 高,根本不是 SQL 写得差,而是 innodb_buffer_pool_size 没设够,导致每条查询都在反复刷磁盘。这不是工具能直接报错的点,得自己算。
- 查当前命中率:
SHOW GLOBAL STATUS LIKE 'Innodb_buffer_pool_read%';,计算(Innodb_buffer_pool_read_requests - Innodb_buffer_pool_reads) / Innodb_buffer_pool_read_requests - 安全线是 >99.5%,低于 99% 就该加内存了——注意:加完要重启或在线调整(5.7+ 支持
SET GLOBAL innodb_buffer_pool_size = N),但不能超过物理内存的 70% - 容易忽略的坑:buffer pool 里缓存的是「页」,不是「行」。如果查询总跨页(比如
ORDER BY+LIMIT跳过大量页),即使命中 buffer pool,也可能触发大量随机 IO,这时得靠联合索引优化,而不是堆内存











