这一章在做什么

回答一个问题:慢是不是因为磁盘,如果是,慢在哪一层、是谁发的 IO。先把一次 IO 的时间分清:在系统队列里等的、在盘上服务的;再用 USE 方法看每块盘的利用率、饱和度和错误,用负载特征描述 IO 的次数、大小、随机还是顺序;最后用延迟分布和逐个 IO 的跟踪找出异常值。最该带走的是 iostat -x 的读法,以及为什么 %util 到了 100% 的 SSD 可能离上限还很远。

一次磁盘 IO 走过的路,各层由谁看见 应用:read / write / fsync 文件系统 + page cache 块层:合并、IO 调度blk-mq 的各个队列 设备驱动 盘:盘内队列 → 缓存 / 介质 按进程:pidstat -d、iotop、biotop 读命中缓存就到此为止,盘上看不到 按盘:iostat、sar -d、/proc/diskstats 每个 IO:biolatency、biosnoop、blktrace 盘自己报:smartctl(健康、错误计数)
本站画的示意。块层是这一章的主战场:iostat 的统计和 BCC 工具挂的跟踪点都在这里。驱动下面还可能有硬件 RAID 或存储阵列,操作系统看不进去(§9.4.4)。

什么时候翻这一章

上一章(文件系统)讲应用看到的 IO,这一章讲落到盘上的 IO;两者的数量和大小常常对不上,原因在 §9.3.13 和 §8.3.12。利用率到 60% 就开始排队的道理来自第 2 章的排队论(§2.6.5)。perf、BPF 工具的细节在第 13、15 章。

先用中文过一遍

一次 IO 的时间:从哪里开始算

一次 IO 的时间分两种:在队列里等的(wait time),和真正被处理的(service time)。问题在于「从哪里开始算」。书里分了两套说法(§9.3.1):从内核看,IO 先在块层的队列里等,下发给盘以后到完成中断回来是「块 IO 服务时间」,两段加起来是「块 IO 请求时间」;从盘看,IO 先在盘自己的队列里等,再被服务,两段加起来正好等于内核眼里的块 IO 服务时间。

这就是为什么各个工具报的延迟对不上:iostat 的 await 从 IO 进入块层算起,包括在系统队列里等的时间;biolatency 默认从下发给盘算起,加 -Q 才把系统队列算进去;应用自己计时,还要再加上系统调用和文件系统的开销。老版本 iostat 有一列 svctm,用利用率除以 IOPS 推算服务时间,前提是盘一次只处理一个 IO;今天的盘能同时处理很多个,这个算法不再准,新版本已经删了这一列(§9.6.2)。还要记住:盘内缓存命中和未命中的延迟差一到两个数量级,平均值是两个峰的折中,谁也代表不了(§9.3.2)。

一次读 IO 的时间分成几段,各工具从哪里开始算 系统调用、文件系统 块层队列(系统里等) 盘内排队 盘服务:寻道或读闪存、传输 进入块层 下发给盘 完成中断 biolatency、biosnoop:盘的请求时间 iostat 的 await、biolatency -Q:块层请求时间 应用自己计时:还要加上系统调用和文件系统 盘内排队和盘服务的分界只有盘自己知道,操作系统量到的是两段之和
本站画的示意,各段长短不按比例。blk-mq 不用调度器时,块层队列可能很短甚至没有;延迟主要在哪一段,要分层量(§9.5.5)。

利用率和饱和度:%util 为什么在 SSD 上会骗人

iostat 的 %util 是按时间算的利用率:这一秒里,有多少时间盘上至少挂着一个没完成的 IO(§9.3.9)。对一次只能服务一个 IO 的设备,这个定义很好用:100% 就是满了,再来的只能排队。而且机械盘不用等到 100%:书里用第 2 章的 M/D/1 模型说明,利用率过了 60%,平均响应时间就是空闲时的两倍(§2.6.5),所以 60% 左右就可能开始影响延迟。

但 SSD 内部有很多个闪存通道,NVMe 还有多个硬件队列,能同时处理许多 IO;存储阵列和 RAID 虚拟盘背后是多块盘。对它们,100% 只说明「一直有 IO 在飞」,不说明「接不下了」。第 2 章说得更直白:忙了 100% 的时间,不等于用了 100% 的能力(§2.3.11)。sysstat 自己的 iostat 手册也写明了这一点。本站在 WSL2 上实测:一个 dd 用 4 KB 直接 IO 写,%util 约 70%、每秒约 1.1 万次;四个 dd 同时写,每秒到了 4.4 万次,%util 只涨到 95% 左右。

所以判断饱和,要看排队:aqu-sz 是平均有多少个请求在系统队列里等或正在盘上(§9.6.1),并且看 await 有没有随着负载往上走。aqu-sz 远大于盘能并行的数量、await 跟着涨,才是真饱和。还有一层:利用率是区间平均,这一秒 50% 可能是前半秒满载、后半秒空闲(§9.3.10)。

两块盘的 %util 都是 100% 机械盘:一次只服务一个 IO 一秒里每一刻都有 IO 在服务 100% ≈ 真满了 再来的 IO 只能排队,await 上涨 NVMe SSD:几十个单元可以同时干活 同一时刻 只有两三个 在干活 100%:只说明一直有 IO 在飞 加并发,IOPS 多半还能涨 判断饱和:看 await 是否随负载上涨、aqu-sz 和压测出的上限比
本站画的示意,单元数是编的。虚拟盘(硬件 RAID、云盘)也是同样的道理:操作系统只知道这块「盘」忙不忙,不知道底下几块盘各在干什么(§9.3.9)。

IOPS 不能直接比:大小、随机、读写

吞吐等于 IOPS 乘以平均 IO 大小。大 IO 吞吐高,但每次更慢;小 IO 次数多,每次都要付一遍固定开销(§9.3.6)。所以「这块盘能跑 5 万 IOPS」单独说没有意义,要连同大小、随机还是顺序、读还是写、同时有几个 IO 一起说(§9.3.7)。

机械盘最怕随机:每次 IO 前磁头要先寻道,再等盘片转到那个扇区,两样都是毫秒级,而传 4 KB 数据只要几十微秒。顺序 IO 省掉了这两步,所以同一块盘,顺序和随机的 IOPS 可以差几十倍(§9.4.1)。闪存没有机械动作,随机读和顺序读基本一样(§9.3.4);它怕的是小块写:闪存只能按整块擦除再写,比擦除块小的写会引起额外的搬移,这叫写放大(write amplification)。书里举了个例子:有的闪存盘可能在 4 KB 读、1 MB 写时表现最好,不同的盘要查厂商资料或自己测(§9.3.6)。

一次 4 KB 读的时间去哪了(典型量级,按比例画) 机械盘随机读 寻道:几毫秒 等盘转过来:平均半圈 ≈ 8 ms 机械盘顺序读 不用寻道、不用等,只剩传输和固定开销 SSD 读 约 0.1 ms,随机和顺序差不多 7200 转的盘转半圈约 4.2 ms。比例尺下 SSD 那一条短得几乎看不见,这正是它替代机械盘的原因
本站画的示意,数字是量级。书里 §9.3.2 有一张按事件列的延迟量级表,可以对照看;真实的数要查厂商资料或自己测。

iowait:不是盘的指标

iowait 是 CPU 的一种空闲:CPU 闲着,同时有线程在等磁盘 IO(§9.3.11)。它把空闲分成「真没事干」和「在等盘」两种,所以高 iowait 确实提示盘可能是瓶颈。但它会被 CPU 负载左右:来了一个吃 CPU 的进程,CPU 不闲了,iowait 就降下去,盘上的问题一点没变;反过来,应用升级后更省 CPU,iowait 反而冒出来,看着像新版本把盘搞慢了。

更可靠的是直接看线程被 IO 挡住了多久:/proc/pressure/io(PSI)给出有任务因等 IO 而停顿的时间比例(§9.6.3),pidstat -d 的 iodelay 按进程给出阻塞的时钟滴答数(§9.6.4)。另外,盘慢不一定让应用慢:写进 page cache 就返回的写、文件系统的预读、专门的 IO 线程,都和应用的请求是异步的(§9.3.12)。反过来,应用的一次读写到了盘上可能变成好几次 IO(元数据、日志),也可能一次都没有(命中缓存)(§9.3.13)。

英文中文一句话
IOPS每秒 IO 次数单独一个数没意义,要带上大小、随机与否、读写 §9.3.7 术语页
throughput / bandwidth吞吐 / 带宽吞吐是当前每秒传多少字节,带宽是硬件能到的上限 §9.1 术语页
await平均请求时间iostat 的一列,含系统队列和设备上的时间,单位毫秒 §9.6.1 术语页
service time / wait time服务时间 / 等待时间被处理的时间和排队的时间;从内核看和从盘看,分界不同 §9.3.1
utilization (%util)利用率区间里盘上至少有一个 IO 的时间比例,不是能力的比例 §9.3.9 术语页
saturation (aqu-sz)饱和度(平均队列长度)超出处理能力而排着的工作;iostat 里看 aqu-sz §9.3.10 术语页
I/O waitIO 等待CPU 空闲且有 IO 未完成的时间,会被 CPU 负载挤掉 §9.3.11 术语页
random / sequential I/O随机 / 顺序 IO按相邻两次 IO 在盘上的位置是否接续来分 §9.3.4 术语页
latency outlier延迟异常值延迟远高于大多数的个别 IO,平均值里看不见 §9.1 术语页
write-back cache回写缓存数据进了缓存就报完成,稍后再写到介质;对应的是直写 §9.2.2 术语页
write amplification写放大闪存按块擦除,小块写引起额外的搬移和写入 §9.4.1 术语页
FTL (flash translation layer)闪存转换层SSD 控制器里把扇区读写翻译成闪存页和擦除块的那层 §9.4.1
I/O scheduler / blk-mqIO 调度器 / 多队列块层块层里给 IO 排序、合并的部件;blk-mq 按 CPU 分提交队列 §9.4.4 术语页
PSI (pressure stall information)压力停顿信息io 一项给出有任务因等 IO 而停顿的时间比例 §9.6.3 术语页
direct I/O直接 IO用 O_DIRECT 绕过 page cache;测盘本身时要用它 §9.8.1 术语页

方法:按什么顺序查

书里列了十种磁盘的方法,作者推荐的顺序是:USE 方法、性能监控、负载特征、延迟分析、微基准测试、静态分析、事件跟踪(§9.5)。落到一次排查上,本站整理成下面几步:前三步用 sysstat 和 /proc 就能做完,第四步开始要 BPF 工具,多数要 root。

  1. 应用真的在等盘吗?

    cat /proc/pressure/io 看 IO 停顿比例和趋势;vmstat 1 的 b 列是阻塞在 IO 上的线程数;pidstat -d 1 的 iodelay 按进程看等了多久。iowait 只当线索。都不高,就别在盘上花时间。§9.3.11§9.6.3

  2. 哪块盘,忙不忙,排不排队,有没有错(USE)

    iostat -sxz 1:按盘看 await、aqu-sz、%util。机械盘 %util 持续 60% 以上就可能在排队;SSD 和虚拟盘要看 await 是否上涨。错误先看 dmesg 里的 IO 报错和 smartctl,盘配成冗余时,坏了一块系统照样能跑,只是变慢,容易被忽略。§9.5.2§9.6.1

  3. 负载长什么样:多少次、多大、读还是写、随机还是顺序

    iostat -dx 1 把读写分开:r/s、w/s、rkB/s、wkB/s、rareq-sz。r_await 通常比 w_await 更直接关系到应用。合并数 rrqm/s 为零、平均大小 8 KB 以下,多半是随机的小 IO。再用 pidstat -d 或 biotop 找出是谁。§9.5.4§9.6.8

  4. 延迟的分布:有没有双峰、有没有异常值

    biolatency -F 按读、写、同步写等标志分开画直方图,-D 按盘分开;看到几十毫秒的桶,再用 biosnoop 把那些 IO 一个个找出来,看它们前面是不是刚来过一批 IO(排队),还是孤零零地慢(盘或宿主的问题)。§9.6.6§9.6.7

  5. 慢在哪一层?

    把应用量到的延迟、biolatency -Q(含块层队列)和默认的 biolatency(只算盘)放在一起比:三者接近,就是盘慢;应用慢而盘不慢,问题在文件系统或更上面(锁、元数据),回到第 8 章。§9.5.5

  6. 这些 IO 为什么会发?

    biostacks 或 perf record -e block:block_rq_issue -a -g 看发 IO 的调用栈。常见的意外:文件系统日志、后台校验、路径查找读目录、内存不够引起的换页。§9.6.5§9.6.9

  7. 配置和上限

    几块盘、什么类型、RAID 怎么配、调度器是什么(/sys/block/*/queue/scheduler)、有没有 cgroup 的 IO 限额;不知道盘的上限,就在空闲时用 fio 或 dd oflag=direct 测一遍,作为以后判断「满没满」的基准。§9.5.6§9.5.9

工具速查

想知道敲什么看哪里节
IO 压力的趋势cat /proc/pressure/iosome avg10 比 avg300 高很多,说明压力在上升;full 是所有非空闲任务同时卡在 IO 上的时间比例§9.6.3
每块盘的概况iostat -sxz 1await 是平均每次 IO 的总时间;aqu-sz 是队列长度;%util 在 SSD 和虚拟盘上只代表忙碌时间§9.6.1
读写分开iostat -dx 1r_await 和 w_await 分开看;rareq-sz 小、rrqm/s 为零是随机小 IO;f/s 是刷缓存命令§9.6.1
过去的磁盘统计sar -d 1和 iostat 相近的列(tps、rkB/s、wkB/s、areq-sz、aqu-sz、await、%util),await 不分读写;配了 sysstat 定时采集,就能翻到出事那个时段§9.6.2
哪个进程在读写pidstat -d 1kB_rd/s、kB_wr/s;iodelay 是等 IO 的时钟滴答数(新内核要先打开 delay accounting,见「成书之后」)§9.6.4
按进程排的磁盘 topbiotop-bpfcc
iotop -o
每个进程的 IO 次数、字节数、平均延迟;PID 是尽力而为的,IO 下发时发起者可能已经不在 CPU 上§9.6.8
延迟的分布biolatency-bpfcc -m 10 1
biolatency-bpfcc -F 10 1
有没有两个峰、最右边的桶到了多少毫秒;-F 按 IO 标志拆开,常能看出慢的是写§9.6.6
每一个 IObiosnoop-bpfcc -Q进程、读写、扇区、大小、LAT(ms);QUE(ms) 是在系统队列里的时间。存成文件,按延迟列排序看最慢的几行§9.6.7
是哪条代码路径发的 IObiostacks.bt
perf record -e block:block_rq_issue -a -g -- sleep 10
内核栈指向文件系统的哪个操作;IO 被内核线程下发时,换成 block:block_rq_insert 更容易看到发起者§9.6.9§9.6.5
IO 在块层各阶段的耗时btrace /dev/sda
btt -i 文件
每个 IO 的排队、合并、下发、完成各一行;btt 的 D2C 是盘上的时间,Q2C 是块层的全程§9.6.10
盘的健康和错误smartctl -a /dev/sda
dmesg | grep -i error
纠正过的错误数、重映射的扇区、温度;纠错次数上涨是快坏的征兆§9.6.13
调度器和队列参数cat /sys/block/sda/queue/scheduler方括号里的是当前的调度器,none 表示不排序;同目录还有 nr_requests、read_ahead_kb§9.4.4§9.9.1
单次 IO 有多慢(轻量)ioping -c 10 .每秒一次小读,打印延迟;负载很轻,生产机上也能用§9.8.5
盘的上限fio --direct=1 …
dd … oflag=direct
同时开着 iostat 对照;一定要绕过 page cache,否则测的是内存§9.5.9§9.8.1

iostat、sar、pidstat 在 sysstat 包里,看自己的进程不要 root;带 -bpfcc 后缀的是 Debian bpfcc-tools 包里的 BCC 工具,biostacks.bt 在 bpftrace 包里,btrace、btt 在 blktrace 包里,这些都要 root。smartctl 在 smartmontools 包里,要 root。这些要 root 的工具本站没有在 WSL2 上实测,ioping、fio 也没有装。工具名的链接到本站的工具卡:iostat、sar、pidstat、biotop、iotop、biolatency、biosnoop、biostacks、perf、blktrace、smartctl、ioping、fio。

动手:在自己的机器上试

换 IO 大小、加并发,看 iostat -x 怎么变

用 dd 加 oflag=direct 绕过 page cache,让每次 write 都直接变成一次盘 IO,同时开着 iostat 看。先猜:块大小从 4 KB 换成 1 MB,每秒次数和每次的延迟各怎么变?四个 dd 一起写,%util 会不会变成原来的四倍?

# 找出你当前目录所在的盘:WSL2 上根目录一般是 sdX 里最大的那块,本站是 sdf
$ df . && lsblk
# 终端 1:每秒一行,-y 跳过开机以来的汇总
$ iostat -dxy sdf 1
# 终端 2:块大小 4K、64K、1M,各写一次再删掉(共 224 MB)
$ dd if=/dev/zero of=t.dat bs=4k count=8192 oflag=direct; rm t.dat
$ dd if=/dev/zero of=t.dat bs=64k count=1024 oflag=direct; rm t.dat
$ dd if=/dev/zero of=t.dat bs=1M count=128 oflag=direct; rm t.dat
# 一个写者和四个写者:每个在 32 MB 的文件上来回写四遍,持续三秒左右
$ w() { for k in 1 2 3 4; do dd if=/dev/zero of=$1 bs=4k count=8192 oflag=direct conv=notrunc 2>/dev/null; done; }
$ w s.dat; rm s.dat
$ for i in 1 2 3 4; do w p$i.dat & done; wait; rm p*.dat

本站在 WSL2 上看到的(iostat 里写的几列,各截了一行;4K 两行的吞吐是同一秒 iostat 的 wkB/s 换算的,64K 和 1M 不到一秒就写完了,吞吐用 dd 自己报的):

            吞吐       w/s   w_await  wareq-sz  aqu-sz  %util
4K  1 个   46 MB/s   11172     0.07      4.00    0.74   72.0
4K  4 个  198 MB/s   48240     0.06      4.00    2.67   97.2
64K 1 个  526 MB/s       -     0.08     63.97       -      -
1M  1 个  3.6 GB/s       -     0.19   1024.00       -      -
  1. 块越大,每次 IO 越慢(0.07 ms 到 0.19 ms),吞吐却高了几十倍:吞吐等于 IOPS 乘大小。64K 和 1M 两次 dd 都不到一秒就写完了,那一秒里大半是空的,所以每秒的数没有意义,只看平均大小和平均延迟(§9.3.6)。
  2. 一个写者时,%util 约 70%、aqu-sz 约 0.7:只有一个 IO 在飞,盘「闲着」的那 30% 其实是 dd 在做系统调用。用书里的式子推服务时间:0.72 ÷ 11172 ≈ 0.064 ms,和 w_await 对得上,因为此时一次只有一个 IO(§9.3.1)。
  3. 四个写者时,每秒次数翻了四倍多,%util 只涨到 97%,w_await 不变。这块虚拟盘能同时处理多个 IO,100% 远不是它的上限;同一个式子这时会推出 0.02 ms 的「服务时间」,明显不对。aqu-sz 约等于 IOPS 乘 await(2.67 ≈ 48240 × 0.06 ms),这是 Little 定律(§2.6.5)。
  4. 1 MB 的写到了 3.6 GB/s,超过普通 SSD 的能力:多半是 WSL2 的虚拟盘背后还有 Windows 一侧的缓存,direct IO 只绕过了 Linux 自己的 page cache。所以这里的数只能看趋势,不能当真机的数。
WSL2
本站实测过上面的命令,不需要 root(iostat 从 sysstat 包来)。WSL2 的盘是 Hyper-V 虚拟盘,/sys/block/sdf/queue/rotational 显示 1(按机械盘报),调度器是 none;延迟和吞吐都受宿主缓存影响,数字不代表真机。别的程序在同一块盘上读写时,iostat 里会混进它们的 IO。
服务器
别在生产盘上跑写测试;要测就在空闲时、在单独的目录里,量控制在几百 MB。机械盘上把 4K 的那条换成随机写(用 fio 的 --rw=randwrite),会看到 IOPS 掉到一两百、%util 一下到 100%。
ARM 板
busybox 的 dd 支不支持 oflag=direct,看它的版本和裁剪,跑一下就知道;不支持时用下一个实验里的 C 程序。没有 iostat 时用下面的 ds.sh 从 /proc/diskstats 算。eMMC 和 SD 卡写得多了会磨损,测试量要小。

顺序读和随机读,以及没有 iostat 时怎么算

dd 只会顺序读写。下面这个程序用 O_DIRECT 打开文件,按顺序或随机偏移读,自己记时间,算出应用看到的平均延迟,可以和 iostat 的 r_await 对照,这就是最简单的分层延迟分析(§9.5.5)。

// rd.c:用 O_DIRECT 绕过 page cache,按顺序或随机偏移读一个文件,量每次读的平均延迟
// 用法:./rd 文件 seq|rand 块大小(字节) 次数
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

static double now(void) {
    struct timespec t;
    clock_gettime(CLOCK_MONOTONIC, &t);
    return t.tv_sec + t.tv_nsec / 1e9;
}

int main(int argc, char **argv) {
    if (argc != 5) { fprintf(stderr, "usage: %s file seq|rand bs count\n", argv[0]); return 1; }
    int rnd = strcmp(argv[2], "rand") == 0;
    size_t bs = strtoul(argv[3], 0, 0);
    long n = atol(argv[4]);
    int fd = open(argv[1], O_RDONLY | O_DIRECT);        // O_DIRECT:缓冲区、偏移、长度都要按块对齐
    if (fd < 0) { perror("open"); return 1; }
    off_t blocks = lseek(fd, 0, SEEK_END) / bs;
    void *buf;
    if (posix_memalign(&buf, 4096, bs)) return 1;
    srand(42);
    double t0 = now();
    for (long i = 0; i < n; i++) {
        off_t b = rnd ? (off_t)(((unsigned long)rand() << 16 ^ rand()) % blocks) : i % blocks;
        if (pread(fd, buf, bs, b * bs) != (ssize_t)bs) { perror("pread"); return 1; }
    }
    double t = now() - t0;
    printf("%s bs=%zu: %.0f IOPS, %.1f MB/s, avg %.1f us\n", argv[2], bs, n / t, n * bs / t / 1e6, t / n * 1e6);
    return 0;
}
$ gcc -O2 -Wall rd.c -o rd
# 准备一个 128 MB 的文件(内容随机),iostat 照样在另一个终端开着
$ dd if=/dev/urandom of=big.dat bs=1M count=128; sync
$ ./rd big.dat seq 4096 30000
$ ./rd big.dat rand 4096 30000
$ ./rd big.dat seq 1048576 1500
$ rm big.dat

本站在 WSL2 上看到的(右边是同一时段 iostat 的 r_await):

seq bs=4096: 8425 IOPS, 34.5 MB/s, avg 118.7 us     r_await 0.10
rand bs=4096: 5501 IOPS, 22.5 MB/s, avg 181.8 us    r_await 0.15
seq bs=1048576: 2248 IOPS, 2357.7 MB/s, avg 444.7 us   r_await 0.41
  1. 应用量到的 119 µs 比 r_await 的 100 µs 多出约 20 µs:那是系统调用和文件系统的开销,不在块层里。两者接近,说明延迟主要在盘这一侧(§9.5.5)。
  2. 随机读比顺序读慢了一半左右,但远没有机械盘上几十倍的差距:这块虚拟盘背后是 SSD 加宿主缓存。真 SSD 上随机读和顺序读通常差不多(§9.3.4)。
  3. 程序一次只有一个读在飞,所以 IOPS 约等于 1 秒除以平均延迟。想要更高的 IOPS,得同时发多个 IO(多线程或异步 IO),这正是 SSD 需要的。

板子上没有 iostat 时,iostat 的几列都能从 /proc/diskstats 两次读数的差算出来。下面这个脚本只用 sh 和 awk,busybox 里一般都有:

#!/bin/sh
# ds.sh:没有 iostat 时,从 /proc/diskstats 算一块盘这一秒的 r/s、w/s、await、aqu-sz、%util
# 用法:sh ds.sh sda
s() { awk -v d="$1" '$3 == d { print $4, $8, $7, $11, $13, $14 }' /proc/diskstats; }
# 六个数依次是:读完成次数、写完成次数、读耗时、写耗时、忙的时间、加权耗时(后四个单位是毫秒)
a=$(s "$1"); sleep 1; b=$(s "$1")
echo "$a $b" | awk '{ n = ($7 - $1) + ($8 - $2)
  printf "r/s %d  w/s %d  await %.2f ms  aqu-sz %.2f  %%util %.1f\n",
    $7 - $1, $8 - $2, n ? (($9 - $3) + ($10 - $4)) / n : 0, ($12 - $6) / 1000, ($11 - $5) / 10 }'
$ sh ds.sh sdf
ds.sh:   r/s 0  w/s 10345  await 0.06 ms  aqu-sz 0.67  %util 66.4
iostat:  w/s 10312    w_await 0.07    aqu-sz 0.67  %util 66.0

两边是同一秒里各自取的样,差一点是正常的。%util 就是「忙的时间」这个计数器每秒涨了多少毫秒;aqu-sz 是「加权耗时」每秒涨了多少毫秒再除以 1000。看清了这两个计数器怎么来的,就明白 %util 为什么说不出盘还剩多少能力。

WSL2
本站实测过 rd 和 ds.sh,不需要 root;ds.sh 和 iostat 的结果在同一秒里对得上。
服务器
rd 可以直接对一个大文件跑(不要对裸设备做写测试)。机械盘上随机 4 KB 读的平均延迟会到几毫秒,和顺序读差出一两个数量级;顺带用 iostat -x 看 rrqm/s:顺序读常有合并,随机读没有。
ARM 板
rd 用交叉编译器编一下就能跑,O_DIRECT 要文件系统支持(ext4 支持;不支持的文件系统上会报错)。eMMC 在 /proc/diskstats 里叫 mmcblk0,SD 卡常是 mmcblk1,看你的板子。

动手:模拟器

先猜:一块 NVMe SSD,一个线程一次读 4 KB,%util 是多少?这时它用了自己多少能力?把并发从 1 加到 32,IOPS 涨几倍、%util 又涨几倍?再换成机械盘,把随机比例从 0 拖到 10%,IOPS 会掉多少?

模拟器

IO 大小、随机与顺序

一个简化的盘:每条流发一个 IO,等它完成,花 10 µs 处理,再发下一个。选盘、IO 大小、随机比例和同时在飞的 IO 数,看 IOPS、吞吐、延迟、iostat 算出的 %util,和盘真正用了多少能力。时间条画的是前几条流的 IO:橙色是排队,蓝色是在盘上服务。

时间条

盘上有 IO

排队在盘上服务。灰条是任一 IO 在盘上的时间,%util 就是它占的比例。

盘内的并行单元

带宽用了
—
IOPS(r/s)
—
吞吐 MB/s
—
单次延迟(await)
—
aqu-sz
—
%util(iostat 的算法)
—
能力用了
  1. NVMe、4 KB 随机、并发 1:%util 约 90%,「能力用了」不到 3%。连点「并发加倍」到 32:IOPS 涨到约 32 倍,%util 只是从 90% 到 100%。这就是 iostat 的 %util 在 SSD 上说不出「还剩多少」的原因。
  2. 换成机械盘,4 KB,随机比例 0%:每秒上万次;拖到 10%:每次 IO 平均多出 0.8 ms 的寻道和等待,IOPS 掉到一千出头。再把并发加到 8:IOPS 不涨,await 涨到约 8 倍,aqu-sz 约等于 8,这是真饱和。
  3. 选 SATA SSD、1 MB 顺序:并发 1 时「带宽用了」就已经九成多,加到 2 就撞上 530 MB/s 的接口上限。大 IO 先撞带宽,小 IO 先撞次数,所以 IOPS 和吞吐要一起看。

成书之后

pidstat 的 iodelay、iotop 的 IO% 默认没有数了

书里
用 pidstat -d 的 iodelay 看进程被磁盘 IO 阻塞了多久,并说 iotop 需要内核开着几项任务统计的配置(§9.6.4、§9.6.8)。
今天
Linux 5.14(2021 年)起,任务的延迟统计(delay accounting)默认关闭,iodelay 会一直是 0,iotop 的 IO 一列显示不可用。打开的办法是启动参数加 delayacct,或者运行时 sysctl kernel.task_delayacct=1,后者只对打开之后启动的进程有效。本站的 WSL2 上 /proc/sys/kernel/task_delayacct 是 0,一个正在直接写盘的 dd,iodelay 读出来也是 0。
核对于 2026-10。出处:内核文档《Delay accounting》;2021 年 8 月 LKML 上关于 5.14-rc 中 iotop 失效的讨论(提交「delayacct: Default disabled」);iotop(8) 手册页。

cgroup 的磁盘限额换成了 v2 的 io 控制器

书里
按进程组限磁盘 IO 用 cgroup 的 blkio 子系统,可以按比例分配,也可以设 IOPS 或吞吐的上限(§9.9.1)。
今天
blkio 是 cgroup v1 的名字。用 systemd 的发行版已经默认 cgroup v2(Debian 从 11 开始),对应的是 io 控制器:io.max 设读写的字节和次数上限,io.weight 按比例分(要内核开了相应的支持),io.latency 保护延迟目标,io.pressure 是这个组自己的 PSI。本站的 WSL2 就是 cgroup v2,子组里能看到 io.max、io.latency、io.pressure。容器里 IO 慢,先看这个组的 io.max 和 io.pressure。
核对于 2026-10。出处:内核文档《Control Group v2》的 IO 一节;Debian 11 发行说明(systemd 默认使用 cgroup v2)。

Optane 停产了

书里
介绍 Intel 和 Micron 的 3D XPoint(品牌 Optane),预期它会以介于内存和闪存之间的性价比用到更多场合(§9.4.1)。
今天
Intel 在 2022 年 7 月的季度财报中宣布逐步结束 Optane 业务,不再开发后续产品。书里关于持久内存的性能对比仍可当背景读,但选型时已经不是一个选项。
核对于 2026-10。出处:Intel 2022 年第二季度 10-Q 报告;AnandTech《Intel to Wind Down Optane Memory Business》。

分节地图

这一章按书的固定套路走:背景(§9.1–9.3)、架构(§9.4)、方法(§9.5)、工具(§9.6)、可视化、实验和调优(§9.7–9.9)。上手只需要概念里讲时间、IO 大小与随机、利用率、饱和度、iowait 的几节,方法里的 USE、负载特征和延迟分析,工具里的 iostat、PSI、pidstat、biolatency、biosnoop。机械盘和 SSD 的内部、RAID、调度器,用到时再查。

  1. Disk I/O can cause significant…约 300 词开篇磁盘为什么值得查,SSD 也会出问题;学习目标当目录扫一眼。
  2. Terminology约 200 词术语吞吐和带宽、IO 延迟、延迟异常值怎么区分。
  3. Models约 100 词模型三个简单模型的引子。
  4. Simple Disk约 100 词简单磁盘盘自己带队列,可以按排队论分析。
  5. Caching Disk约 200 词带缓存的磁盘盘内缓存让延迟分成命中、未命中两种;回写和直写。
  6. Controller约 100 词控制器两条总线、控制器、盘,任何一处都可能是上限。
  7. Concepts约 100 词概念一组概念的引子。
  8. Measuring Time约 700 词量时间等待、服务、请求时间各从哪儿量到哪儿;用 IOPS 和利用率推服务时间的局限。
  9. Time Scales约 300 词时间尺度从几十微秒到上千毫秒;多少毫秒算慢,看环境。
  10. Caching约 100 词缓存从应用到盘有好几层缓存,最好的 IO 是不发生。
  11. Random vs. Sequential I/O约 300 词随机与顺序机械盘怕随机,闪存基本不在乎;操作系统看到的偏移不等于物理位置。
  12. Read/Write Ratio约 100 词读写比读多加缓存,写多加盘。
  13. I/O Size约 200 词IO 大小大 IO 吞吐高、单次慢;闪存盘对读写大小各有偏好。
  14. IOPS Are Not Equal约 200 词IOPS 不能直接比IOPS 要连同大小、随机与否、读写一起说。
  15. Non-Data-Transfer Disk Commands约 100 词不传数据的命令缓存刷盘、TRIM 也会占着盘。
  16. Utilization约 500 词利用率60% 就可能开始排队;虚拟盘的利用率为什么和底下的盘对不上。
  17. Saturation约 100 词饱和度队列长度;利用率不到 100% 也可能有排队。
  18. I/O Wait约 300 词IO 等待iowait 为什么会误导,更可靠的是线程被 IO 阻塞的时间。
  19. Synchronous vs. Asynchronous约 200 词同步与异步盘慢不一定让应用慢:回写、预读、IO 线程。
  20. Disk vs. Application I/O约 100 词磁盘 IO 与应用 IO盘上的 IO 和应用发的 IO 数量、大小对不上的几种原因。
  21. Architecture约 100 词架构容量规划时读,排查时回来核对。
  22. Disk Types约 2800 词磁盘类型机械盘的寻道和旋转、扇区、振动;SSD 的闪存、FTL、写放大、寿命;持久内存。
  23. Interfaces约 800 词接口SCSI、SAS、SATA、FC、NVMe;NVMe 的多队列。带宽数字要查当前规格。
  24. Storage Types约 1000 词存储类型直连盘、RAID 各级别的性能、存储阵列、NAS。
  25. Operating System Disk I/O Stack约 800 词操作系统的磁盘 IO 栈块层的合并、IO 调度器、blk-mq。改调度器前读。
  26. Methodology约 100 词方法方法总表和作者推荐的顺序。
  27. Tools Method约 200 词工具法挨个跑工具,并给了几个参考阈值。
  28. USE Method约 400 词USE 方法对盘和控制器分别查利用率、饱和度、错误;控制器的利用率按吞吐算。
  29. Performance Monitoring约 200 词性能监控长期盯利用率和响应时间,要按盘看。
  30. Workload Characterization约 600 词负载特征五个基本属性怎么合起来描述负载;后面两张值得照着问的清单。
  31. Latency Analysis约 300 词延迟分析在栈的各层量延迟,找出慢在哪一层。
  32. Static Performance Tuning约 300 词静态性能调优一张配置核对清单:盘、控制器、RAID、固件、驱动。
  33. Cache Tuning约 100 词缓存调优指向第 2 章的缓存调优。
  34. Resource Controls约 100 词资源控制按进程限 IOPS 或吞吐。
  35. Micro-Benchmarking约 400 词微基准测试测盘和控制器上限时,IO 大小、偏移、并发怎么配。
  36. Scaling约 300 词扩展按目标负载算要几块盘、几个控制器;别按 100% 算。
  37. Observability Tools约 100 词观测工具工具总表:传统工具、跟踪工具、控制器工具。
  38. iostat约 1200 词iostat-x 的每一列,先看哪几列;为什么要把读写分开看。
  39. sar约 200 词sar能看历史的磁盘统计;svctm 为什么被删了。
  40. PSI约 100 词PSIIO 压力的趋势,适合做告警。
  41. pidstat约 200 词pidstat按进程看读写量和 iodelay。
  42. perf约 400 词perf记录块层跟踪点和调用栈,找发 IO 的代码。
  43. biolatency约 300 词biolatency延迟直方图,找双峰;按 IO 标志拆开。
  44. biosnoop约 500 词biosnoop每个 IO 一行;从开始时间看出排队,按延迟排序找异常值。
  45. iotop, biotop约 400 词iotop 与 biotop按进程排序的磁盘 top;iotop 的计数要先验证。
  46. biostacks约 100 词biostacks带调用栈的块 IO 延迟,找不是读写引起的 IO。
  47. blktrace约 500 词blktrace每个 IO 的各个阶段;btt 统计各阶段耗时。
  48. bpftrace约 600 词bpftrace现场写单行命令看 IO 大小、类型、错误、延迟。
  49. MegaCli约 200 词MegaCli只针对 LSI 控制器;换成你的控制器厂商的工具。
  50. smartctl约 100 词smartctl盘自己报的健康数据,用来预测故障。
  51. SCSI Logging约 100 词SCSI 日志调试 SCSI 错误时才用,打开后会刷满系统日志。
  52. Other Tools约 100 词其他工具BPF 书里的更多磁盘工具和 /proc/diskstats。
  53. Visualizations约 100 词可视化几种图的引子。
  54. Line Graphs约 100 词折线图平均值会藏住双峰和单块盘的异常。
  55. Latency Scatter Plots约 200 词延迟散点图每个 IO 一个点,看异常值和读写的关系。
  56. Latency Heat Maps约 300 词延迟热图事件多到散点图看不清时用热图。
  57. Offset Heat Maps约 100 词偏移热图看访问位置的老方法,SSD 上意义不大。
  58. Utilization Heat Maps约 100 词利用率热图成规模的机器才用得上,单机排查用不到。
  59. Experimentation约 100 词实验压测时让 iostat 一直开着对照。
  60. Ad Hoc约 200 词临时测试用 dd 测顺序读写;direct 标志绕过缓存。
  61. Custom Load Generators约 100 词自己写负载C 程序加 O_DIRECT 造自己想要的负载。
  62. Micro-Benchmark Tools约 100 词微基准工具hdparm 测缓存读和盘读。
  63. Random Read Example约 100 词随机读的例子并发一级级加,看队列和延迟一起涨。
  64. ioping约 100 词ioping像 ping 一样测单次 IO 延迟,负载很轻,生产上也能用。
  65. fio约 100 词fio指向第 8 章;用 direct 模式测盘。
  66. blkreplay约 100 词blkreplay回放 blktrace 记录的负载,专门场景才用。
  67. Tuning约 100 词调优默认值通常够用,先去掉不必要的 IO。
  68. Operating System Tunables约 300 词操作系统的可调项ionice、cgroup 的 IO 限额、调度器和预读参数。
  69. Disk Device Tunables约 100 词磁盘设备的可调项hdparm 的设置,多是电源管理,有的选项会丢数据。
  70. Disk Controller Tunables约 100 词磁盘控制器的可调项随厂商和型号而定,看你的控制器文档。
  71. Exercises约 200 词习题习题;动手部分本页已经给了替代。

词与短语

只收先读和用到再查两档的节。斜体那一行是原句里连续的几个词,在电子书里搜它就能找到那一句。

§0 开篇

by orders of magnitude
… and application throughput by orders of magnitude.
成数量级地(提高)
一个数量级是十倍。去掉磁盘瓶颈,提升可以是十倍、百倍。
not immune to
… devices are not immune to performance issues.
也免不了(性能问题)
immune 本义是免疫。意思是换了闪存也不等于从此没有性能问题。
perils
Understand the perils of interpreting disk utilization.
陷阱,风险
作者把「读利用率时容易掉的坑」单独列为一条学习目标。

§9.2.1 Simple Disk

grocery store checkout
… similar to a grocery store checkout, where …
超市收银台

§9.2.2 Caching Disk

counter-term
The counter-term is the write-through cache …
与之相对的说法
不是「反对的术语」。回写(write-back)的对应词是直写(write-through)。
coupled with
… write-back caches are often coupled with batteries …
配上,搭配着
回写缓存配电池,断电时还能把缓存里的数据写下去。

§9.3.2 Time Scales

in great numbers
… become an issue only in great numbers …
数量多了才(成问题)
great 修饰 numbers,是「数量庞大」,不是「了不起」。每个都很快的 IO,积少成多也会变慢。
tolerance
… there is greater tolerance for high latencies …
容忍度
web-facing
… especially in web-facing applications that already …
直接面向网络用户的

§9.3.7 IOPS Are Not Equal

not created equal
… IOPS are not created equal and …
不能一视同仁
化用「人人生而平等」(all men are created equal)。意思是两个 IOPS 数字不能直接比大小。
on its own
… value on its own doesn’t mean …
单拿出来看
不是「靠它自己」。光一个 IOPS 值说明不了什么。

§9.3.9 Utilization

disguised
… which can be disguised when summarizing over …
被掩盖
区间取得越长,突发越容易被平均掉。
counterintuitive
… on the actual disks (and is counterintuitive):
反直觉的

§9.3.10 Saturation

suffer from
Any interval summary can suffer from similar issues.
有(同样的)毛病
不是「受苦」。任何按区间汇总的指标都有被平均掉的问题。

§9.3.11 I/O Wait

comes along
… If another CPU-hungry process comes along …
(又)冒出来
CPU-hungry 是「吃 CPU 的」。来了一个计算密集的进程,iowait 就被挤掉了。
revealing
… uses fewer CPU cycles, revealing I/O wait.
(把本来就有的 iowait)露出来
原文用斜体强调:iowait 不是新出现的,是 CPU 闲下来以后被看见了。
pain endured
… captures the pain endured by application threads …
(应用线程)吃的苦头
pain 在性能语境里常指用户或线程实际承受的等待,不是夸张的修辞。

§9.4.1 Disk Types

went viral
… where it went viral, and it …
(视频)疯传
作者对着磁盘阵列大喊、引起慢 IO 的那段视频。
sloth disks
… what has been called sloth disks.
树懒盘
sloth 是树懒。指偶尔慢到一秒以上、却不报任何错误的机械盘。
nuisance
Sloth disks are a nuisance, especially when
让人头疼的东西

§9.4.4 Operating System Disk I/O Stack

CPU-talk
… (noop is CPU-talk for no-operation) …
CPU 圈子的行话
noop 借自指令集里的「空操作」指令。
jump the queue
… as newly issued I/O jump the queue …
插队
英式说法,美式多说 cut in line。新来的 IO 一直插队,老的就一直等。
writes starving reads
… due to writes starving reads, and …
写把读饿着了
starve 在调度里是「一直分不到资源」,不是「挨饿」的字面意思。

§9.5.2 USE Method

albeit
… system functions correctly—albeit more slowly—in spite …
尽管,虽然
盘坏了,冗余配置下系统照样能跑,只是慢了,所以错误常被忽略。
levels off
… IOPS always levels off at a certain rate …
到某个值就平了
不是「关掉」。换什么负载都卡在同一个数,说明上限可能在控制器或总线。

§9.5.4 Workload Characterization

Better still
… Better still, examine the full distribution …
更好的办法是
still 在这里加强比较级,不是「仍然」。

§9.5.5 Latency Analysis

drilling deeper
… involves drilling deeper into the system …
一层层往下钻
safe to assume
… it’s usually safe to assume that the …
可以放心地认为
safe 不是「安全」。注意前面的 usually:作者给的是经验判断,不是定论。

§9.5.10 Scaling

Add more spindles
We used to say, “Add more spindles.”
加盘
spindle 是机械盘的主轴,代指一块机械盘。作者接着改口:现在该说「加闪存」。

§9.6.1 iostat

negligible
… this tool is considered negligible.
可以忽略不计
这些计数器内核默认就在维护,iostat 只是去读。
might have been better to
… it might have been better to call …
当初要是……就好了
虚拟语气,作者调侃 iostat 这个名字:它只报磁盘 IO,叫 diskiostat 更不容易误会。
otherwise
… otherwise all USE method metrics could be …
要不然(就能……了)
话是反着说的:iostat 不报磁盘错误,所以 USE 的三样不能在一个工具里查齐。

§9.6.6 biolatency

sounds like
… this sounds like queueing on the device.
看起来像是
不是「听起来」。作者的判断带着保留:最慢的那批 IO 像是在盘上排了队。

§9.6.7 biosnoop

in turn
… and then completed in turn, with …
依次,一个接一个
一批同时发出的写在盘上排队,一个个完成,所以延迟逐个变长。
de-scheduled
… can be de-scheduled by the hypervisor during …
被宿主调度下去
虚拟机在 IO 进行中被宿主暂停,这段时间也算进了 IO 的延迟。

§9.6.8 iotop, biotop

undercounts
… it greatly undercounts write workloads.
少算
作者的建议:用已知的负载验证一下工具的数对不对。

§9.9 Tuning

rarely need much adjusting
… reasonable and rarely need much adjusting.
很少需要大调

诊断题:换成你来查

场景和输出都是本站编的。先自己判断,再点选项看解释。

盘到顶了吗?§9.3.9

场景:一台服务器跑你的 C++ 键值服务,数据在一块 NVMe SSD 上,读路径是一个线程同步 pread。高峰时你截了一秒的 iostat:

Device    r/s   rareq-sz  r_await  aqu-sz  %util
nvme0n1  11800      4.0     0.08    0.96   99.8

aqu-sz 约等于 r/s 乘 r_await(11800 × 0.08 ms ≈ 0.94),说明盘上基本只有一个读:瓶颈是这一个线程串行地等,不是盘。%util 99.8% 只说明这一秒里一直有一个 IO 挂着。下一步:读路径改成多线程或异步 IO,再看 IOPS 涨不涨、r_await 动不动;也可以在空闲时用 fio 测一下这块盘在高并发下的上限当基准。0.08 ms 对 NVMe 本来就是正常的读延迟。

这组数说明什么,先做什么?§9.3.7

场景:一台老归档机,数据在一块 7200 转机械盘上,一个查询服务按键随机读 4 KB 记录。用户说查询越来越慢:

Device  r/s  rkB/s  rrqm/s  rareq-sz  r_await  aqu-sz  %util
sdb     160    640    0.00       4.0     38.5     6.2  100.0

平均大小 4 KB、没有合并,是随机小读;每次要寻道加等旋转,一块 7200 转的盘每秒也就一两百次。aqu-sz 6.2 说明盘上加队列里平均有六个左右,await 38 ms 里大半是排队(6.2 ÷ 160 ≈ 39 ms,Little 定律)。吞吐低正是因为 IO 小,IOPS 不能拿吞吐来衡量。预读对随机读没用,只会多读用不上的数据。盘坏也会慢,值得顺手看一眼 smartctl,但这组数和随机负载完全吻合。

iowait 降了,盘变好了吗?§9.3.11

场景:8 核服务器。运维在凌晨 2 点加了一个日志压缩任务。监控上 iowait 从 25% 掉到 3%,同事说「压缩把盘的问题解决了」,可服务读请求的延迟没变:

time    %usr  %iowait  %idle
01:59    6.0     25.1   68.2
02:01   71.4      3.2   24.9

iowait 是 CPU 空闲时间的一部分。压缩任务把 CPU 占到七成,CPU 不再空闲,iowait 自然变小,而原来等盘的线程照样在等。这正是书里说 iowait 容易误导的情形。要量应用到底被盘挡了多久,看 /proc/pressure/io、vmstat 的 b 列,或者用 biolatency 看盘的延迟分布。

平均延迟不高,偶尔有读卡几十毫秒,下一步?§9.6.7

场景:一台日志服务器,SATA SSD。iostat 里 r_await 平均 1.2 ms,可应用每隔几秒就有一批读超过 30 ms。你跑了十秒 biolatency -Fm:

flags = Read
  msecs     : count
  0 -> 1    : 9120
  2 -> 3    : 210
  32 -> 63  : 41
flags = Write
  0 -> 1    : 380
  2 -> 3    : 2950
  4 -> 7    : 3300

直方图上读有一小撮落在 32–63 ms,写的量又大又慢,平均值把这些都抹平了。书里的散点图例子就是写突发之后出现读的异常值,作者推测读排在了写后面(§9.7.2)。下一步用 biosnoop-bpfcc -Q 存一段,按延迟排序,看慢读之前的几十毫秒里发生了什么。确认是写突发,再考虑让脏页更早、更平稳地刷出去,或者换能限制写饿死读的调度器(§9.4.4);换调度器是可以试的方向,但「一定」说过头了。

板子上的日志怎么改最有效?§9.3.6

场景:一块 ARM 板,日志写在 eMMC 上。程序每条日志(约 200 字节)write 一次再 fsync 一次。用 ds.sh 看了一秒:

mmcblk0:  w/s 620  await 3.10 ms  aqu-sz 1.9  %util 97.0

每次 fsync 至少落一次 4 KB 的数据写,加上文件系统日志,几百字节的日志变成了每秒六百多次小写。闪存怕的正是小块写(写放大、擦除),IOPS 先到顶(§9.4.1)。攒批是从源头减少 IO 次数,也能延长 eMMC 的寿命;代价是断电时可能丢最后一批,要按业务定间隔。O_DIRECT 不减少次数,还要求对齐;调度器和预读都和这里无关。

值得停下来的句子

The best disk I/O performance is none at all.— §9.3.3。本站译:最好的磁盘 IO 性能,是根本不发生 IO。

这句话放在缓存一节的开头,却是整章的底色。前面的工具能告诉你盘忙不忙、慢在哪,但最大的收益常常来自让 IO 消失:命中缓存、攒批写、去掉不必要的 fsync、别让日志和数据抢同一块盘。负载特征那一节也说,看清施加的负载,可能找到可以整个砍掉的工作(§9.5.4)。查到盘是瓶颈之后,先问「这些 IO 必须发吗」,再问「怎么让盘更快」。

bear in mind that it is only a measure of busyness (non-idle time) and may mean little for virtual devices backed by multiple disks.— §9.6.1。本站译:要记住,它(%util)只衡量忙碌,也就是非空闲的时间;对背后有多块盘的虚拟设备,它可能说明不了什么。

书里这句说的是虚拟设备,道理对 SSD 一样成立:只要设备能同时处理多个 IO,「一直有 IO 在飞」和「接不下更多了」就是两回事。监控面板上最显眼的往往就是 %util,告警阈值也常设在它上面。读懂这句话,就不会因为一块 NVMe 的 %util 到了 100% 而急着换盘,而会去看 await 有没有涨、队列有没有堆起来。

带走的几条

一句话:先确认应用在等盘,再按盘看延迟和排队,最后用延迟分布和逐个 IO 找到异常值和发起者。

先看 await 和 aqu-sz

iostat -sxz 1。await 是每次 IO 的总时间,aqu-sz 是队列长度;%util 放在最后看。

§9.6.1
SSD 的 100% 不等于满

%util 只算有 IO 在飞的时间。能并行的设备,看 await 是否随负载涨、aqu-sz 和压测上限比。

§9.3.9
读写分开看

iostat -dx 1。写多被回写缓存吸收,r_await 通常更直接关系到应用的延迟。

§9.6.1
IOPS 要带着大小说

吞吐 = IOPS × 大小。rareq-sz 小、合并为零是随机小 IO;机械盘每秒一两百次就到顶。

§9.3.7
iowait 不是盘的指标

CPU 一忙它就降。看 /proc/pressure/io 和 vmstat 的 b 列,量线程被 IO 挡住的时间。

§9.3.11
平均值藏住异常值

biolatency 看分布找双峰,biosnoop 逐个找慢 IO,看它前面是不是刚来过一批写。

§9.6.6
测盘要绕过缓存

dd 加 oflag=direct 或 fio --direct=1,同时开着 iostat 核对;虚拟机里的数不代表真机。

§9.8.1