判断io瓶颈应关注await和avgqu-sz而非%util:await持续>10ms(ssd)或>20ms(hdd)且avgqu-sz>2表明磁盘响应慢;结合iotop、innodb status、刷盘参数及sys schema定位真实原因。

看 iostat 的 %util 和 await 是不是真瓶颈
别一看到 %util 接近 100% 就断定是 MySQL 问题——它在 SSD 或 RAID 上基本没参考价值,尤其云盘或 NVMe 设备。真正该盯的是 await(平均 IO 等待毫秒)和 avgqu-sz(平均队列深度)。如果 await 持续 > 10ms(SSD)或 > 20ms(HDD),且 avgqu-sz > 2,说明磁盘响应变慢,IO 请求在排队。
执行:iostat -xdk 1,重点观察 MySQL 数据目录所在设备(比如 /dev/nvme0n1p1),而不是笼统看 dm-0 或 loop 设备。
- 若
r/s和w/s高但rMB/s/wMB/s很低,可能是小 IO(如随机读页、redo 写),不是吞吐瓶颈而是 IOPS 瓶颈 - 若
rrqm/s或wrqm/s占比高(>30%),说明内核做了大量 IO 合并,底层设备可能已饱和 -
iotop -o必须同步跑,确认mysqld进程是否真是 IO 主力,还是被其他进程(如 backup、logrotate)拖累
查 SHOW ENGINE INNODB STATUS 里的 pending IO 和 buffer pool 命中率
登录 MySQL 执行:SHOW ENGINE INNODB STATUS\G,跳转到 FILE I/O 和 BUFFER POOL AND MEMORY 部分。
Pending normal aio reads/writes 如果长期 > 0(尤其是写),说明 InnoDB 提交的 IO 请求没被系统及时处理;Buffer pool hit rate 若低于 95%,意味着大量物理读——这是最常见原因。
- 命中率低 ≠ 缓冲池小,也可能是查询扫描范围太大(如全表扫描、大范围 range 查询),导致热数据被挤出
-
Pages made young和not young比例失衡(前者远小于后者),说明 LRU 链表老化过快,缓冲池实际有效容量打折扣 - 注意
Modified db pages数量:若持续很高(比如 > 10 万),且pending writes不为 0,说明刷脏压力大,innodb_io_capacity可能设得太保守
确认 innodb_flush_log_at_trx_commit 和 sync_binlog 是否在从库误配
从库磁盘 IO 高,80% 是因为把主库配置原样搬过去:innodb_flush_log_at_trx_commit=1 + sync_binlog=1,等于每条事务刷两次盘(redo + binlog),完全没必要。
先查当前值:SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit'; 和 SHOW VARIABLES LIKE 'sync_binlog';,再确认 log_slave_updates 是否开启:
- 没开
log_slave_updates:从库可安全设innodb_flush_log_at_trx_commit = 2,sync_binlog = 0 - 开了
log_slave_updates:保留sync_binlog = 1或设为10,但innodb_flush_log_at_trx_commit仍建议用2 -
sync_relay_log默认是1,从库回放 relay log 时每事务刷一次盘,应改为10000减少刷盘频次
用 sys schema 定位高 IO SQL,别只盯着慢查询日志
慢查询日志只记录执行时间长的语句,但很多高 IO 语句执行很快(比如 SELECT COUNT(*) FROM huge_table),却扫了上千万行——这类语句不会进慢日志,但会拉垮 IO。
直接查 sys.statement_analysis 或更精准的 sys.statements_with_full_table_scans:
SELECT digest_text, rows_examined, rows_sent, avg_timer_wait FROM sys.statements_with_full_table_scans WHERE rows_examined > 100000 ORDER BY rows_examined DESC LIMIT 5;
- 关注
rows_examined而非exec_count:单次扫描百万行,比一百次各扫千行更伤 IO -
digest_text中带GROUP BY、ORDER BY、DISTINCT的语句,容易触发临时表(Created_tmp_disk_tables计数器飙升),产生额外文件 IO - 配合
performance_schema.file_summary_by_event_name查哪个文件(如ibdata1、ib_logfile0)被频繁读写,再反推是 undo、redo 还是数据页问题
真正卡住 IO 的,往往不是某一条 SQL 或一个参数,而是多个因素叠加:缓冲池刚够用但查询突然变宽、从库刷盘策略没调、同时又碰上 OS 层面 noatime 没挂载——排查时得一层层剥,别指望改一个值就立竿见影。











