%util不准,因只统计驱动层排队时间,不计硬件处理时间;应重点关注r/s、w/s、rkb/s、wkb/s、await(机械盘>10ms/nvme>1ms告警)及队列堆积情况。

怎么看 iostat 里 %util 到底准不准
它经常被当成“磁盘忙不忙”的唯一指标,但其实是个误导性很强的数字。Linux 内核只在设备驱动层有请求排队时才计时,一旦 I/O 被下发到硬件(比如 NVMe 控制器),%util 就停止计数了。SSD 或带缓存的 RAID 卡上,%util 常常卡在 100%,但实际延迟很低、吞吐很高。
真正该盯的是:r/s 和 w/s(每秒请求数)、rkB/s 和 wkB/s(吞吐量)、await(平均等待+服务时间)和 svctm(已废弃,别信)。用 iostat -x 1 每秒刷新一次,重点看 await 是否持续 > 10ms(机械盘)或 > 1ms(NVMe)。
- 别用
iostat -c(CPU 模式),它不显示磁盘细节 -
iostat默认单位是 KB,不是字节;加-k或-m显式指定更安全 - 如果
await >> svctm,说明队列堆积严重,瓶颈在调度或并发过高
为什么 iotop 看不到某些进程的磁盘读写
因为 iotop 依赖内核的 /proc/PID/io 接口,而这个接口只统计同步 I/O(read/write 系统调用),对异步 I/O(如 io_uring、libaio)、内存映射写(mmap + msync)、或者直接绕过页缓存的 O_DIRECT 操作,统计会严重偏低甚至为零。
典型场景:数据库(PostgreSQL、MySQL InnoDB)、Elasticsearch、Kafka 日志写入,都可能大量使用这些模式。
- 确认是否用了
O_DIRECT:查进程打开的文件标志,cat /proc/PID/fdinfo/FD | grep flags,看有没有0100000 - 用
perf record -e block:block_rq_issue -a sleep 10抓底层块层请求,再perf script查来源 -
iotop -o只显示有 I/O 的进程,但依然漏掉异步路径;别全信它的“TOP”排序
blktrace 抓出来的 trace 文件怎么快速定位慢请求
它输出的是原始事件流(Q/G/M/I/D/U等字母代表不同阶段),直接 human-read 几乎不可能。必须配合 blkparse 解析,再用 seekwatcher 或自定义脚本聚合分析。
最实用的速查方式:用 blkparse -i trace.bin -f "%5T.%9t %5p %2a %3d %8s %4n %12C\n" | awk '$3=="Q" {q[$2]=$1} $3=="D" && $2 in q {print $2, $1-q[$2], $4, $5}' | sort -k2 -nr | head -10,提取每个请求从 Q(入队)到 D(下发)的延迟,找最大值。
-
blktrace开销大,别在生产环境长开;单次抓10~30秒足够 - 设备名要写对,
blktrace -d /dev/nvme0n1,不能写成/dev/nvme0n1p1(分区无效) - 生成的 trace 文件默认无后缀,别误删;
blkparse必须用同名.bin和.log配对解析
用 fio 模拟业务 I/O 时,哪些参数最容易配错
很多人照抄网上命令跑 fio --name=randread --ioengine=libaio --rw=randread,结果压不出真实负载。关键在 bs、iodepth、numjobs 三者关系没理清,导致要么压不起来,要么把队列打爆却没测出瓶颈点。
例如:MySQL 随机读通常是 16k 块大小,iodepth=4~8(InnoDB 的 innodb_read_io_threads),numjobs=1 就够;而对象存储上传则要用 bs=1M + iodepth=128 + numjobs=4 才能模拟多连接并发。
-
direct=1必须开,否则走 page cache,测的是内存不是磁盘 -
runtime和time_based要配对用,不然可能只跑几秒就停 - 测试前用
echo 3 > /proc/sys/vm/drop_caches清缓存,避免干扰
复杂点在于:I/O 模式(顺序/随机)、访问粒度(4k/64k/1M)、队列深度、线程数,这四个维度得按真实 workload 对齐。配错一个,结果就和线上行为脱节。











