fio —— 磁盘 IO 基准测试

运维工具手册 10-压测与基准 6.4 KB 原文

fio —— 磁盘 IO 基准测试

分类:10 压测与基准 适用:Linux(fio 3.x),支持块设备与文件 一句话:测出磁盘真实的 IOPS、吞吐、延迟,验证云盘规格、比较存储方案。

1. 它解决什么问题

  • 云盘标称 5 万 IOPS,实际能跑多少?
  • 新采购的 SSD 是否达标、是否有坏盘。
  • 数据库该用多大 iodepth、什么块大小。
  • 磁盘"变慢"是硬件问题还是业务压力。

2. 安装

apt install -y fio
dnf install -y fio
fio --version

3. 四个核心维度

维度 参数 说明
读写模式 --rw read、write、randread、randwrite、rw、randrw
块大小 --bs 随机 4k、顺序 128k/1M
队列深度 --iodepth 并发 IO 数,SSD 要更深
并发任务 --numjobs 多进程模拟多线程

典型组合:

场景 参数
数据库 OLTP randread/randwrite bs=4k iodepth=32 numjobs=4
日志顺序写 write bs=128k iodepth=16
大文件拷贝 read/write bs=1M iodepth=8
混合读写 randrw --rwmixread=70 bs=4k

4. 常用命令

# 随机读 IOPS(数据库型)
fio --name=randread --filename=/data/test.bin --size=4G \
    --rw=randread --bs=4k --iodepth=32 --numjobs=4 \
    --ioengine=libaio --direct=1 --runtime=60 --time_based \
    --group_reporting

# 随机写 IOPS
fio --name=randwrite --filename=/data/test.bin --size=4G \
    --rw=randwrite --bs=4k --iodepth=32 --numjobs=4 \
    --ioengine=libaio --direct=1 --runtime=60 --time_based --group_reporting

# 顺序读带宽
fio --name=seqread --filename=/data/test.bin --size=4G \
    --rw=read --bs=1M --iodepth=16 --numjobs=1 \
    --ioengine=libaio --direct=1 --runtime=60 --time_based --group_reporting

# 顺序写带宽
fio --name=seqwrite --filename=/data/test.bin --size=4G \
    --rw=write --bs=1M --iodepth=16 --numjobs=1 \
    --direct=1 --runtime=60 --time_based --group_reporting

# 混合读写 7:3
fio --name=mix --filename=/data/test.bin --size=4G \
    --rw=randrw --rwmixread=70 --bs=4k --iodepth=32 --numjobs=4 \
    --direct=1 --runtime=60 --time_based --group_reporting

# 延迟测试(iodepth=1,看单 IO 延迟)
fio --name=lat --filename=/data/test.bin --size=1G \
    --rw=randread --bs=4k --iodepth=1 --numjobs=1 \
    --direct=1 --runtime=30 --time_based

# JSON 输出(便于入库对比)
fio ... --output-format=json > result.json

5. 关键参数

参数 作用 注意
--filename 测试文件/设备 裸盘写 /dev/sdb(⚠️ 会毁数据)
--size 文件大小 至少为内存的 2 倍,否则测到缓存
--direct=1 绕过 page cache 必加,否则测的是内存
--ioengine=libaio 异步 IO Linux 首选;io_uring 更新
--iodepth 队列深度 异步引擎才有效
--numjobs 并发任务数 每个 job 一个独立文件/区域
--runtime + --time_based 跑固定时长 避免文件大小影响结果
--ramp_time 预热时间 排除冷启动,建议 10s
--group_reporting 汇总输出 多 job 时更清晰
--rwmixread 读比例 配合 randrw
--fsync 每次写后 fsync 测真实持久化延迟
--invalidate=1 每次重新打开文件 避免缓存
--output-format=json JSON 自动比对

6. 结果怎么读

randread: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=32
Jobs: 4 (f=4): [r(4)][100.0%][r=195MiB/s][r=50.0k IOPS][eta 00m:00s]
   read: IOPS=50.0k, BW=195MiB/s (205MB/s)(11.4GiB/60001msec)
    clat (usec): min=120, max=8500, avg=2550.12, stdev=320.45
     lat (usec): min=125, max=8520, avg=2560.30, stdev=321.00
    clat percentiles (usec):
     |  1.00th=[  180],  5.00th=[  220], 10.00th=[  250], 20.00th=[  300],
     | 30.00th=[  350], 40.00th=[  400], 50.00th=[  450], 60.00th=[  520],
     | 70.00th=[  600], 80.00th=[  700], 90.00th=[  900], 95.00th=[ 1100],
     | 99.00th=[ 1500], 99.50th=[ 1800], 99.90th=[ 2500], 99.95th=[ 3000],
     | 99.99th=[ 6000]
指标 含义
IOPS 每秒 IO 次数
BW 带宽(MiB/s)
clat 完成延迟(从提交到完成)
lat 总延迟(含排队)
slat 提交延迟
percentiles P50/P99/P99.9 延迟,比 avg 更有意义

判读:

  • 随机 4k IOPS 达标、P99 延迟稳定 → 盘正常。
  • IOPS 随 iodepth 增加不涨 → 到设备上限。
  • P99.9 远高于 P50 → 抖动大,可能是云盘共享/GC。
  • 顺序带宽远低于标称 → 检查是否被限速、链路是否 10G。

7. 实战场景

7.1 验证云盘规格

# 阿里云 ESSD PL1 标称:随机读写 5万 IOPS,吞吐 350MB/s
fio --name=t --filename=/data/t.bin --size=10G --rw=randwrite --bs=4k \
    --iodepth=64 --numjobs=4 --direct=1 --runtime=120 --time_based \
    --ramp_time=10 --group_reporting

云盘常有 burst 额度(初始几分钟性能更高),测 2 分钟以上看稳态。

7.2 对比两块盘

for d in /data1 /data2; do
  echo "== $d"
  fio --name=t --filename=$d/t.bin --size=4G --rw=randread --bs=4k \
      --iodepth=32 --numjobs=4 --direct=1 --runtime=60 --time_based \
      --group_reporting 2>&1 | grep -E 'IOPS|clat percentiles' -A2
done

7.3 测数据库盘的真实延迟

fio --name=db --filename=/var/lib/mysql/fio.test --size=2G \
    --rw=randrw --rwmixread=70 --bs=16k --iodepth=1 --numjobs=8 \
    --direct=1 --runtime=120 --time_based --group_reporting

iodepth=1 numjobs=8 模拟 8 个同步 IO 的数据库线程。

7.4 裸盘测试(⚠️ 会清空数据)

lsblk -f /dev/sdb        # 确认没有文件系统
fio --name=raw --filename=/dev/sdb --rw=randwrite --bs=4k \
    --iodepth=32 --direct=1 --runtime=60 --time_based

裸盘测试跳过文件系统,能测出硬件真实能力,但绝对不能选错盘。

7.5 批量压测并汇总

for bs in 4k 16k 64k 1M; do
  fio --name=t --filename=/data/t.bin --size=4G --rw=randread --bs=$bs \
      --iodepth=32 --numjobs=4 --direct=1 --runtime=30 --time_based \
      --output-format=json 2>/dev/null \
    | jq -r --arg bs "$bs" '"\($bs) IOPS=\(.jobs[0].read.iops) BW=\(.jobs[0].read.bw) lat_p99=\(.jobs[0].read.clat_ns.percentile["99.000000"])"'
done

8. 常见坑

  1. 不加 --direct=1:测的是 page cache 速度,结果虚高几十倍。
  2. --size 小于内存:整个文件被缓存,随机读变成内存读。
  3. --iodepth 对同步引擎无效:ioengine=sync 下深度恒为 1,用 libaio。
  4. --numjobs 共享同一文件:多个 job 写同一区域互相干扰,用 --filename_format 或独立文件。
  5. 云盘 burst:前几分钟数据漂亮,稳态会掉,--runtime 至少 120s。
  6. 测到文件系统瓶颈:ext4/xfs 的元数据、journal 会影响结果,裸盘对照。
  7. SSD 磨损:大量写入消耗寿命,测试盘与生产盘分开。
  8. --filename 写错设备:/dev/sda 与 /dev/sdb 一字之差,数据全毁。执行前 lsblk 确认。
  9. 没加 --time_based:文件写完就结束,结果只代表小文件场景。
  10. 延迟单位:输出是 usec,报告时换算成 ms,别搞错数量级。
  11. 网络存储(NFS/Ceph):fio 结果受客户端缓存与网络影响,需多客户端测。
  12. CPU 成为瓶颈:高 IOPS 下 libaio 的提交线程可能吃满 CPU,看 top 确认。

9. 速查表

# 随机读 IOPS
fio --name=r --filename=/data/t.bin --size=4G --rw=randread --bs=4k \
    --iodepth=32 --numjobs=4 --ioengine=libaio --direct=1 \
    --runtime=60 --time_based --group_reporting

# 随机写 IOPS
fio --name=w --filename=/data/t.bin --size=4G --rw=randwrite --bs=4k \
    --iodepth=32 --numjobs=4 --direct=1 --runtime=60 --time_based --group_reporting

# 顺序带宽
fio --name=s --filename=/data/t.bin --size=4G --rw=read --bs=1M \
    --iodepth=16 --direct=1 --runtime=60 --time_based --group_reporting

# 混合
fio --name=m --filename=/data/t.bin --size=4G --rw=randrw --rwmixread=70 \
    --bs=4k --iodepth=32 --numjobs=4 --direct=1 --runtime=60 --time_based

# 延迟
fio --name=l --filename=/data/t.bin --size=1G --rw=randread --bs=4k \
    --iodepth=1 --numjobs=1 --direct=1 --runtime=30 --time_based

# 关注:IOPS、BW、clat P99/P99.9
fio —— 磁盘 IO 基准测试 - 知识库 - 起航社区