「Linux」- 磁盘 I/O(学习笔记)

解决方案

存储系统的 I/O ,通常是整个系统中最慢的一环。所以, Linux 通过多种缓存机制来优化 I/O 效率:
1)比方说,为了优化文件访问的性能,会使用页缓存、索引节点缓存、目录项缓存等多种缓存机制,以减少对下层块设备的直接调用;
2)为了优化块设备的访问效率,会使用缓冲区,来缓存块设备的数据

磁盘性能指标(常见指标)

不要孤立地去比较某一指标,而要结合读写比例、I/O 类型(随机还是连续)以及 I/O 的大小综合来分析;
1)在数据库、大量小文件等这类随机读写比较多的场景中,IOPS 更能反映系统的整体性能;
2)而在多媒体等顺序读写较多的场景中,吞吐量才更能反映系统的整体性能;

Bandwidth(吞吐量)

指每秒的 I/O 请求大小

Latency(响应时间)

指 I/O 请求从发出到收到响应的间隔时间

IOPS(每秒请求数)

指每秒的 I/O 请求数量

使用率

指磁盘处理 I/O 的时间百分比。过高的使用率(比如超过 80%),通常意味着磁盘 I/O 存在性能瓶颈;

注意,使用率只考虑有没有 I/O,而不考虑 I/O 的大小。换句话说,当使用率是 100% 的时候,磁盘依然有可能接受新的 I/O 请求;

饱和度(Saturation Level)

指磁盘处理 I/O 的繁忙程度;

过高的饱和度,意味着磁盘存在严重的性能瓶颈。当饱和度为 100% 时,磁盘无法接受新的 I/O 请求;

磁盘 I/O 性能(基准测试)

推荐用性能测试工具 fio ,来测试磁盘的 IOPS、吞吐量以及响应时间等核心指标。但还是那句话,因地制宜,灵活选取。在基准测试时,一定要注意根据应用程序 I/O 的特点,来具体评估指标;

并且需要我们测试出,不同 I/O 大小(一般是 512B 至 1MB 中间的若干值)分别在随机读、顺序读、随机写、顺序写等各种场景下的性能情况;

用性能工具得到的这些指标,可以作为后续分析应用程序性能的依据。一旦发生性能问题,你就可以把它们作为磁盘性能的极限值,进而评估磁盘 I/O 的使用情况;

磁盘 I/O 观测

容易被误读的 iostat | Linux Performance
深入理解 iostat

iostat,来自 /proc/diskstats 文件,而 /proc/diskstats 并不提供存储设备性能的数据。其显示的数据包含在队列中等待的时间,所以并不能直接反馈存储设备性能;

# iostat -d -x 1                                                                // -d -x 表示显示所有磁盘 I/O 的指标
Linux 4.19.0-10-amd64 (laptop)  08/10/2020      _x86_64_        (4 CPU)

Device     r/s     w/s     rkB/s     wkB/s   rrqm/s   wrqm/s  %rrqm  %wrqm r_await w_await aqu-sz rareq-sz wareq-sz  svctm  %util
mmcblk0   0.00    0.00      0.07      0.73     0.00     0.00   0.00   3.42    2.03   46.85   0.00    18.60   472.42   5.88   0.00
sda       4.52   11.59    125.31    169.50     0.69     8.18  13.17  41.39    0.65    1.86   0.01    27.73    14.63   0.19   0.31
sdb       0.00    0.00      0.11      0.00     0.00     0.00   0.00   0.00    4.48    3.73   0.00    27.04     0.00   4.23   0.00
dm-0      0.87    1.95     25.49     13.11     0.00     0.00   0.00   0.00    0.64    1.12   0.00    29.37     6.71   0.56   0.16
loop0     0.00    0.00      0.01      0.00     0.00     0.00   0.00   0.00    0.54    0.00   0.00    35.09     0.00   0.32   0.00
loop1     0.00    0.00      0.01      0.00     0.00     0.00   0.00   0.00    0.62    0.00   0.00    21.96     0.00   0.21   0.00

// Device,磁盘设备的名字

// rrqm/s,每秒合并的读请求数,
// wrqm/s,每秒合并的写请求数,

// %rrqm,%orrqm 表示合并读请求的百分比
// %wrqm,%wrqm 表示合并写请求的百分比
// Linux 常见的调度算法有: none noop deadline cfq

// aqu-sz,
// 平均请求队列长度,旧版中为 avgqu-sz
// 该值越大,表示排队等待处理的 IO 越多;

// areq-sz(avgrq-sz),旧字段
// avgrq-sz,反应用户的 IO 模式:用户过来的 IO 是大 IO 还是小 IO
// rareq-sz,平均读请求大小,单位为 kB
// wareq-sz,平均写请求大小,单位为 kB

// svctm,废弃,处理 I/O 请求所需的平均时间(不包括等待时间),单位为毫秒;
// 这个值不是独立的,它是根据其他值计算出来的。注意该字段是推断的数据,并不保证完全准确

在观测指标时,需要结合请求的大小( rareq-sz 和 wareq-sz)一起分析;

Bandwidth

参考如下两个指标:
1)rkB/s,每秒从磁盘读取的数据量
2)wkB/s,每秒向磁盘写入的数据量

Latency

参考如下两个指标:
1)r_await,读请求处理完成等待时间,包括队列中的等待时间和设备实际处理的时间,单位为 ms;
2)w_await,写请求处理完成等待时间,包括队列中的等待时间和设备实际处理的时间,单位为 ms;

await 是单个 I/O 所消耗的时间,包括:
1)I/O 请求在 kernel 队列中等待的时间(正常情况,队列等待时间可以忽略不计,姑且把 await 当作衡量硬盘速度的指标);
2)硬盘设备处理 I/O 的时间;

要根据应用场景来判断 await 是否正常:
1)如果 I/O 模式很随机、I/O 负载比较高,会导致磁头乱跑,寻道时间长,那么相应地 await 要估算得大一些;
2)如果 Seq. I/O 模式,只有单进程产生 I/O 负载,那么寻道时间和旋转延迟都可忽略不计,主要考虑传输时间,相应地 await 就应该很小,甚至不到 1ms;

IOPS

参考如下两个指标:
1)r/s,每秒发送给磁盘的读请求数,合并后的请求数
2)w/s,每秒发送给磁盘的写请求数,合并后的请求数

使用率(%util)

iostat 中的 %util 指标说明 – 腾讯云开发者社区-腾讯云

磁盘 IO 使用率,参考 %util 指标,%util 表示该设备有 I/O(即非空闲)的时间比率,不考虑 I/O 有多少,只考虑有没有;
向设备发出 I/O 请求所用的时间百分比(设备的带宽利用率)。当该值接近 100% 时,设备饱和;换成公式的话,就是: 请求时间 /(请求时间+io 处理时间)

但是 iostat 的 %util 基本已经没有任何作用,原因是:现代硬盘设备都有并行处理多个 I/O 请求的能力,所以 %util==100% 也不意味着设备饱和了;
iostat(1) 没有可以衡量硬盘设备的饱和程度的指标;磁盘是否达到真正极限瓶颈,需要参考通过 fio 等工具压测出的极限带宽和 IOPS 值;

假如,某硬盘处理单个 I/O 需要 0.1 秒,有能力同时处理 10 个 I/O 请求,那么:
1)当 10 个 I/O 请求依次顺序提交的时候,需要 1 秒才能全部完成,在 1 秒的采样周期里 %util 达到 100%;
2)而如果 10 个 I/O 请求一次性提交的话,0.1 秒就全部完成,在 1 秒的采样周期里 %util 只有 10%;
3)可见,即使 %util 高达 100%,硬盘也仍然有可能还有余力处理更多的 I/O 请求,即没有达到饱和状态;

饱和度

从 iostat 并不能直接得到磁盘饱和度。事实上,饱和度通常也没有其他简单的观测方法,
不过,可以把观测到的,平均请求队列长度或者读写请求完成的等待时间,跟基准测试的结果(比如通过 fio 测试)进行对比,综合评估磁盘的饱和情况;

进程 I/O 观测

使用 pidstat 和 iotop 这两个工具:

pidstat

通过 -d 选项,可以查看进程的 I/O 情况:

# pidstat -d 1
Linux 4.19.0-10-amd64 (laptop)  08/10/2020      _x86_64_        (4 CPU)

06:21:39 PM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
06:21:40 PM     0       608      0.00      3.92      0.00       1  jbd2/dm-0-8
06:21:40 PM 64055      2158      0.00      3.92      0.00       0  qemu-system-x86
06:21:40 PM  1001      2986      0.00      3.92      0.00       0  thunderbird-bin

06:21:40 PM   UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
06:21:41 PM  1001      2986      0.00      4.00      0.00       0  thunderbird-bin

// 用户 ID(UID)和进程 ID(PID)
// 每秒读取的数据大小(kB_rd/s) ,单位是 KB
// 每秒发出的写请求数据大小(kB_wr/s) ,单位是 KB
// 每秒取消的写请求数据大小(kB_ccwr/s) ,单位是 KB
// 块 I/O 延迟(iodelay),包括等待同步块 I/O 和换入块 I/O 结束的时间,单位是时钟周期;

iotop

类似于 top 的工具,你可以按照 I/O 大小对进程排序:

# iotop
Total DISK READ:         0.00 B/s | Total DISK WRITE:        31.97 K/s
Current DISK READ:       0.00 B/s | Current DISK WRITE:       8.72 K/s
  TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND
  943 be/3 root        0.00 B/s    0.00 B/s  0.00 %  0.30 % [jbd2/dm-3-8]
18608 be/4 libvirt-    0.00 B/s   11.63 K/s  0.00 %  0.00 % qemu-system-x86_64
18458 be/4 libvirt-    0.00 B/s    2.91 K/s  0.00 %  0.00 % qemu-system

// 前两行分别表示,进程的磁盘读写大小总数和磁盘真实的读写大小总数。因为缓存、缓冲区、I/O 合并等因素的影响,它们可能并不相等
// 剩下的部分是从各个角度来分别表示进程的 I/O 情况,包括线程 ID、I/O 优先级、每秒读磁盘的大小、每秒写磁盘的大小、换入和等待 I/O 的时钟百分比等

参考文献

24 | 基础篇:Linux 磁盘 I/O 是怎么工作的(上)