简介:fio-2.2.10.tar.gz 是 Linux 平台下 Flexible I/O Tester 的稳定版源码包,面向系统管理员、测试工程师与存储性能调优人员,用于模拟随机读写、顺序读写、混合读写等负载,评估硬盘、SSD 及网络存储的 IOPS、吞吐量与延迟表现。压缩包共 342 个文件,约 573KB,以 125 个 c 源文件与 146 个 h 头文件构成核心实现,另含 38 个 fio 作业配置、Makefile 与 configure 构建脚本、manpage 手册及 gnuplot 绘图脚本,便于编译安装与自定义测试场景。已有 409 人学习下载。资源涵盖多线程并发、队列深度、ioengine 选择、CSV/JSON 报告输出与错误检测等关键机制,读者可据此搭建可复现的磁盘基准测试环境,掌握从配置编写到结果分析的完整流程,为数据库、虚拟化等场景的存储选型与瓶颈定位提供依据。
1. fio-2.2.10.tar.gz 到底解决什么问题:一次磁盘性能压测的完整起点
拿到fio-2.2.10.tar.gz这个包,多数场景不是要研究它源码,而是要在内网机器上快速搭一套可复现的磁盘性能压测环境。fio 是 Flexible I/O Tester 的缩写,专门用来对块设备、文件系统做可控的读写压力测试,输出 IOPS、带宽、延迟这些硬指标。为什么偏偏是 2.2.10 这个版本?因为不少生产环境跑的是 CentOS 7 一类老系统,glibc 版本低,直接上最新 fio 经常编译报错,而 2.2.10 对老工具链友好,源码包体积小,./configure && make基本一把过。这篇笔记就围绕这个 tar.gz 包,把解压、编译、参数配置、结果解读和踩坑串成一条能照着复现的路径,适合要做存储选型、数据库调优、或者单纯想验证一块盘真实性能的工程师。
2. 从 tar.gz 到可执行文件:编译安装与最小验证
2.1 为什么选源码编译而不是直接装包
很多人第一反应是yum install fio或apt install fio,省事。但在实际项目里,发行版仓库里的 fio 版本往往偏旧,参数支持不全,比如--ioengine=io_uring这种新引擎在老仓库里根本没有。而fio-2.2.10.tar.gz这种源码包,好处是版本锁定、可移植、能按需裁剪。你拿到的是一个确定行为的二进制,换台机器重新编译,结果一致,这对压测的可复现性很关键。
编译前先确认依赖。fio 核心只依赖 libaio(异步 IO 引擎需要),其他像 libzbc、libgfapi 都是可选。最小依赖装法:
# CentOS / RHEL 系 yum install -y gcc make libaio-devel zlib-devel # Debian / Ubuntu 系 apt-get install -y gcc make libaio-dev zlib1g-devlibaio-devel是必须的,否则编译出来的 fio 没有libaio引擎,只能跑同步 IO,压不出真实并发。zlib-devel用于支持 gzip 压缩的 IO 负载,做压缩场景测试时会用到。
2.2 解压、配置、编译三步走
# 1. 解压 tar -zxvf fio-2.2.10.tar.gz cd fio-2.2.10 # 2. 配置,指定安装路径,开启 libaio ./configure --prefix=/usr/local/fio --enable-libaio # 3. 编译并安装 make -j$(nproc) make install--prefix把 fio 装到独立目录,不污染系统路径,卸载时直接删目录就行。--enable-libaio显式打开异步引擎支持,如果 configure 阶段提示找不到 libaio,说明前面的依赖没装对。make -j$(nproc)用满 CPU 核数加速编译,fio 源码量不大,一般一两分钟完事。
装完验证:
/usr/local/fio/bin/fio --version # 输出类似:fio-2.2.10版本号对上了,说明二进制可用。接着做一个最小压测,确认工具链正常:
# 在 /tmp 下写一个 128M 文件,做 4K 随机读,跑 10 秒 /usr/local/fio/bin/fio --name=minitest --filename=/tmp/fio_test \ --size=128M --rw=randread --bs=4k --ioengine=libaio \ --iodepth=16 --runtime=10 --time_based --direct=1这条命令的含义:--rw=randread随机读,--bs=4k块大小 4K,--ioengine=libaio用异步引擎,--iodepth=16队列深度 16,--direct=1绕过页缓存直接打盘。跑完看输出的iops和lat (usec)两列,如果 IOPS 是个合理数字(比如 SATA SSD 几千到几万),说明环境没问题。如果 IOPS 异常高,多半是--direct=1没生效,数据全走内存了。
提示:第一次跑建议加
--output-format=normal,默认输出就够看。要存结果加--output=result.txt,方便后续对比。
3. 压测参数怎么设:job file 与命令行两种写法
3.1 命令行适合快速验证,job file 适合固化场景
命令行参数一多就难维护,fio 支持把配置写成 job file,一行一个参数,可读性好,还能版本管理。上面那条最小命令等价于:
# minitest.fio [global] ioengine=libaio direct=1 runtime=10 time_based=1 group_reporting=1 [minitest] filename=/tmp/fio_test size=128M rw=randread bs=4k iodepth=16[global]段是所有 job 共享的配置,[minitest]是具体任务名。group_reporting=1把多个 job 的结果合并输出,做多盘对比时很有用。执行:
/usr/local/fio/bin/fio minitest.fiojob file 的好处是改参数不用重敲长命令,团队里传一份文件就能复现同样的负载。
3.2 四个必调参数:rw、bs、iodepth、direct
这四个参数决定了压测的“形状”,设错了结果毫无参考价值。
| 参数 | 作用 | 常见取值 | 选错后果 |
|---|---|---|---|
| rw | 读写模式 | randread/randwrite/read/write/randrw | 顺序写测成随机写,IOPS 差一个量级 |
| bs | 块大小 | 4k/8k/64k/1M | 数据库场景用 1M,测不出真实 IOPS |
| iodepth | 队列深度 | 1/16/32/128 | 深度太低压不满盘,太高延迟失真 |
| direct | 是否绕过缓存 | 0/1 | 设 0 测的是内存不是盘 |
rw的选择跟业务强相关:数据库 OLTP 多是 4K 随机读写,选randrw并配rwmixread=70模拟读写比;日志类顺序追加选write配大bs;文件服务器混合负载用randrw。iodepth要结合ioengine看,libaio支持高深度,sync引擎深度只能是 1。direct=1在压测里几乎必开,否则第一次读全命中页缓存,数字漂亮但没意义。
3.3 用 ramp_time 排除预热干扰
盘刚上电、缓存未热、GC 未触发时,前几秒数据波动大。fio 提供ramp_time,让负载先跑一段不计入统计:
[global] ramp_time=30 runtime=120这样前 30 秒只压不记,后 120 秒才是有效数据。做 SSD 测试时尤其重要,很多盘前 10 秒能飙到标称值,之后掉速,不设 ramp_time 就会误判。
注意:
ramp_time和runtime是叠加的,总耗时是两者之和,脚本里算超时时间别漏了。
4. 结果怎么看:IOPS、带宽、延迟三张表
4.1 输出字段逐项拆解
fio 跑完会打印一大段,核心看三块。以随机读为例:
read: IOPS=45000, BW=175MiB/s (184MB/s)(2048MiB/12001msec) slat (usec): min=2, max=120, avg= 8.5, stdev= 3.2 clat (usec): min=10, max=8000, avg=350.2, stdev=120.5 lat (usec): min=12, max=8010, avg=358.7, stdev=121.0IOPS是每秒 IO 次数,BW是带宽。slat是提交延迟,clat是完成延迟,lat是两者之和,也就是应用看到的端到端延迟。压测报告里最该关注的是clat的avg和max,平均值看常态,最大值看抖动。如果max比avg高两个数量级,说明盘有长尾延迟,数据库场景要警惕。
4.2 用 percentile 看长尾
平均值会掩盖长尾,fio 支持输出百分位:
/usr/local/fio/bin/fio --name=pct --filename=/tmp/fio_test \ --size=1G --rw=randread --bs=4k --ioengine=libaio \ --iodepth=32 --runtime=60 --time_based --direct=1 \ --percentile_list=50:90:99:99.9输出里会多出50.00th、99.00th这些列。99th延迟才是 SLA 里真正承诺的数字,avg好看但99th爆表的情况太常见了。做存储选型时,把99.9th延迟作为硬门槛,比看 IOPS 峰值靠谱得多。
4.3 多 job 对比与结果落盘
要对比两块盘,写一个 job file 跑两遍,或者用--output分别存文件再 diff:
/usr/local/fio/bin/fio disk_a.fio --output=a.json --output-format=json /usr/local/fio/bin/fio disk_b.fio --output=b.json --output-format=jsonJSON 格式方便脚本解析,把jobs[].read.iops、jobs[].read.clat_ns.percentile这些字段抽出来做表格。手工看输出容易漏,落成 JSON 再处理是更工程化的做法。
5. 避坑与排查:五个血泪教训
5.1 现象:IOPS 高得离谱,接近内存速度
原因:direct=1没生效,或者filename指向了 tmpfs、页缓存覆盖的路径。解决:确认direct=1在[global]或 job 段里,filename指向真实块设备或普通文件系统,跑之前echo 3 > /proc/sys/vm/drop_caches清缓存。
5.2 现象:编译报错libaio.h: No such file
原因:只装了运行时库没装开发头文件。解决:装libaio-devel(RHEL 系)或libaio-dev(Debian 系),重新./configure。如果还报错,检查--enable-libaio是否拼写正确。
5.3 现象:iodepth 设了 128,IOPS 却没涨
原因:ioengine用了sync或psync,这两个引擎不支持队列深度,设了也白设。解决:换libaio或io_uring(新内核),确认iodepth生效。用iostat -x 1看aqu-sz字段,能反映实际队列深度。
5.4 现象:跑一段时间后 IOPS 断崖下跌
原因:SSD 的 SLC 缓存写满,或者盘本身有温控降速。解决:加ramp_time跳过前期,延长runtime看稳态;同时用smartctl -a看盘的温度和磨损。如果稳态 IOPS 只有峰值的十分之一,这块盘的真实性能就得按稳态算。
5.5 现象:--size设得比盘大,报错或行为异常
原因:fio 默认在filename指定的文件里跑,文件大小由size决定,设成 1T 但盘只有 256G,写不进去。解决:size不超过可用空间,或者用--filename=/dev/sdX直接打裸设备(会毁数据,务必确认盘是空的)。裸设备测试更接近真实,但风险高,生产机别乱来。
6. 进阶技巧:用 fio 做可复现的存储基线
把 fio 用成一次性工具太浪费,真正有价值的是建立一套可复现的基线。我的习惯是给每类盘建一个 job file 模板,固定bs、iodepth、rw、ramp_time、runtime,只改filename。跑完把 JSON 结果按日期归档,新盘进来先跑同一套,横向对比99th延迟和稳态 IOPS,而不是看厂商标称的峰值。
一个实用的基线脚本:
#!/bin/bash # baseline.sh - 对指定设备跑标准四件套 DEV=$1 OUTDIR=/data/fio_results/$(date +%Y%m%d_%H%M%S) mkdir -p $OUTDIR for mode in randread randwrite read write; do /usr/local/fio/bin/fio --name=${mode} --filename=$DEV \ --size=4G --rw=$mode --bs=4k --ioengine=libaio \ --iodepth=32 --ramp_time=30 --runtime=120 --time_based \ --direct=1 --group_reporting=1 \ --output=$OUTDIR/${mode}.json --output-format=json done echo "结果已存到 $OUTDIR"这个脚本跑四轮,覆盖随机读、随机写、顺序读、顺序写,每轮预热 30 秒、采样 120 秒。size=4G保证有足够数据量,不会因为文件太小反复覆盖同一区域。结果全落 JSON,后续用 jq 或 Python 抽字段做对比表。
验证方法上,我一般会做一次重复性检查:同一块盘连跑三遍,看99th延迟的波动是否在 10% 以内。如果波动很大,说明环境有干扰(后台任务、温度、其他 IO),这时候的基线不可信,得先排除干扰再测。另一个技巧是交叉验证,用iostat -x 1同时观察%util和await,如果 fio 报的延迟和iostat的await对不上,多半是direct或引擎配置有问题。
最后说个我踩过的坑:早期图省事,压测机和生产机共用一块系统盘,fio 一跑系统就卡死,结果全废。后来养成习惯,压测盘单独挂,系统盘只跑 OS,filename永远指向独立设备。这个习惯帮我省了无数次“数据为什么对不上”的排查时间。希望帮到你。
本文还有配套的精品资源,点击获取