一、关于FIO
1.1 简介
FIO是一个开源的I/O压力测试工具,主要是用来测试磁盘的IO性能,也可测试cpu,nic的IO性能。它可以支持13种不同的I/O引擎,包括:sync,mmap, libaio, posixaio, SG v3, splice, network, syslet, guasi, solarisaio, I/Opriorities (针对新的Linux内核), rate I/O, forked or threaded jobs等。
fio 官网地址:http://freshmeat.net/projects/fio/
fio文档:https://fio.readthedocs.io/en/latest/index.html
2.1 常用测试场景
FIO相关测试场景:
顺序读写 (吞吐量,常用单位为MB/s):文件在硬盘上存储位置是连续的。
适用场景:大文件拷贝(比如视频音乐)。速度即使很高,对数据库性能也没有参考价值。
4K随机读写 (IOPS,常用单位为次):在硬盘上随机位置读写数据,每次4KB。
适用场景:操作系统运行、软件运行、数据库。
二、FIO安装
有三种安装方式
2.1 apt安装(Ubuntu)
#更新apt源 apt update #安装fio apt-get install fio
2.2 使用yum安装(centos)
#更新yum源 yum install epel-release #安装fio yum install libaio-devel fio
2.3 手动安装
wget http://brick.kernel.dk/snaps/fio-2.2.10.tar.gz yum install libaio-devel tar -zxvf fio-2.2.10.tar.gz cd fio-2.2.10 make make install
附上一个基本涵盖所有操作系统的FIO包下载地址的网址:https://pkgs.org/download/fio
2.4 验证是否安装成功
输入:fio -h,看是否安装成功
三、FIO使用
3.1 fio参数解释
可以使用fio -help查看每个参数,具体的参数左右可以在官网查看how to文档,如下为几个常见的参数描述
filename=/dev/emcpowerb 支持文件系统或者裸设备,--filename=/dev/sdc或者--filename=/mnt/ccg/test_data(挂载的目录下任意文件名)
direct=1 测试过程绕过机器自带的buffer,使测试结果更真实
rw=randwread 测试随机读的I/O
rw=randwrite 测试随机写的I/O
rw=randrw 测试随机混合写和读的I/O
rw=read 测试顺序读的I/O
rw=write 测试顺序写的I/O
rw=rw 测试顺序混合写和读的I/O
bs=4k 单次io的块文件大小为4k,如果是测试文件系统,建议和文件系统的块大小保持一致。
bsrange=512-2048 同上,提定数据块的大小范围,这里是随机生成一个范围
time_based 如果设置的话,即使file已被完全读写或写完,也要执行完runtime规定的时间。它是通过循环执行相同的负载来实现的,与runtime相对应。
ramp_time=time 设定在记录任何性能信息之前要运行特定负载的时间。这个用来等性能稳定后,再记录日志
size=5g 本次的测试文件大小为5g,以每次4k的io进行测试,即生成读写的文件大小。
fdatasync=int 同fsync,但是采用fdatasync()来同步数据,但不同步元数据
sync=bool 使用sync来进行buffered写。对于多数引擎,这意味着使用O_SYNC
numjobs=30 本次的测试线程为30
iodepth=1 队列深度。默认是1,可以通过设置大于1的数来提升并发度。
runtime=1000 测试时间为1000秒,如果不写则一直将5g文件分4k每次写完为止
ioengine=psync io引擎使用pync方式,如果要使用libaio引擎,需要yum install libaio-devel包
randrepeat=true 对于随机IO负载,配置生成器的种子,使得路径是可以预估的,使得每次重复执行生成的序列是一样的。
rwmixwrite=30 在混合读写的模式下,写占30%,推荐读写配比为7:3
group_reporting=1 关于显示结果的,汇总每个进程的信息
此外
lockmem=1g 只使用1g内存进行测试
zero_buffers 用0初始化系统buffer
nrfiles=8 每个进程生成文件的数量
3.2 fio测试场景及生成报告详解
1)测试变量:
bs大小:(4k,16k,64k,1m)
读写模式:(read,write,rw,randread,randwrite,randrw)
使用libaio异步引擎,iodepth队列长度为128。
运行时间为60s
第一种:4K,顺序写
fio --randrepeat=1 --ioengine=libaio --direct=1 --name=ccg_fio --iodepth=128 --numjobs=16 --size=1g --bs=4k --group_reporting=1 --readwrite=write --time_based=1 --runtime=60 --sync=0 --fdatasync=0 --filename=/mnt/ccg/4k_write
第二种:16K,顺序读
fio --randrepeat=1 --ioengine=libaio --direct=1 --name=ccg_fio --iodepth=128 --numjobs=16 --size=1g --bs=16k --group_reporting=1 --readwrite=read --time_based=1 --runtime=60 --sync=0 --fdatasync=0 --filename=/mnt/ccg/16k_read
第三种:16K,混合读写,70%读,30%写
fio --randrepeat=1 --ioengine=libaio --direct=1 --name=ccg_fio --iodepth=128 --numjobs=16 --size=1g --bs=16k --group_reporting=1 --readwrite=rw -rwmixread=70 --time_based=1 --runtime=60 --sync=0 --fdatasync=0 --filename=/mnt/ccg/16k_rw
第四种:64k,随机写
fio --randrepeat=1 --ioengine=libaio --direct=1 --name=ccg_fio --iodepth=128 --numjobs=16 --size=1g --bs=64k --group_reporting=1 --readwrite=randwrite --time_based=1 --runtime=60 --sync=0 --fdatasync=0 --filename=/mnt/ccg/64k_randwrite
第五种:1m,随机读
fio --randrepeat=1 --ioengine=libaio --direct=1 --name=ccg_fio --iodepth=128 --numjobs=16 --size=1g --bs=1m --group_reporting=1 --readwrite=randread --time_based=1 --runtime=60 --sync=0 --fdatasync=0 --filename=/mnt/ccg/1m_randread
第六种:1m,随机读写,70%读,30%写
fio --randrepeat=1 --ioengine=libaio --direct=1 --name=ccg_fio --iodepth=128 --numjobs=16 --size=1g --bs=1m --group_reporting=1 --readwrite=randrw -rwmixread=70 --time_based=1 --runtime=60 --sync=0 --fdatasync=0 --filename=/mnt/ccg/1m_randrw
2)执行测试
root@client:/mnt/ccg# fio --randrepeat=1 --ioengine=libaio --direct=1 --name=ccg_fio --iodepth=128 --numjobs=16 --size=1g --bs=4k --group_reporting=1 --readwrite=write --time_based=1 --runtime=60 --sync=0 --fdatasync=0 --filename=/mnt/ccg/4k_write
报告详解
#这一行列出了执行的关键参数
ccg_fio: (g=0): rw=write, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=128
...
fio-2.2.10
Starting 16 processes
ccg_fio: Laying out IO file(s) (1 file(s) / 1024MB)
Jobs: 16 (f=16): [W(16)] [100.0% done] [0KB/98.11MB/0KB /s] [0/25.2K/0 iops] [eta 00m:00s]
ccg_fio: (groupid=0, jobs=16): err= 0: pid=77217: Mon Jul 27 17:42:01 2020
write: io=5662.2MB, bw=96631KB/s, iops=24157, runt= 60002msec #io指的是读写的数据总量,iops是关键的测试指标,每秒io次数,runt是执行总时间
slat (usec): min=3, max=5087, avg=611.46, stdev=640.30 #slat=提交延迟,代表IO提交到kernel做处理的过程
clat (usec): min=317, max=238746, avg=82794.77, stdev=24469.97 #clat=完成延迟,代表提交到kernel到IO做完之间的时间
lat (usec): min=500, max=238761, avg=83406.74, stdev=24540.97 #lat=响应时间,IO结构体创建时刻开始,直到紧接着clat完成
clat percentiles (msec): #分位分布图
| 1.00th=[ 21], 5.00th=[ 42], 10.00th=[ 52], 20.00th=[ 64],
| 30.00th=[ 73], 40.00th=[ 79], 50.00th=[ 85], 60.00th=[ 90], #50分位:85us
| 70.00th=[ 95], 80.00th=[ 101], 90.00th=[ 110], 95.00th=[ 120], #90分位:110us,95分位:120us
| 99.00th=[ 151], 99.50th=[ 167], 99.90th=[ 192], 99.95th=[ 200], #99分位:151us
| 99.99th=[ 215]
bw (KB /s): min= 4, max=14296, per=6.30%, avg=6087.37, stdev=1207.17 #bandwidth,带宽
lat (usec) : 500=0.01%, 750=0.01%, 1000=0.01% #latency分布:<500us占0.01%, 500us~750us占0.01%, <1000us占0.01
lat (msec) : 2=0.01%, 4=0.03%, 10=0.19%, 20=0.71%, 50=8.30% #latency分布:<2ms占0.01%, 2ms~4ms占0.03%, 4ms~10ms占0.19%, 10ms~20ms占0.71%,20ms~50ms占8.3%
lat (msec) : 100=69.79%, 250=20.97% #latency分布:<100ms占69.79%,100ms~250ms占20.97%
cpu : usr=0.76%, sys=5.97%, ctx=2862343, majf=0, minf=5759 #cpu=利用率,和top命令中类似
IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=0.1%, 32=0.1%, >=64=99.9% #IO depths=io队列
submit : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0% #IO submit=单个IO提交要提交的IO数
complete : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.1% #IO complete=Like the above submit number, but for completions instead.
issued : total=r=0/w=1449515/d=0, short=r=0/w=0/d=0, drop=r=0/w=0/d=0 #IO issued=The number of read/write requests issued, and how many of them were short.
latency : target=0, window=0, percentile=100.00%, depth=128 #IO latencies=IO完延迟的分布
Run status group 0 (all jobs):
WRITE: io=5662.2MB, aggrb=96631KB/s, minb=96631KB/s, maxb=96631KB/s, mint=60002msec, maxt=60002msec
#io=表示总共完成的IO数量。基于时间的测试场景下,此值为变量(时间越长读写次数越多);在基于容量的测试场景下,此值匹配size参数大小(最多只会读写文件大小的size)。
#aggrb是所有进程/设备的汇总带宽。
#minb/maxb表示测量到的最小/最大带宽。
#mint/maxt表示测试的最短和最长耗时。基于时间的测试场景下,匹配runtime参数(基本一致),基于容量的测试,是一个变量(随时间大小变化)。
3.3 fio的job文件格式
job file格式采用经典的ini文件,[]中的值表示job name,可以采用任意的ASCII字符,‘global’除外,global有特殊的意义。Global section描述了job file中各个job的默认配置值。一个job section可以覆盖global section中的参数,一个job file可以包含几个global section.一个job只会受到它上面的global section的影响。‘;’和‘#’可以用作注释
例子1:两个进程,分别从一个从128MB文件中,随机读的job file.
#global为全局配置,对每个job都生效
[global]
rw=randread
size=128m
[job1]
#这里的job名为job1,如果job1里面也定义rw,则会覆盖global中的rw的值
#rw=randwrite
[job2]
#rw=rw
#–end job file–
job1和job2 section是空的,因为所有的描述参数是共享的。没有给出filename=选项,fio会为每一个job创建一个文件名,如果用命令写,则是:
fio –name=global –rw=randread –size=128m –name=job1 –name=job2
例子2:多个进程随机写文件的实例
;–start job file —
[random-writers]
ioengine=libaio
iodepth=4
rw=randwrite
bs=32k
direct=0
size=64m
numjobs=4
;–end job file–
这个例子没有global section,只有一个job section.
上一个实例的说明:采用async,每一个文件的队列长度为4,采用随机写,采用32k的块,采用非direct io,共有4个进程,每个进程随机写64M的文件。也可以采用下面的命令:
fio –name=random-writers –ioengine=libaio –iodepth=4 –rw=randwrite –bs=32k –direct=0 –size=64m –numjobs=4
3.4 环境变量(参数化)
在job file中支持环境变量扩展。类似于${VARNAME}可以作为选项的值(在=号右边)
例子:
#如下是job配置文件
;–start job files–
[random-writers]
rw=randwrite
size=${SIZE}
numjobs=${NUMJOBS}
;–end job file–
如果执行:
export SIZE=64m NUMJOBS=4 fio jobfile,fio
该文件将被扩展为
;–start job file–
[random-writers]
rw=randwrite
size=64m
numjobs=4
;–end job file–
Tips:
fio有一些保留keywords,在内部将其替换成合适的值,这些keywords是:
$pagesize 当前系统的页大小
$mb_memory 系统的总内存的大小,以MB为单位
$ncpus 在线有效的cpu数
这引起在命令行中和job file中都可以用,当job运行的时候,会自动的用当前系统的徝进行替换。支持简单的数学计算,如:
size=8*$mb_memory
也就是说我们尽量不要用这些保留关键字进行变量命名
3.5 FIO的JOB配置文件实例
[global]
#定义了全局的默认配置,其中参数化了IODEPTH,NUMJOBS,SIZE,BS,MNT_POINT,RUNTIME,SIZE
iodepth=${IODEPTH}
numjobs=${NUMJOBS}
size=${SIZE}
bs=${BS}
directory=${MNT_POINT}
runtime=${RUNTIME} ;e.g 10, 10m; default to seconds
time_based=1
randrepeat=1
ioengine=libaio
direct=1
sync=0
fdatasync=0
group_reporting=1
filename=qfs_fio_test_file_${SIZE}
[write]
#顺序写场景
name=qfs_write_${SIZE}
rw=write
[read]
#顺序读场景
name=qfs_read_${SIZE}
rw=read
[randread]
#随机读场景
name=qfs_randread_${SIZE}
rw=randread
[randwrite]
#随机写场景
name=qfs_randwrite_${SIZE}
rw=randwrite
[rw]
#混合读写场景,读写比为7:3,将读写结果最后合并统计(MIXED)
name=qfs_rw_${SIZE}
rw=rw
rwmixread=70
unified_rw_reporting=1
[randrw]
#随机读写场景,读写比为7:3,将读写结果最后合并统计(MIXED)
name=qfs_randrw_${SIZE}
rw=randrw
rwmixread=70
unified_rw_reporting=1
3.6 FIO脚本编写
#!/bin/bash # trap Ctrl-C and call ctrl_c() trap ctrl_c INT #用于捕获SIGKILL,强行中止本脚本 function ctrl_c() { echo "** Trapped Interupt Signal, Exit. **" exit 1 } if [[ $# == 0 ]] || [[ $1 == '-h' ]] then echo "sh $0 write 128 16 1g 4k 60s /mnt/test_fio_4k" else #这里的参数传递: #TYPE:write,read,rw,randwrite,randread,randrw #JOB_FILE:qfs.fio,即3.5所示的文件 #MNT_POINT:/mnt/ccg/xxx_file,即进行读写测试的文件路径 #IODEPTH:128 #NUMJOBS:最大16进程进行操作 #SIZE:测试的文件大小 #BS:块大小 #RUNTIME:60s #JOB_FILE:qfs.fio export TYPE=$1 JOB_FILE=qfs.fio MNT_POINT=$7 IODEPTH=$2 NUMJOBS=$3 SIZE=$4 BS=$5 RUNTIME=$6; fio --section=$TYPE $JOB_FILE --output="output/qfs_fio-$IODEPTH-$NUMJOBS-$SIZE-$BS-$TYPE.output" fi
四、参考资料
https://docs.cloud.inspur.com/testdata/io_inserver.html
华为云硬盘的性能
云硬盘性能测试工具
腾讯如何衡量云硬盘的性能
fio 含job用法
博主:测试生财
座右铭:用测试完成原始积累,用投资奔向财务自由
csdn:https://blog.csdn.net/ccgshigao
博客园:https://www.cnblogs.com/qa-freeroad/
51cto:https://blog.51cto.com/14900374