03 磁盘好坏它说了算 - 磁盘与 FIO
磁盘的分类
有多种磁盘分类标准,按磁盘存储实现技术分类,可划分为:
- 机械硬盘:
- 使用旋转的磁盘和移动的读写头来存储和检索数据;
- 其优点在于存储容量大、价格相对较低;
- 其劣势在于容易受到物理冲击和振动的影响,寿命有限;
- 固态硬盘:
- 使用闪存芯片来存储数据;
- 相对机械硬盘具有更快的读写速度,并且更可靠、更耐用;
- 价格相对机械硬盘更高;
SATA 和 NVMe
SSD 有两种常用的接口形式,分别是 SATA(Serial Advanced Technology Attachment) 和 NVMe(Non-Volatile Memory Express),两者主要区别包括:
- 传输速度:SATA 接口传输速度最大约 600MB/s,NVMe 可达数 GB/s;
- 读写延时:NVMe 接口的读写延时远低于 SATA 接口;
- 习惯上,SSD 一般指的是 SATA SSD,一般称 NVMe SSD 为 NVMe
磁盘的评价体系
核心评价指标:
- 容量、尺寸、接口等静态指标;
- IOPS - Input/Output Operations Per Second,每秒读写次数,是随机读写的评价指标;
- BW - BandWidth,读写带宽,一般用来衡量磁盘顺序读写的性能;
- Latency - 读写延时,指发送 I/O 请求和接收响应之间的间隔时间;
fio BenchMark
fio(Flexible I/O Tester)是一个开源的 I/O 压力测试和基准测试工具,它可以用来测试存储设备(如硬盘、SSD、网络存储等)的性能,特别是存储设备如硬盘、固态硬盘(SSD)、存储阵列等。
bash
# 安装 fio 工具
yum install fio -y
# 在 /dev/sdb1 设备上执行一个 20 秒的随机读取测试,使用异步 I/O 引擎进行 4KB 大小的读取操作,测试时不使用操作系统的缓存,并且使用 64 个并发线程,每个线程同时处理 64 个 I/O 请求。测试完成后,将输出整体的性能报告。
# fio -filename=/dev/sdb1 -iodepth=64 -ioengine=libaio -direct=1 -rw=randread -bs=4 numjobs=64 -runtime=20 -group_reporting -name=test-rand-read| 参数 | 说明 |
|---|---|
-filename | 测试设备,可为文件系统(如 /dev/sdb1)、裸设备(如 /dev/sdb)和文件(/home/test.bin) |
-iodepth | 测试时的 IO 队列深度,在测试延时的时候,该值一般为 1;测试 IOPS 和 BW 时,该值一般较大 |
-ioengine | 测试所用的 I/O 引擎,通常为 libaio,还可能为 psync 等 |
-direct | 为 1 表明绕过机器自带的 buffer,测试结果更真实 |
-rw | 一般为顺序读/写/读写(read/write/readwrite)、随机读/写/读写(randread/randwrite/randrw) |
-bs | 单次 IO 测试的块文件大小,测试 Latency 和 IOPS 时,该值一般为 4k;测试 BW 时,该值一般为 128k |
-numjobs | 测试过程 fio 线程数 |
-runtime | 测试时间 |
-group_reporting | 启用分组报告模式。fio 将汇总所有 job 的结果,并以一个整体的方式输出统计信息。 |
-name | 本次测试的名称 |
注意
写测试会覆盖磁盘文件,请勿在有数据的磁盘上进行写测试
延时测试
bash
fio --ioengine=libaio --numjobs=1 --runtime=120 --direct=1 --time_based --group_reporting --ramp_time=0 --size=20G --bs=4k --iodepth=1 --filename=/dev/vdb --name=4k_randwrite --rw=randwrite
# [实际场景会测试顺序读、顺序写、顺序读写、随机读、随机写、随机读写等多类型]--ioengine=libaio: 使用异步 I/O 引擎libaio,即通过内核的异步 I/O 接口进行操作。--numjobs=1: 启动 1 个作业(job)。在此配置中,只会有一个工作线程。--runtime=120: 运行时间为 120 秒。这是测试的总持续时间。--direct=1: 启用直接 I/O,绕过操作系统缓存,直接对设备进行 I/O 操作。--time_based: 表示测试基于时间,而不是 I/O 总量。在指定时间内持续运行测试。--group_reporting: 启用分组报告模式,汇总输出所有作业的结果。--ramp_time=0: 没有指定热身时间,测试开始后立即进行计时。--size=20G: 指定测试文件大小为 20 GB。在随机写测试中,将在这个范围内随机写数据。--bs=4k: 块大小为 4 KB,每个 I/O 操作写入 4 KB 的数据。--iodepth=1: 指定 I/O 深度为 1,意味着在任何给定时间,只有一个未完成的 I/O 操作。--filename=/dev/vdb: 指定测试设备为/dev/vdb,测试将在这个设备上执行。--name=4k_randwrite: 为测试作业指定名称为4k_randwrite。--rw=randwrite: 进行随机写操作。
bash
4k_randwrite: (g=0): rw=randwrite, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=1
fio-3.19
Starting 1 process
4k_randwrite: Laying out IO file (1 file / 20971520MiB)
4k_randwrite: (groupid=0, jobs=1): err= 0: pid=12345: Tue Jul 31 15:34:56 2024
write: IOPS=2500, BW=9.77MiB/s (10.2MB/s)(1170MiB/120010msec); 0 zone resets
slat (usec): min=10, max=145, avg=15.50, stdev= 3.20 # 一般查看这三行里的 avg 数据
clat (usec): min=50, max=750, avg=100.50, stdev= 15.20
lat (usec): min=70, max=780, avg=116.00, stdev= 12.10
clat percentiles (usec):
| 1.00th=[ 60], 5.00th=[ 70], 10.00th=[ 80], 20.00th=[ 90],
| 30.00th=[ 95], 40.00th=[ 100], 50.00th=[ 105], 60.00th=[ 110],
| 70.00th=[ 120], 80.00th=[ 130], 90.00th=[ 140], 95.00th=[ 150],
| 99.00th=[ 170], 99.50th=[ 200], 99.90th=[ 250], 99.95th=[ 300],
| 99.99th=[ 400]
lat (nsec) : 1000=0.00%
lat (usec) : 2=0.01%, 4=0.02%, 10=0.05%, 20=0.10%
cpu : usr=0.3%, sys=0.5%, ctx=292500, majf=0, minf=10
IO depths : 1=100.0%, 2=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=0.0%
submit: 0=0.0%, complete: 0=0.0%, issued: total=r=0/w=292500/d=0, short=0, dropped=0
latency: target=0, window=0, percentile=100.00%, depth=1
Run status group 0 (all jobs):
WRITE: bw=9.77MiB/s (10.2MB/s), 9.77MiB/s-9.77MiB/s (10.2MB/s-10.2MB/s), io=1170MiB (1227MB), run=120010-120010msec- 基本信息:
rw=randwrite: 测试是随机写操作。bs=4K: 每个 I/O 操作写入 4 KB 数据。iodepth=1: I/O 深度为 1,表示同一时间只有一个未完成的 I/O 请求。
- 吞吐量和 IOPS:
write: IOPS=2500: 每秒 2500 次 I/O 操作。BW=9.77MiB/s (10.2MB/s): 带宽为 9.77 MiB/s,相当于 10.2 MB/s。在整个测试期间,总共写入了 1170 MiB 数据。
- 延迟统计:
slat (usec): Submission Latency, 提交延迟(从任务请求发出到任务开始执行的时间)平均为 15.50 微秒,最大 145 微秒。clat (usec): Completion Latency, 完成延迟(从任务请求发出到任务完成的时间)平均为 100.50 微秒,最大 750 微秒。lat (usec): 总延迟(从请求发出到完成的总时间)平均为 116.00 微秒,最大 780 微秒。- 延迟分布:
- 百分位数提供了延迟的分布情况。例如,99% 的 I/O 操作在 170 微秒内完成,99.99% 的操作在 400 微秒内完成。
- CPU 使用率:
usr=0.3%: 用户态 CPU 使用率为 0.3%。sys=0.5%: 系统态 CPU 使用率为 0.5%。
- I/O 深度分布:
- 100% 的时间内 I/O 深度为 1,这与命令中指定的
iodepth=1一致。
- 100% 的时间内 I/O 深度为 1,这与命令中指定的
- 这个测试结果展示了设备
/dev/vdb在 4 KB 随机写操作中的性能表现。测试中,平均写带宽为 9.77 MiB/s,IOPS 为 2500。平均延迟为 116 微秒,最大延迟为 780 微秒。延迟分布表明,大多数 I/O 操作的延迟在 200 微秒以下,这对于了解设备在低 I/O 深度和高延迟敏感性的应用中的表现非常有帮助。
带宽测试
bash
fio --ioengine=libaio --numjobs=1 --runtime=120 --direct=1 --time_based --group_reporting --ramp_time=0 --size=20G --bs=128k --iodepth=128 --filename=/dev/vdb --name=128k_randwrite --rw=randwrite
# [实际场景会测试顺序读、顺序写、顺序读写、随机读、随机写、随机读写等多类型]--bs=128k: 块大小设置为 128 KB。每次 I/O 操作会写入 128 KB 的数据。--iodepth=128:指定 I/O 深度为 128,这表示在任意时刻,最多可以有 128 个未完成的 I/O 操作。这个参数对于测试高并发的 I/O 设备性能非常重要。
bash
128k_randwrite: (g=0): rw=randwrite, bs=(R) 128KiB-128KiB, (W) 128KiB-128KiB, ioengine=libaio, iodepth=128
fio-3.25
Starting 1 process
128k_randwrite: Laying out IO file (1 file / 0KiB)
128k_randwrite: (groupid=0, jobs=1): err= 0: pid=12345: Wed Jul 31 10:15:42 2024
write: IOPS=2202, BW=275MiB/s (288MB/s)(32.2GiB/120000msec); 0 zone resets
slat (nsec): min=400, max=217.2k, avg=714.80, stdev=733.23
clat (usec): min=430, max=1474, avg=58.28, stdev=23.97
lat (usec): min=432, max=1474, avg=59.00, stdev=23.99
clat percentiles (usec):
| 1.00th=[ 44], 5.00th=[ 47], 10.00th=[ 49], 20.00th=[ 51],
| 30.00th=[ 52], 40.00th=[ 54], 50.00th=[ 56], 60.00th=[ 59],
| 70.00th=[ 62], 80.00th=[ 66], 90.00th=[ 75], 95.00th=[ 84],
| 99.00th=[ 106], 99.50th=[ 118], 99.90th=[ 155], 99.95th=[ 178],
| 99.99th=[ 279]
bw ( KiB/s): min=278240, max=291072, per=100.00%, avg=281477.20, stdev=3243.90, samples=239 # 一般查看平均 BandWidth,若为混合读写,则有两个值
iops : min= 2173, max= 2274, avg=2198.90, stdev=25.34, samples=239
lat (usec) : 500=99.60%, 750=0.38%, 1000=0.01%
cpu : usr=3.51%, sys=5.34%, ctx=164058, majf=0, minf=42
IO depths : 1=0.0%, 2=0.1%, 4=0.2%, 8=0.5%, 16=1.2%, 32=2.3%, >=64=95.7%
submit : 0=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.1%, >=64=99.9%
complete : 0=0.0%, 4=0.0%, 8=0.0%, 16=0.1%, 32=0.5%, >=64=99.3%
issued rwts: total=0,257795,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=128- IOPS: 每秒的输入输出操作数。这里显示随机写的 IOPS 为 2202。
- BW: 带宽,表示数据传输速率。此处为 275MiB/s。
- lat (usec): 延迟,分为提交延迟 (
slat),完成延迟 (clat),和总体延迟 (lat)。延迟分布显示在不同百分位的延迟情况。 - cpu: CPU 使用率,包括用户态和内核态的比例。
- IO depths: IO 深度的分布情况。
IOPS 测试
bash
fio --ioengine=libaio --numjobs=1 --runtime=120 --direct=1 --time_based --group_reporting --ramp_time=0 --size=20G --bs=4k --iodepth=128 --filename=/dev/vdb --name=4k_randwrite --rw=randwrite
# [实际场景会测试顺序读、顺序写、顺序读写、随机读、随机写、随机读写等多类型]--bs=4k: 块大小为 4KB。--iodepth=128: I/O 深度为 128。
bash
4k_randwrite: (g=0): rw=randwrite, bs=(R) 4KiB-4KiB, (W) 4KiB-4KiB, ioengine=libaio, iodepth=128
fio-3.25
Starting 1 process
4k_randwrite: Laying out IO file (1 file / 0KiB)
4k_randwrite: (groupid=0, jobs=1): err= 0: pid=23456: Wed Jul 31 11:15:42 2024
write: IOPS=120000, BW=469MiB/s (492MB/s)(55.0GiB/120001msec); 0 zone resets
slat (nsec): min=500, max=232.8k, avg=1500.40, stdev=1100.23
clat (usec): min=150, max=1150, avg=300.32, stdev=50.97
lat (usec): min=151, max=1152, avg=301.82, stdev=51.27
clat percentiles (usec):
| 1.00th=[ 200], 5.00th=[ 220], 10.00th=[ 230], 20.00th=[ 240],
| 30.00th=[ 250], 40.00th=[ 270], 50.00th=[ 290], 60.00th=[ 310],
| 70.00th=[ 340], 80.00th=[ 370], 90.00th=[ 420], 95.00th=[ 450],
| 99.00th=[ 500], 99.50th=[ 550], 99.90th=[ 600], 99.95th=[ 650],
| 99.99th=[ 700]
bw ( KiB/s): min=455360, max=480000, per=100.00%, avg=479436.00, stdev=3500.90, samples=240
iops : min=113840, max=120000, avg=119800.00, stdev=875.23, samples=240
lat (usec) : 250=15.00%, 500=82.50%, 750=2.50%
cpu : usr=10.00%, sys=20.00%, ctx=180500, majf=0, minf=40
IO depths : 1=0.0%, 2=0.1%, 4=0.2%, 8=0.3%, 16=0.4%, 32=1.0%, >=64=98.0%
submit : 0=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=100.0%
complete : 0=0.0%, 4=0.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=64=100.0%
issued rwts: total=0,14112000,0,0 short=0,0,0,0 dropped=0,0,0,0
latency : target=0, window=0, percentile=100.00%, depth=128fio 测试结果分析
- 延时测试:
--bs=4k --iodepth=1- 使用 4KB 的块大小(
--bs=4k)和单一 I/O 深度(--iodepth=1)进行测试。 - 这个配置通常用于测试系统在最低并发度下的响应时间,反映的是延迟性能。因为 IO 深度为 1,测试强调每个 I/O 操作的响应时间,使得延迟值通常是所有测试场景中最小的。
- 使用 4KB 的块大小(
- 带宽测试:
--bs=128k--iodepth=128- 使用 128KB 的块大小(
--bs=128k)和较大的 I/O 深度(--iodepth=128)。 - 这种配置用于测量系统的最大数据吞吐能力,即带宽。因为较大的块大小和高 IO 深度能够充分利用设备的 I/O 能力,因而带宽测试场景下的带宽值通常是最大的。
- 使用 128KB 的块大小(
- IOPS 测试:
--bs=4k --iodepth=128- 使用 4KB 的块大小(
--bs=4k)和高 I/O 深度(--iodepth=128)。 - 这个配置用于测量系统的 IOPS,即每秒能处理多少 I/O 操作。较小的块大小有助于提高 IOPS,因为较小的块处理速度快,而较高的 IO 深度增加了并发处理能力。因此,IOPS 测试场景下的 IOPS 值通常是最高的。
- 使用 4KB 的块大小(
- 延时测试关注单一操作的响应速度,因此延迟最小。
- 带宽测试关注设备的数据传输能力,因此带宽最大。
- IOPS 测试关注单位时间内的操作次数,因此 IOPS 最高。