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