iostat 命令详解与参数实测(Ubuntu 22.04)

iostat 逐参数实测详解:默认两段输出逐列解读、「自开机以来」语义与间隔采样差异、-x 扩展列(r/s、w_await、aqu-sz、%util)、-d/-c/-k/-m/-t/-y/-p/-z 参数与 dd 直写 2GB 压力实证,Ubuntu 22.04(sysstat 12.5.2)全部回显实测通过。

Linux命令Linux系统UbuntuUbuntu 22.04命令行系统信息2026/10/15

iostat 命令详解与参数实测(Ubuntu 22.04)

目标:全面掌握 iostat 的每一个常用参数——默认两段输出(avg-cpu 与 Device)逐列解读、「自开机以来」语义与间隔采样差异、-x 扩展统计(r/s、w_await、aqu-sz、%util 等)、-d/-c/-k/-m/-t/-y/-p/-z,并用 dd 直写 2GB 做 IO 压力实证,全部命令与回显已在 Ubuntu 22.04(sysstat 12.5.2)实测通过。

一、环境信息

  • 操作系统:Ubuntu 22.04.5 LTS(x86_64)
  • 内核:6.8.0-138-generic(iostat 报告头首行可见)
  • iostat 版本:sysstat 12.5.2(iostat -V 实测)
  • CPU:12 核(报告头 (12 CPU))
  • 磁盘:系统盘 sda(800G),含 sda1/sda2/sda3 三个分区,根文件系统位于 sda3 分区之上(iostat -p ALL 实测 sda3 承担了几乎全部读写量);另有一块 sr0 光驱与 18 个 snap 回环设备 loop0~loop17,默认输出会一并列出
  • 演示目录:/root/demo/iostat/(用于存放 dd 压力测试的临时大文件,测完即删)

二、命令概览

2.1 安装(iostat 属于 sysstat 包)

Ubuntu 22.04 默认不预装 iostat,它与 mpstat、sar、pidstat 同属 sysstat 性能工具包,一条命令安装:

$ apt-get install -y sysstat

实测回显(关键段落照抄,安装包 487 kB,解压后约 1.5 MB):

Reading package lists...
Building dependency tree...
Reading state information...
Suggested packages:
  isag
The following NEW packages will be installed:
  sysstat
0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 487 kB of archives.
After this operation, 1507 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu jammy-updates/main amd64 sysstat amd64 12.5.2-2ubuntu0.2 [487 kB]
Fetched 487 kB in 2s (220 kB/s)
Selecting previously unselected package sysstat.
Preparing to unpack .../sysstat_12.5.2-2ubuntu0.2_amd64.deb ...
Unpacking sysstat (12.5.2-2ubuntu0.2) ...
Setting up sysstat (12.5.2-2ubuntu0.2) ...
Creating config file /etc/default/sysstat with new version
update-alternatives: using /usr/bin/sar.sysstat to provide /usr/bin/sar (sar) in auto mode
Created symlink /etc/systemd/system/sysstat.service.wants/sysstat-collect.timer → /lib/systemd/system/sysstat-collect.timer.
Created symlink /etc/systemd/system/sysstat.service.wants/sysstat-summary.timer → /lib/systemd/system/sysstat-summary.timer.
Created symlink /etc/systemd/system/multi-user.target.wants/sysstat.service → /lib/systemd/system/sysstat.service.

安装完成即验证版本与包状态:

$ dpkg -l sysstat | tail -1
ii  sysstat        12.5.2-2ubuntu0.2 amd64        system performance tools for Linux

$ iostat -V
sysstat version 12.5.2
(C) Sebastien Godard (sysstat <at> orange.fr)

💡 说明:安装脚本自动注册了 sysstat-collect.timer 定时器(默认每 10 分钟采集一次系统活动数据写入 /var/log/sysstat/),这是 sar 历史数据功能的开关,对 iostat 手工使用没有影响;若不想要它,把 /etc/default/sysstat 里的 ENABLED="false" 改掉即可。

2.2 语法与测试数据

iostat 的语法是 iostat [ options ] [ <interval> [ <count> ] ]:不给 interval 时只输出一份「自系统启动以来」的累计快照;给了 interval(如 iostat 1 3)则每隔 1 秒输出一份增量报告,共 3 份。设备名可以跟在最后(如 iostat -x sda),不给则列出全部块设备。

本文的 IO 压力演示数据是一个用 dd 直写生成的 2GB 临时文件(oflag=direct 绕过页缓存,保证落盘动作能被 iostat 观察到;实验发现本机写入速度约 600 MB/s,500 MB 不到 1 秒就写完、采样窗口太短,因此加大到 2000 MB):

$ mkdir -p /root/demo/iostat
$ dd if=/dev/zero of=/root/demo/iostat/load.bin bs=1M count=2000 oflag=direct
2000+0 records in
2000+0 records out
2097152000 bytes (2.1 GB, 2.0 GiB) copied, 3.48979 s, 601 MB/s

全部演示结束后用 rm -f /root/demo/iostat/load.bin 删除该文件即可(实测删除后 ls /root/demo/iostat/ 为空,rc=0)。

2.3 --help 全量输出

iostat --help 输出全部参数清单(实测逐行照抄,可作为参数速查):

Usage: iostat [ options ] [ <interval> [ <count> ] ]
Options are:
[ -c ] [ -d ] [ -h ] [ -k | -m ] [ -N ] [ -s ] [ -t ] [ -V ] [ -x ] [ -y ] [ -z ]
[ { -f | +f } <directory> ] [ -j { ID | LABEL | PATH | UUID | ... } ]
[ --dec={ 0 | 1 | 2 } ] [ --human ] [ --pretty ] [ -o JSON ]
[ [ -H ] -g <group_name> ] [ -p [ <device> [,...] | ALL ] ]
[ <device> [...] | ALL ]

💡 说明:iostat 的参数分四类——视角切换(默认全部 / -c 只看 CPU / -d 只看设备)、单位控制(-k 千字节 / -m 兆字节 / -h、--human 人类可读)、报告形态(-x 扩展列、-t 时间戳、-y 省略首份快照、-z 隐藏零活动设备、-p 分区细分)、输出格式(-o JSON、--pretty)。本文第三章逐项实测其中最常用的 9 个。

2.4 版本信息

$ iostat -V
sysstat version 12.5.2
(C) Sebastien Godard (sysstat <at> orange.fr)

sysstat 12.5.2 是 Ubuntu 22.04 官方仓库(jammy-updates)的当前维护版本,包名 sysstat 12.5.2-2ubuntu0.2。

💡 技术拓展:磁盘 IO 观测不止 iostat 一种工具,常见同类工具横向对比——

工具视角特点与适用场景
iostat块设备级吞吐/请求数/延迟/利用率一屏看全,判磁盘瓶颈首选
vmstat系统总览含 CPU wa 列与内存/swap,1 秒粗粒度快速体检
dstat多资源合并插件式架构,一条命令同屏对比磁盘/网络/CPU
sar历史归档依赖 sysstat 定时采集,可回放昨天任何时段
pidstat进程级定位「哪个进程」在打 IO,配合 iostat 使用

本文主角是 iostat:需要判断「磁盘是否成为瓶颈」时,它提供的 %util、await、aqu-sz 设备级指标最直接;进程归属再交给 pidstat,历史回放交给 sar。

三、参数逐项实测

3.1 默认输出:avg-cpu 与 Device 两段

不带任何参数,iostat 输出两段报告:上半段 avg-cpu 是 CPU 使用率,下半段 Device 是块设备 IO。本机默认会列出全部 21 个块设备(18 个 loop 回环设备 + sr0 光驱 + sda 及其分区),为聚焦主盘,下面用 grep -vE "^loop" 隐藏 loop 行(loop 设备处理见 3.9 与五、Q5):

$ iostat | grep -vE "^loop"
Linux 6.8.0-138-generic (f1034b9326b2)  10/15/26        _x86_64_        (12 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          19.63    0.00    4.96    0.77    0.00   74.64

Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda             872.94       490.99     29579.26         0.00   13477689  811949513          0

报告头:内核版本、主机名、采样时刻、架构、CPU 核数。

avg-cpu 段六列:

列含义
%user用户态 CPU 占比
%nice低优先级(nice)用户态占比
%system内核态占比
%iowaitCPU 空闲且有未完成磁盘 IO 的时间占比,IO 瓶颈的关键信号
%steal虚拟化环境下被宿主抢走的时间占比
%idle空闲且无磁盘 IO 等待的占比

Device 段七列:

列含义
tps每秒 I/O 请求数(传输次数)
kB_read/s每秒读取千字节数
kB_wrtn/s每秒写入千字节数
kB_dscd/s每秒丢弃(discard/TRIM)千字节数
kB_read开机以来累计读取千字节
kB_wrtn开机以来累计写入千字节
kB_dscd开机以来累计丢弃千字节

读法示例:上表中 sda 的 kB_wrtn 达 811949513 kB(约 774 GB),kB_wrtn/s 29579 kB/s(约 29 MB/s),说明这是一台写入量不小的机器;kB_dscd 全 0,说明文件系统没启用 discard 策略。同一块盘的三个分区在默认输出里不会出现,需要 -p(见 3.8)。

💡 要点:默认输出的统计口径是「自系统启动以来的平均值」,它适合看总体水位,不适合看「此刻」的状态——观察实时行为必须用间隔采样,见 3.2。

3.2 「自开机以来」语义与间隔采样(iostat 1 3)

iostat 1 3 表示每隔 1 秒输出一份、共输出 3 份。第一份永远是「自开机以来」的累计平均,第二份起才是相邻两次采样之间(实时窗口)的数据——这是 iostat 最容易踩的语义坑:

$ iostat 1 3 | grep -E "avg-cpu|^ +[0-9]|Device|sda "
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          19.09    0.00    4.89    0.75    0.00   75.27
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda             861.39       475.78     29539.04         0.00   13547229  841086325          0
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           1.68    0.00    0.59    0.00    0.00   97.73
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda               3.00         0.00        56.00         0.00          0         56          0
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           1.43    0.00    0.67    0.00    0.00   97.89
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda              18.00         0.00        80.00         0.00          0         80          0

对比三份报告:第一份 %user 19.09、tps 861.39,是开机以来几十天的平均水位;第二、三份 %user 只有 1.68/1.43、tps 3.00/18.00,才是「当下这 1 秒」的真实状态。注意累计列(kB_read/kB_wrtn)只在第一份里有意义,实时窗口里它们表示「该窗口内读写了多少」。

💡 要点:排查「现在」的问题时,直接丢弃第一份报告,或者干脆用 3.7 的 -y 让它不打印。

3.3 -x:扩展统计(r/s、w_await、aqu-sz、%util 等 21 列)

-x 是磁盘排障的主力参数,把 Device 段从 7 列扩展到 21 列,补齐了每秒请求数、请求合并、等待时间、队列深度与利用率。空闲基线(grep 只保留表头与 sda、loop17 两行设备数据,loop17 用于展示 d_await/dareq-sz 等丢弃列的数值):

$ iostat -x | grep -E "avg-cpu|^ +[0-9]|Device|sda |loop17"
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          19.09    0.00    4.89    0.75    0.00   75.27
Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz     d/s     dkB/s   drqm/s  %drqm d_await dareq-sz     f/s f_await  aqu-sz  %util
loop17           0.08      1.38     0.00   0.00    0.07    18.06    0.01      1.67     0.01  37.05    5.39   123.66    0.00     20.71     0.00   0.00    5.89 32770.00    0.01    9.57    0.00   0.01
sda             29.86    475.75     8.82  22.79    1.57    15.93  831.47  29536.97   226.42  21.40    1.25    35.52    0.00      0.00     0.00   0.00    0.00     0.00    0.00    0.00    1.08   8.06

重点列逐一解读:

列含义健康参考
r/s、w/s每秒读/写请求完成数(IOPS)对比盘的规格上限
rkB/s、wkB/s每秒读/写千字节(吞吐)对比盘的带宽上限
rrqm/s、%rrqm读请求被相邻请求合并的比例顺序读多则高
r_await、w_await读/写请求平均耗时(毫秒,含排队+服务)机械盘 <20ms、SSD <5ms 属正常
rareq-sz、wareq-sz平均每次读/写请求大小(kB)越大越顺序,越小越随机
aqu-sz平均队列深度(在途请求数)持续 >1 且 await 上升说明排队
%util设备处理 IO 的时间占比(饱和度)持续接近 100% 需警惕

以 sda 行为例:写入侧 w/s 831.47、wkB/s 29536.97(约 29 MB/s)、w_await 1.25 ms、aqu-sz 1.08、%util 8.06%——写得多但单次请求快、队列浅,盘很空闲。对照 loop17 行:dareq-sz 32770 kB 是一次 discard 请求的块大小,仅作列含义示例。

💡 要点:判断磁盘瓶颈看「三件套」——%util 高 + await 高 + aqu-sz 持续大于 1,三个条件同时满足才指向磁盘饱和;压力下的实测对比见 3.10。

3.4 -d 与 -c:只看设备 / 只看 CPU

两个参数互为反向开关:-d 隐藏 CPU 段只留设备段,-c 隐藏设备段只留 CPU 段:

$ iostat -d | grep -vE "^loop"
Linux 6.8.0-138-generic (f1034b9326b2)  10/15/26        _x86_64_        (12 CPU)

Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda             872.88       490.97     29577.33         0.00   13478045  811956293          0

$ iostat -c
Linux 6.8.0-138-generic (f1034b9326b2)  10/15/26        _x86_64_        (12 CPU)

avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          19.63    0.00    4.96    0.77    0.00   74.64

💡 要点:-d 与 -x 常组合成 -dx(纯设备扩展视图),-c 的价值在于不想多开一个 mpstat 时快速看一眼 %iowait(实测见 3.11)。

3.5 -k 与 -m:单位切换

默认输出的累计量单位就是 kB(速率列带 /s),-k 显式声明、-m 升级为 MB。同一时刻的对比:

$ iostat -k -d | grep -E "Device|sda "
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda             872.88       490.97     29577.33         0.00   13478045  811956293          0

$ iostat -m -d | grep -E "Device|sda "
Device             tps    MB_read/s    MB_wrtn/s    MB_dscd/s    MB_read    MB_wrtn    MB_dscd
sda             872.88         0.48        28.88         0.00      13162     792926          0

同一数据在 -m 下列名变为 MB_*,29577.33 kB/s 显示为 28.88 MB/s(二进制换算 1024 进制),792926 MB 约等于 774 GB,读起来直观得多。

💡 要点:看长期累计量用 -m 避免一长串数字,做精确对比或写脚本解析时用 -k 固定单位;两者与 -x 可自由组合。

3.6 -t:每份报告前打印时间戳

-t 在每份报告头前加一行当前时间(格式 月/日/年 时:分:秒),留档排查时能对齐应用日志的时间线:

$ iostat -t -d -k 1 2 | grep -E "^[0-9]|Device|sda "
10/15/26 14:00:38
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda             869.79       485.67     29647.09         0.00   13522681  825473825          0
10/15/26 14:00:39
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda              99.00         0.00      7964.00         0.00          0       7964          0

两份报告分别盖了 14:00:38 与 14:00:39 的时间戳,第二份显示该 1 秒窗口内 sda 写入 7964 kB。

💡 要点:-t 与 -y 组合(iostat -yt 1 N)可以只留纯实时窗口、每份都带时间,是后台持续采样的标准姿势。

3.7 -y:跳过第一份「自开机以来」快照

给 interval 时第一份报告总是开机以来的累计平均,多数场景它是噪音。-y 让它直接不出现,iostat -y 1 3 从第一秒起就输出实时窗口:

$ iostat -y 1 3 | grep -E "avg-cpu|^ +[0-9]|Device|sda "
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           1.60    0.00    1.01    0.08    0.00   97.30
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda             206.00        64.00     10208.00         0.00         64      10208          0
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           1.60    0.00    0.76    0.00    0.00   97.64
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda               4.00         0.00        48.00         0.00          0         48          0
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           2.44    0.00    1.18    0.00    0.00   96.38
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda             220.00         0.00     10516.00         0.00          0      10516          0

三份全是实时窗口:每份的 CPU 行紧跟 %idle 97 左右的当下值,sda 的写入量是「该 1 秒内」的增量(10208/48/10516 kB)。对比 3.2 的输出可确认第一份开机快照已被省去。

💡 要点:-y 不带 interval 时无效(本来就只有一份开机快照);与 -t 同用是无人值守采样的最佳组合。

3.8 -p ALL:把分区从总账里拆出来

默认输出只报整盘(sda),-p ALL 额外列出每个分区(sda1/sda2/sda3)的独立账本:

$ iostat -p ALL -d -k | grep -E "Device|sda"
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda             872.81       490.90     29574.63         0.00   13478097  812001029          0
sda1              0.00         0.02         0.00         0.00        416          0          0
sda2              0.01         0.22         0.00         0.00       6072          5          0
sda3            872.80       490.58     29574.63         0.00   13469401  812001024          0

解读:sda1 只在开机时被读过 416 kB(BIOS 引导分区),sda2 读写都趋近于 0(EFI 分区),sda3 的 kB_wrtn(812001024)与整盘 sda(812001029)几乎完全相等——全部业务写入都落在根分区所在的 sda3 上。

💡 要点:-p ALL 展开所有设备及其分区,也可点名:iostat -p sda 只看 sda 及其分区;分区视图对「哪个挂载点在打 IO」的第一层定位很有用(进一步到文件/进程层要用 iotop 或 pidstat)。

3.9 -z:隐藏采样窗口内零活动的设备

本机有 18 个 loop 回环设备常年刷屏。-z 会把「该报告窗口内没有任何 IO」的设备整行隐藏,实时窗口下通常只剩主盘:

$ iostat -z -d -k 1 2
Linux 6.8.0-138-generic (f1034b9326b2)  10/15/26        _x86_64_        (12 CPU)

Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
loop0             0.00         0.00         0.00         0.00         17          0          0
loop1             0.00         0.04         0.00         0.00       1082          0          0
loop10            0.00         0.05         0.00         0.00       1265          0          0
loop11            0.00         0.01         0.00         0.00        355          0          0
loop12            0.00         0.01         0.00         0.00        345          0          0
loop13            0.00         0.01         0.00         0.00        345          0          0
loop14            0.03         1.18         0.00         0.00      32390          0          0
loop15            0.00         0.01         0.00         0.00        295          0          0
loop16            0.00         0.00         0.00         0.00         51          0          0
loop17            0.09         1.44         1.73        21.48      39415      47484     589860
loop2             0.00         0.04         0.00         0.00       1102          0          0
loop3             0.01         0.13         0.00         0.00       3629          0          0
loop4             0.00         0.04         0.00         0.00       1075          0          0
loop5             0.00         0.04         0.00         0.00       1110          0          0
loop6             0.00         0.04         0.00         0.00       1082          0          0
loop7             0.00         0.04         0.00         0.00       1101          0          0
loop8             0.02         0.28         0.00         0.00       7805          0          0
loop9             0.01         0.05         0.00         0.00       1375          0          0
sda             872.81       490.90     29574.62         0.00   13478097  812001029          0


Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda               3.00         0.00        48.00         0.00          0         48          0

对比两份报告:第一份是开机快照,所有设备都有历史累计量故全部保留;第二份是 1 秒实时窗口,loop 设备恰好都空闲,只剩 sda 一行——-z 的价值就是把注意力强制聚焦到真正有活动的设备上。

💡 要点:-z 的判断口径是「这一份报告里 tps 与吞吐全为 0」,不是「设备不存在」;若某 loop 设备恰好活动,它仍会出现。

3.10 压力实证:dd 直写 2GB 期间的 iostat -x 1 4

后台启动 dd 直写(oflag=direct 绕过页缓存直接落盘),随后立即连续采样 4 份 -x 报告,观察瓶颈指标的变化(grep 只保留了 avg-cpu 数值行、表头与 sda 行):

$ dd if=/dev/zero of=/root/demo/iostat/load.bin bs=1M count=2000 oflag=direct & sleep 0.3; iostat -x 1 4 | grep -E "avg-cpu|%user|%system|%iowait|%idle|Device|sda "; wait; rm -f /root/demo/iostat/load.bin
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz     d/s     dkB/s   drqm/s  %drqm d_await dareq-sz     f/s f_await  aqu-sz  %util
sda             30.82    490.87     8.80  22.21    1.57    15.93  841.96  29581.46   233.02  21.68    1.25    35.13    0.00      0.00     0.00   0.00    0.00     0.00    0.00    0.00    1.10   8.15
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz     d/s     dkB/s   drqm/s  %drqm d_await dareq-sz     f/s f_await  aqu-sz  %util
sda              7.00     28.00     0.00   0.00    0.43     4.00 1445.00 661980.00    65.00   4.30    1.40   458.12    0.00      0.00     0.00   0.00    0.00     0.00    0.00    0.00    2.03  90.20
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz     d/s     dkB/s   drqm/s  %drqm d_await dareq-sz     f/s f_await  aqu-sz  %util
sda              3.00     12.00     0.00   0.00    3.00     4.00 1121.00 568756.00    47.00   4.02    1.58   507.36    0.00      0.00     0.00   0.00    0.00     0.00    0.00    0.00    1.78  93.40
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz     d/s     dkB/s   drqm/s  %drqm d_await dareq-sz     f/s f_await  aqu-sz  %util
sda              4.00     76.00    15.00  78.95    5.25    19.00 1174.00 500572.00   257.00  17.96    1.92   426.38    0.00      0.00     0.00   0.00    0.00     0.00    0.00    0.00    2.27  85.80
2000+0 records in
2000+0 records out
2097152000 bytes (2.1 GB, 2.0 GiB) copied, 3.48979 s, 601 MB/s

第 1 份是开机快照(wkB/s 29581、%util 8.15%);第 2~4 份落在 dd 存续的 3.5 秒窗口内,指标对比一目了然:

指标空闲基线(快照)dd 期间窗口变化
wkB/s29581661980 → 568756 → 500572约 17~22 倍
wareq-sz35.13 kB458.12 → 507.36 → 426.38顺序大块写(1 MB/次)
w_await1.25 ms1.40 → 1.58 → 1.92排队变长、单请求变慢
aqu-sz1.102.03 → 1.78 → 2.27队列深度翻倍
%util8.15%90.20% → 93.40% → 85.80%设备接近饱和

第 2~4 份 CPU 行中 %iowait 与 %system 同步抬升(详见 3.11),与设备侧指标互相印证。dd 结束后 4 份报告的最后一行给出了本次写入总量:2097152000 字节、601 MB/s——与 iostat 的 wkB/s(约 647 MB/s 峰值)口径吻合。

3.11 压力实证:默认视图 iostat -k 1 4 与 CPU 侧 %iowait

同样的 dd 压力用默认视图(-k)观察:

$ dd if=/dev/zero of=/root/demo/iostat/load.bin bs=1M count=2000 oflag=direct & sleep 0.3; iostat -k 1 4 | grep -E "avg-cpu|%iowait|%idle|Device|sda "; wait; rm -f /root/demo/iostat/load.bin
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda             872.83       490.82     29651.96         0.00   13478233  814266929          0
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda             809.00        12.00    411660.00         0.00         12     411660          0
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda            1496.00        36.00    642152.00         0.00         36     642152          0
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
Device             tps    kB_read/s    kB_wrtn/s    kB_dscd/s    kB_read    kB_wrtn    kB_dscd
sda            2559.00         4.00    569064.00         0.00          4     569064          0
2000+0 records in
2000+0 records out
2097152000 bytes (2.1 GB, 2.0 GiB) copied, 3.82669 s, 548 MB/s

说明:grep 模式里没有匹配 CPU 数值行,故上面只保留 CPU 表头与设备行;三份实时窗口的 kB_wrtn/s 分别为 411660、642152、569064(即 402/627/555 MB/s),tps 从 809 涨到 2559。再用 -c 单独看 CPU 段的窗口值:

$ dd if=/dev/zero of=/root/demo/iostat/load.bin bs=1M count=2000 oflag=direct & sleep 0.3; iostat -c 1 3 | grep -E "avg-cpu|^ +[0-9]"; wait; rm -f /root/demo/iostat/load.bin
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
          19.11    0.00    4.89    0.75    0.00   75.24
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           8.99    0.00   11.03    1.10    0.00   78.88
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           9.44    0.00   10.78    2.27    0.00   77.51
2000+0 records in
2000+0 records out
2097152000 bytes (2.1 GB, 2.0 GiB) copied, 3.58539 s, 585 MB/s

开机快照的 %iowait 0.75、%system 4.89;dd 期间两个窗口涨到 %iowait 1.10/2.27、%system 11.03/10.78——直接 IO 的落盘路径主要消耗内核态时间(%system 翻倍),而 %iowait 只轻微上升,因为这块盘足够快、CPU 很少真正「等」盘。

💡 要点:%iowait 高说明 CPU 在等 IO,但它不是 IO 量的度量——高速盘 + 高吞吐时 %iowait 可以很低;判断 IO 瓶颈永远以设备侧 %util/await/aqu-sz 为主,%iowait 作旁证。

四、实战示例

4.1 判断磁盘是否成为瓶颈

线上「服务卡」时按三步走(数据取自 3.10 实测):

$ iostat -x -k 1 2        # 丢弃第一份快照,只看第二份实时窗口
  1. 看 %util:dd 期间 90.20%~93.40%,说明该窗口内设备九成时间在处理 IO;
  2. 看 w_await/r_await:从 1.25 ms 涨到 1.92 ms,请求开始排队;
  3. 看 aqu-sz:从 1.10 涨到 2.27,平均有 2 个以上请求在盘上排队。

三者同向恶化即可判定磁盘达到瓶颈;若 %util 高但 await 与 aqu-sz 都很低(SSD 并行处理强),盘未必是真瓶颈,需结合应用延迟交叉判断。

4.2 观察一次大文件写入的完整过程

先用 iostat -t -d -k 1 2 挂上时间戳持续采样,再在另一窗口执行写入,事后按时间对账(实测回显见 3.6):14:00:39 窗口内 sda 写入 7964 kB,就是那一秒应用的真实落盘量。无人值守采集建议直接用:

$ iostat -yt -d -k 2 > /tmp/io.log 2>&1 &

-y 去掉开机快照、-t 带时间戳,日志文件里每份报告自含时间,事后可与业务日志逐秒对齐。

4.3 定位「哪块盘、哪个分区」在打 IO

多盘机器先用 -x 看哪块盘 %util 高,再用 -p ALL 下钻分区(实测回显见 3.8):本机 sda3 的写入量(812001024 kB)与整盘(812001029 kB)几乎相等,说明 IO 全部来自根文件系统,排除其他分区的嫌疑后再用 iotop/pidstat 定位到进程。

4.4 在满是回环设备的机器上聚焦主盘

带 snap 的 Ubuntu 上 loop 设备多达十几个,两种过滤姿势(均已实测):

$ iostat -z -d -k 1 2          # 让 iostat 自己隐藏零活动设备(见 3.9)
$ iostat -x 1 | grep -E "Device|^sda "   # 或直接点名只看 sda(grep 过滤,输出同 3.3 的 sda 行)

4.5 用 -c 快速核对 CPU 是否被 IO 拖住

不想开 mpstat 时,iostat -c 1 3 一条命令看 %iowait 走势(实测回显见 3.11):dd 直写期间 %iowait 从 0.75 升至 2.27、%system 从 4.89 升至 11.03。若某窗口 %iowait 长期 >20%,通常意味着存储层跟不上,回到 4.1 的设备侧指标确认。

五、注意事项与常见问题

Q1:为什么第一条报告的数字和后面完全不是一个量级?

因为 iostat 不带 interval 或首份报告的口径都是「自系统启动以来的平均值」(实测见 3.2:首份 %user 19.09 对比实时窗口 1.68)。排查当下问题只看第二份起的数据,或直接加 -y 屏蔽首份。

Q2:%util 接近 100% 就一定是磁盘瓶颈吗?

不一定。%util 统计的是「设备有未完成 IO 的时间占比」,对支持并行处理的 SSD/NVMe 与多队列设备,100% 只表示「一直有 IO 在飞」,不代表吞吐已饱和(此时可能 %util 100% 但 await 仅零点几毫秒)。可靠判据是三件套同向恶化:%util 高 + await 明显上升 + aqu-sz 持续大于 1(实测对照见 3.10:本机 dd 期间 %util 90%~93%、w_await 1.40→1.92 ms、aqu-sz 2.0 左右,才是真实的接近饱和状态)。

Q3:应用写了 1 GB,iostat 的 kB_wrtn 却对不上?

三个常见原因:写入先进页缓存、尚未落盘(kB_wrtn 滞后);相邻写请求在块层被合并(%wrqm 列可见合并比例);读改写、文件系统元数据带来的额外写。想让应用写多少 iostat 就看到多少,用 oflag=direct 式的直接 IO(本文压力实测即如此,2 GB 写入与 wkB/s 累计口径吻合,见 3.10)。

Q4:装完 sysstat 后磁盘上多了什么?

安装脚本启用了 sysstat-collect.timer(回显见 2.1),默认每 10 分钟把一份系统活动快照写入 /var/log/sysstat/,由 sar 读取回放。它本身就是少量周期性磁盘写,且日志会随时间增长;不需要历史功能时可编辑 /etc/default/sysstat 将 ENABLED 改为 "false" 关闭。

Q5:机器上一堆 loop 设备刷屏怎么办?

snap 管理的每个包都挂一个回环设备,iostat 会如实列出(本机 18 个)。推荐 -z(隐藏零活动设备,见 3.9)或 grep -vE "^loop" 管道过滤;也可点名只看目标盘 iostat -x sda。

Q6:r_await/w_await 的单位是什么?怎么和 %util 配合读?

单位是毫秒,统计口径「在队列里等待 + 设备处理」的总平均。await 低(SSD 亚毫秒到几毫秒)说明请求吞吐顺畅;await 高而 %util 也高,说明设备处理能力到顶了;await 高而 %util 低,更可能是请求在系统其他环节(如调度、限流)排队。

Q7:kB_dscd 是什么?为什么是 0?

discard(TRIM)统计,文件系统批量「告知盘某段不再使用」时产生。本机未启用周期性 discard(kB_dscd 恒为 0,见 3.1 与 3.9),属常见现象,不代表异常;在启用 discard 挂载选项或 fstrim.timer 的机器上会看到数值。

六、参考链接