新上线一台数据库服务器,厂商承诺的磁盘 IOPS 是 6000,业务跑起来却各种超时。你跑了个 dd 测出 1.2GB/s,看起来快得离谱,于是更加困惑——为什么纸面数据很好,实际却慢?问题出在测试方法上:dd 测的是单线程大块顺序写,而且很可能测的是操作系统页缓存,跟数据库那种"成千上万个 4KB 随机读"的真实负载毫无关系。要科学地测量服务器硬盘读写速度,业界的标准工具是 fio(Flexible I/O Tester)。本文讲清 fio 的参数怎么配、结果怎么看、以及哪些数字才是判断磁盘好坏的关键。
一、测试前的三条铁律
在用 fio 之前,先确立三条原则,否则测出来的数字没有任何参考价值。
1.1 必须绕过页缓存(direct I/O)
Linux 默认会用空闲内存做页缓存,写操作先落到内存就返回。如果你的测试文件比内存小,或者用默认的 buffered 模式,测到的其实是内存速度,动辄几 GB/s。fio 通过 --direct=1 使用 O_DIRECT,绕过页缓存直写设备。等价的选项是 --buffered=0。
1.2 测试文件要远大于内存
即使开了 direct I/O,如果测试文件太小、且被缓存过,仍可能出现偏差。经验做法是 --size 设为内存容量的 2 倍以上,且不低于 2GB。查看内存: free -g。
1.3 明确你要模拟的业务模型
磁盘性能不是单一数字,而是"块大小 + 读写比例 + 队列深度"三个变量的组合结果。数据库的 OLTP 负载是 4KB/8KB 随机读写、队列深度较高;日志和备份是 1MB 顺序写;视频点播是 1MB 顺序读。测试前先问自己:我要测的是哪一种?
# 安装 fio yum install -y fio # RHEL/CentOS/Rocky apt-get install -y fio # Debian/Ubuntu # 测试前先确认目标盘与剩余空间,避免把系统盘写满 lsblk df -h /data free -g # 确认文件系统类型(影响可选的 ioengine) stat -f -c %T /data
二、fio 核心参数速查
fio 的参数非常多,但日常只需要掌握下面这些。理解它们的含义,比记住命令行模板更重要。
| 参数 | 作用 | 常用取值与说明 |
|---|---|---|
--filename | 测试目标 | 文件路径或裸设备,如 /data/testfile、/dev/sdb。测裸设备会破坏数据,慎用 |
--ioengine | I/O 引擎 | libaio(Linux 原生异步,最常用);sync(同步,用 pread/pwrite);psync;libaio 需内核支持 |
--direct | 是否绕过缓存 | 1=O_DIRECT 直写设备;0=走页缓存。压测必须设 1 |
--rw | 读写模式 | read/write 顺序;randread/randwrite 随机;randrw 随机混合;rw 顺序混合 |
--rwmixread | 混合读占比 | 与 randrw 配合,70 表示 70% 读 30% 写 |
--bs | 单次 I/O 块大小 | 随机场景 4k/8k;顺序吞吐场景 1m。可用 4k,8k 逗号分隔做混合分布 |
--iodepth | 单任务队列深度 | 同时 inflight 的 I/O 数。1=测纯延迟;32/64=测极限 IOPS |
--numjobs | 并发任务数 | 模拟多线程业务。总并发 ≈ iodepth × numjobs |
--size | 单任务数据量 | 应远大于内存,如 4G、20G |
--runtime | 运行时长(秒) | 建议 ≥60 秒,太短无法反映稳态性能 |
--time_based | 按时间而非数据量结束 | 与 runtime 配合,跑满指定时间 |
--group_reporting | 汇总多任务结果 | 多 numjobs 时输出总 IOPS 而非每个 job 单独输出 |
--name | 任务名 | 结果输出中的标识,如 randread |
--output | 结果输出到文件 | 配合 --output-format=json 可做自动化比对 |
三、四组典型场景的标准测试命令
3.1 场景一:4K 随机读(数据库查询型负载)
fio --name=randread --filename=/data/fio.test --ioengine=libaio \
--direct=1 --rw=randread --bs=4k --iodepth=32 --numjobs=4 \
--size=4G --runtime=120 --time_based --group_reporting
这组参数模拟的是并发查询时的索引扫描。重点看 IOPS 和读延迟的 P99。
3.2 场景二:4K 随机写(事务提交、redo 日志)
fio --name=randwrite --filename=/data/fio.test --ioengine=libaio \
--direct=1 --rw=randwrite --bs=4k --iodepth=16 --numjobs=4 \
--size=4G --runtime=120 --time_based --group_reporting \
--fsync=1
注意比随机读多加了 --fsync=1:它表示每次写后调用 fsync 刷盘。如果业务是数据库,事务提交必须落盘,那么不加 fsync 测出来的写性能是虚高的——数字很好看,但数据安全性完全不同。想要更贴近 MySQL/PostgreSQL 的 redo 写入模型,还可以用 --fdatasync=1。如果测的是纯粹的硬件能力(与其他机器横向对比),则不要加 fsync。
3.3 场景三:1M 顺序读写(吞吐、备份、大文件)
# 顺序读吞吐
fio --name=seqread --filename=/data/fio.test --ioengine=libaio \
--direct=1 --rw=read --bs=1m --iodepth=8 --numjobs=1 \
--size=8G --runtime=60 --time_based --group_reporting
# 顺序写吞吐(把 rw 换成 write,bs 换成 1m)
fio --name=seqwrite --filename=/data/fio.test --ioengine=libaio \
--direct=1 --rw=write --bs=1m --iodepth=8 --numjobs=1 \
--size=8G --runtime=60 --time_based --group_reporting
这一组看的是 BW(带宽),单位为 MiB/s。机械盘在这个场景通常能达到 100~200 MiB/s,SATA SSD 约 400~550 MiB/s,NVMe 则可到数 GB/s。具体数值取决于介质与接口,不要跨介质类型硬比。
3.4 场景四:混合随机读写(真实业务模型)
fio --name=mixed --filename=/data/fio.test --ioengine=libaio \
--direct=1 --rw=randrw --rwmixread=70 --bs=8k \
--iodepth=32 --numjobs=4 --size=4G --runtime=180 \
--time_based --group_reporting
输出里会分别给出 read 和 write 两组的 IOPS/BW/lat,对读写分离的业务(如主从库)很有参考价值。
四、结果判读:看懂 fio 输出里的每个数字
fio 跑完会输出一大段文本,核心是三部分。
4.1 IOPS 与 BW
类似 read: IOPS=24.5k, BW=95.8MiB/s (100MB/s)(5748MiB/60001msec)。IOPS 是每秒完成次数,BW 是带宽。两者可以互相换算:BW ≈ IOPS × bs。所以 4K 随机读要 24000 IOPS 才能达到约 94 MiB/s,而 1M 顺序读只需要 95 IOPS。这就是为什么"IOPS 高"和"速度快"不能直接画等号——块大小不同,主导指标就不同。
4.2 延迟三分法:slat、clat、lat
这是最容易看错的地方。fio 把一次 I/O 拆成三段计时:
- slat(submission latency):提交延迟,从 fio 发起 I/O 到真正把请求交给内核的时间。通常只有几微秒,如果很高说明 CPU 或内核队列已经饱和。
- clat(completion latency):完成延迟,从请求交给内核到 I/O 真正完成的时间。这才是反映磁盘真实性能的关键指标。
- lat(total latency):总延迟 = slat + clat(在同步引擎下还包括排队时间)。
看磁盘快慢,主要看 clat 而不是 lat,因为 slat 里混入了 fio 自身的调度开销。
4.3 延迟分布百分位(比平均值重要十倍)
clat percentiles (usec): | 1.00th=[ 221], 5.00th=[ 262], 10.00th=[ 293], 20.00th=[ 338], | 30.00th=[ 379], 40.00th=[ 420], 50.00th=[ 462], 60.00th=[ 510], | 70.00th=[ 570], 80.00th=[ 660], 90.00th=[ 824], 95.00th=[ 1037], | 99.00th=[ 2147], 99.50th=[ 3294], 99.90th=[ 8455], 99.95th=[12649], | 99.99th=[23173]
上面是一段真实的延迟分布。读法:50.00th = 462 微秒表示一半的请求在 462μs 内完成;99.00th = 2147 微秒表示 1% 的请求超过了 2.1 毫秒;99.99th = 23173 微秒表示万分之一的请求超过了 23 毫秒。
判读标准:
- P99.9 与 P50 的比值在 10 倍以内:延迟一致性良好,适合放数据库。
- P99.9 是 P50 的 20 倍以上:存在严重尾延迟抖动。可能原因包括:云硬盘被其他租户抢占、后端正在做副本重建、SSD 正在做垃圾回收(GC)、RAID 卡电池学习(BBU learn)导致写缓存被临时关闭。
- P99.99 出现几十毫秒级尖峰:对 OLTP 数据库来说,这意味着每万个查询里就有一个要卡几十毫秒,前端体验就是"偶尔转圈"。
五、交叉验证:fio + iostat + ioping
只靠 fio 一个工具还不够,配合系统级工具能定位更多问题。
# 压测的同时另开一个终端,观察设备级实时指标 iostat -xmt 2 # 重点列: # r/s w/s 每秒读写请求数(即 IOPS) # rkB/s wkB/s 每秒读写数据量 # await 平均 I/O 响应时间(含排队),ms # r_await/w_await 读写分开的平均响应时间 # %util 设备繁忙度,接近 100% 即饱和 # aqu-sz 平均请求队列长度 # ioping 用于快速测纯延迟(类似网络 ping) ioping -c 20 /data ioping -D -c 20 /data # 显示每次请求的延迟
%util 接近 100% 说明设备已到瓶颈;此时如果 aqu-sz 也很大(比如超过 8),说明请求严重积压。机械盘上 await 持续超过 20ms、固态盘持续超过 5ms 通常意味着需要优化或扩容。注意 %util 对 NVMe 这类多队列设备的意义有限(它按单设备统计,无法反映多队列并行度),应以 IOPS 与延迟为准。
六、标准压测流程清单
- 确认测试目标:明确业务是随机为主还是顺序为主、块大小、读写比例。
- 确认磁盘剩余空间充足,不要在系统盘或生产数据目录上跑写测试。
- 确认内存大小,设置
--size为内存的 2 倍以上。 - 先跑
--iodepth=1 --numjobs=1测基准延迟,再逐步加大 iodepth 找性能拐点。 - 按场景跑随机读、随机写、顺序读、顺序写四组,每组
--runtime不小于 60 秒。 - 数据库场景的写测试加
--fsync=1或--fdatasync=1,保证结果可比。 - 记录 IOPS、BW、clat 的 P50/P99/P99.9,重点看尾延迟而非平均值。
- 用
--output=result.json --output-format=json存档,方便扩容前后横向对比。 - 压测期间并行跑
iostat -xmt 2,观察 %util、await、aqu-sz 是否与 fio 结论一致。 - 测试完成删除测试文件:
rm -f /data/fio.test,释放空间。
小结与避坑建议
测试服务器硬盘读写速度的核心不是"跑个命令",而是用正确的模型测出可比的数字,并从延迟分布里读出稳定性。记住:IOPS 是吞吐视角,延迟是体验视角,两者必须一起看;块大小决定了该看哪一个;direct=1 和足够大的 size 是数据可信的前提。
常见误区:
- 用
dd测磁盘性能:dd if=/dev/zero of=test bs=1M count=1000默认走缓存,且是单线程顺序写,结果既不代表 IOPS 也不代表真实延迟。用dd ... oflag=direct至少能绕过缓存,但仍无法模拟并发。 - 忘了
--direct=1,测出几 GB/s 的内存速度,据此做的容量规划全部错误。 - 只看平均延迟。业务卡顿由尾延迟决定,P99.9 才是关键指标。
- 在生产盘的剩余空间上跑大文件测试:把磁盘写满导致业务故障是真实发生过的事故,务必先确认剩余容量。
- 拿不同块大小的结果横向对比:4K 随机读 5000 IOPS 和 1M 顺序读 5000 IOPS 是完全不同的性能等级。
- 忽略 CPU 瓶颈:高性能 NVMe 上,单核 CPU 可能先于磁盘被打满。此时 fio 会显示 slat 升高。解法是用
--numjobs分摊到多核,或使用io_uring引擎降低开销。
最后给出一个可量化的验收思路:把压测结果按"目标 IOPS + 目标 P99 延迟"两个条件同时约束。例如"4K 随机写 IOPS ≥ 3000 且 P99 ≤ 5ms",只有同时满足才算达标。只满足 IOPS 而尾延迟超标,数据库依然会卡。
本文为存储与硬盘管理系列,fio 参数以实际安装版本为准,可用 fio --version 查看。
企业QQ咨询




