iostat -x -k -d 1 能看到设备级io详细指标(如%util、await、aqu-sz、r/s、w/s、rkb/s、wkb/s等),但不能看到进程级io归属、mysql内部写类型(日志/数据页)、sql语句或innodb脏页刷写来源。

iostat -x -k -d 1 能看到什么,不能看到什么
iostat 是系统级快照工具,它不关联 MySQL 进程、不解析 SQL、也不区分 InnoDB 的日志写还是数据页写。你只能看到设备层的宏观 IO 压力:比如 %util 接近 100%、w_await 持续 >20ms、aqu-sz(平均队列深度)飙升到 5 以上——这些是磁盘响应已吃紧的明确信号。
但以下情况它完全无法判断:
-
svctm字段在现代内核中已失效,必须忽略 - 只看
w/s高,却没结合wkB/s和wareq-sz,就可能误判:比如 4KB 小写密集型负载和 128KB 大块写对 SSD 的压力完全不同 - 看不到请求来源:是
ib_logfile0在狂写?还是ibdata1或某个表空间在刷脏页?还是 mysqldump 正在拷贝?
SHOW ENGINE INNODB STATUS 中 FILE I/O 部分怎么看
执行 SHOW ENGINE INNODB STATUS\G 后,重点盯住 FILE I/O 小节里的这几项:
-
pending normal aio reads/writes:非零且持续不降 → 真实 IO 阻塞已发生,不是假象 -
log i/o's done远高于buffer pool i/o's done→ 日志写压倒性占优,说明事务提交频繁或innodb_flush_log_at_trx_commit=1+sync_binlog=1组合正在起效 -
os file reads/os file writes数值本身不重要,要看它们随时间是否线性增长;突增往往对应大查询或批量导入
如果这里 pending 不为零,同时 Innodb_buffer_pool_wait_free 开始上涨,基本可以确认是脏页刷得太慢,而不是 SQL 慢。
innodb_io_capacity 和 innodb_write_io_threads 怎么配
这两个参数必须协同调,否则一个设高了另一个没跟上,等于白调。
-
innodb_io_capacity应设为磁盘随机写 IOPS 的 50%~75%,不是标称值。例如一块标称 5000 IOPS 的 NVMe,实际稳定可用约 3500,那么设2500更稳妥;设成8000反而因锁竞争导致有效吞吐下降 -
innodb_io_capacity_max建议设为innodb_io_capacity的 2 倍,防止 checkpoint lag 触发紧急刷盘,造成 IO 尖峰 -
innodb_write_io_threads默认是 4,SSD 上可提到 8~12,但必须匹配队列深度:用iostat -x 1观察aqu-sz,如果长期 >2 且w_await升高,说明线程数不够;但如果aqu-sz已经压到 0.3 以下,再加线程无意义,还可能增加上下文切换开销 -
innodb_adaptive_flushing必须保持 ON(默认),关掉后无法根据 redo log 增长速率动态调节刷页节奏,大事务后必出尖峰
innodb_flush_method=O_DIRECT 为什么不能省
这是最容易被跳过的一步,但后果严重。
默认的 fdatasync 模式下,InnoDB 写页会先进 OS page cache,再由内核刷盘,形成 double buffering:既占内存又加重 IO。而 O_DIRECT 绕过 page cache,让 InnoDB 直接与磁盘对话。
- 好处:降低内存压力、避免 cache 污染、减少 writeback 延迟抖动
- 前提:文件系统必须支持(XFS/ext4 都行),且存储设备要有断电保护(BBU/FBWC 或 UPS),否则禁用 barrier 可能丢数据
- 验证方式:改完重启 mysqld,再跑
iostat -x 1,观察wkB/s是否更平稳、w_await波动是否收窄
真正卡在 IO 的时候,很多人反复调 buffer pool 或索引,却忘了这行配置——它不解决逻辑问题,但决定了底层 IO 路径是否干净。











