Linux 性能问题是 CPU 还是 IO?

6次阅读

%wa 高表明IO等待严重,需结合iostat -x 1看await和%util,用pidstat -u -d 1定位高IO进程,dd oflag=direct绕过缓存测真实性能,深层瓶颈可能在文件系统或硬件层。

Linux 性能问题是 CPU 还是 IO?

top 看 CPU 和 IO 等待的实时分布

直接运行 top,重点关注右上角的三行负载指标:%Cpu(s)%wa(即 IO wait)。如果 %wa 长期高于 20%,说明内核正在大量等待磁盘或网络 IO 完成,此时 CPU 利用率可能不高,但系统响应慢——这不是 CPU 不够,而是 IO 成了瓶颈。

注意:%wa 是“等待 IO 的 CPU 时间占比”,不是“IO 设备忙的比例”。它反映的是有进程在就绪队列里干等 IO 返回,而不是 CPU 空转。所以即使 %Cpu(s) 显示 30% idle,只要 %wa 高,就别急着加 CPU。

  • top 默认不显示线程模式,按 H 可切换,排查单个高 IO 进程时有用
  • 某些虚拟化环境(如 KVM)中 %wa 可能被虚报,需结合 iostat -x 1 交叉验证
  • top 的采样间隔默认是 3 秒,对瞬态 IO 尖峰不敏感,可按 s 键临时调到 0.5 秒

iostat -x 1 定位具体设备和延迟

iostat -x 1 每秒输出一次扩展统计,关键看三列:%utilawaitsvctm(已弃用,忽略)。

%util 接近 100% 表示设备几乎一直在处理请求,但要注意:SSD 的并行能力远高于 HDD,%util 高不一定等于瓶颈;而 await(平均每次 IO 的等待+服务时间)超过 10ms(HDD)或 1ms(SSD)才真正可疑。

  • await 高但 %util 很低,可能是应用发了太多小随机 IO,或存在锁竞争(如 ext4 journal 争抢)
  • r/sw/s 突增但 rkB/s/wkB/s 不高,大概率是小 IO 导致的上下文切换/中断开销问题
  • NVMe 设备会显示多个队列(如 nvme0n1p1),别只盯着 nvme0n1 看总值

区分 CPU 密集型 vs IO 密集型进程:用 pidstat -u -d 1

pidstat -u -d 1 同时输出每个进程的 CPU 使用率(%CPU)和 IO 读写速率(kB_rd/skB_wr/s),比单独看 topiotop 更直观判断“谁在拖慢系统”。

典型误判场景:一个 java 进程 %CPU 占 80%,但同时 kB_wr/s 达 50MB/s —— 它可能一边做计算一边刷日志,单纯降 CPU 优先级没用,得查日志落盘策略。

  • -t 参数可显示线程级数据,适合排查 GC 日志刷盘或 Netty flush 线程阻塞
  • 注意单位:默认是 kB,不是 KB;遇到大数值时可用 -h 开启人类可读格式
  • 如果某进程 %CPU 低但 Command 列显示 D(uninterruptible sleep),基本可断定它卡在 IO 路径里(如 NFS hang、坏盘重试)

绕过缓存测真实 IO 性能:用 dd with oflag=direct

很多“IO 慢”其实是 page cache 在捣鬼。用 dd if=/dev/zero of=testfile bs=1M count=1024 oflag=direct 绕过缓存直写磁盘,才能测出设备真实吞吐。对比 oflag=sync(同步写)和 oflag=direct 的耗时差异,还能判断存储是否支持高效异步 IO。

  • oflag=direct 要求 bs 是文件系统逻辑块大小的整数倍(通常是 4K),否则会 fallback 到缓存模式且不报错
  • 测试前用 echo 3 > /proc/sys/vm/drop_caches 清缓存,避免上次测试污染结果
  • SSD 测试要预填充(preconditioning),空盘和满盘性能可能差 5 倍以上;生产环境更应关注 fio --name=randwrite --ioengine=libaio --direct=1 这类混合负载模拟

真正难的不是分清 CPU 还是 IO,而是当 %wa 高、iostat 却看不出设备瓶颈时——那往往卡在文件系统层(如 XFS allocation group 锁)、块设备驱动(如 multipath 路径切换)、甚至硬件固件(NVMe controller thermal throttle)。这时候得看 /proc/diskstatsios_pgr 字段,或者用 perf record -e block:block_rq_issue,block:block_rq_complete 抓 IO 生命周期事件

text=ZqhQzanResources