fio - IO测试工具

目前主流的第三方IO测试工具有fio、iometer和Orion,这三种工具各有千秋。

fio在Linux系统下使用比较方便,iometer在window系统下使用比较方便,Orion是oracle的IO测试软件,可在没有安装oracle数据库的情况下模拟oracle数据库场景的读写。

一、简介

FIO为Linux kernel的IO部分维护者axobe开发,Github: https://github.com/axboe/fio。FIO的详细使用方法参见自带的HOWTO文件。
FIO一种IO系统的Benchmark和压力测试工具,可以模拟不同IO场景下的IO负载。最初axobe设计FIO是为了节省为特定负载设计专门测试程序、进行性能测试,以及debug的时间。写测试程序非常浪费时间,因为不同的测试负载的IO场景不同,他们都用自己的方式产生IO,故需要模拟这些IO负载,FIO需足够灵活。
FIO支持 13种不同的 I/O 引擎, 包括:sync, mmap, libaio, posixaio, SG v3, splice, null, network,syslet,guasi,solarisaio 等等。 FIO 的引擎能识别简单易懂的job file文本去进行测试,同时能提供各种 I/O 性能指标,如I/O 延迟、IOPS和带宽等。因此被大量的制造商和广泛的用户认可,作为确认IO系统良好的测试工具。

二、典型工作过程

FIO命令行格式:fio [options] [job options] <job file(s)>

1. 运行过程

1)确定待模拟IO负载的IO参数,如I/O type,Block size,I/O engine,I/O depth,Target file/device等。可以配合fio的一些命令行参数来涉及I/O参数。如--parse-only或--showcmd等。
2)写一个 job file来描述要simulate的 I/O 负载。一个 job 文件可以控制产生任意数目的线程和文件。典型的 job 文件有一个 global 段(定义共享参数),一个或多个job 段(描述具体要产生的 job)。
3)运行时,FIO 从文件读这些参数,做处理,并根据这些参数描述,启动这些simulate线程/进程。
4)测试完成后,输出测试结果。
5)对FIO测试结果进行分析,评估IO系统的设计或优化方案或确定debug思路。

2. 运行方式

按参数输入方式分为如下两种:
1) 命令行方式:如FIO –filename=/dev/sdb –direct=1 –rw=randread –bs=4k –size=60G –numjobs=64 –runtime=10 –group_reporting –name=test
2) fio job_file 方式:如job_file 的名字为 test,则运行方式为 fio test。
按FIO工作模式分为如下两种:
1)client/server方式:FIO工作模式可以分为前端和后端,即client端和server端。server端执行fio --server,client端执行fio --client=hostname jobfile,这样client端就可以把jobfile发到server端来执行,hostname为server端的IP。
2)本地执行:命令行/job file。

3. 命令行选项

FIO有很多有用的命令行选项,具体请直接查看fio --help或HOWTO文档。这里简单列出如下几个:
filename=/dev/emcpowerb 支持文件系统或者裸设备,-filename=/dev/sda2或-filename=/dev/sdb
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         同上,提定数据块的大小范围
size=5g                  本次的测试文件大小为5g,以每次4k的io进行测试
numjobs=30               本次的测试线程为30
runtime=1000             测试时间为1000秒,如果不写则一直将5g文件分4k每次写完为止
ioengine=psync           io引擎使用pync方式,如果要使用libaio引擎,需要yum install libaio-devel包
rwmixwrite=30            在混合读写的模式下,写占30%
group_reporting          关于显示结果的,汇总每个进程的信息
此外
lockmem=1g               只使用1g内存进行测试
zero_buffers             用0初始化系统buffer
nrfiles=8                每个进程生成文件的数量
parse-only        仅解析configuration文件的参数使用是否正确
output          结果输入到一个文件中
cmdhelp=cmd       打印某个命令的帮助文档
showcmd          解析配置文件并转化成命令行参数

 


三、常用参数

1) IO type:表示 I/O 类型,Read Write;包括:Read 顺序读、 Write 顺序写、RandWrite 随机写、 RandRead 随机读。例如:rw=randwrite。
2) Block Size: 测试工具向被测设备发出的读写数据块的大小,可以是一个单独的值,也可以是一个范围。例如:blocksize=4k,4K 是主流,8K 是 Mysql和 Oracle,16K 是 MysqlInnodb。
3) IO engine: I/O 引擎,处理 IO 的方式,定义了发起 IO 的方式,可以是内存映射文件,可以是使用普通的读/写,我们可以使用 splice,异步 IO,syslet,甚至 SG(SCSI generic sg) 。包括 sync,pync,vsync,libaio 等,目前使用最广泛的是 libaio。
  • Sync:基本的read,write。通过lseek来作定位;
  • Psync:基本的pread,pwrite,在文件描述符给定的位置偏移上读取或写入数据,pread() 从文件 fd 指定的偏移 offset (相对文件开头) 上读取 count 个字节到buf 开始位置。文件当前位置偏移保持不变。 pwrite() 把缓存区 buf 开头的 count个字节写入文件描述符 fd offset 偏移位置上,文件偏移没有改变。 fd 引用的文件必须是可定位的,属于同步I/O;
  • Vsync:基本的 readv, writev;
  • Libaio: Linux 专有的异步 IO,Linux 仅支持非 buffered IO 的队列行为;
注意
  • 同步和异步的区别:同步 IO 是指,进程触发 IO 操作后,后面的操作不能进行,只能等待 IO 操作完成或者放弃 IO 操作,保证当前只有一个 IO 操作在进行。异步 IO 是指,触发 IO 操作以后,直接返回,可以继续执行后面的操作,IO 交给内核来处理,完成后内核通知进程 IO 完成。
  • libaio 引擎会用这个 iodepth 值来调用 io_setup,准备可以一次提交iodepth 个 IO 的上下文,同时申请iodepth个 io 请求队列用于保持 IO。在压测进行的时候,系统会生成特定的 IO 请求,往 io 请求队列里面提交,当队列里面的IO个数达到 iodepth_batch值的时候,就调用 io_submit 批次提交请求,然后开始调用 io_getevents 开始收割已经完成的 IO。每次收割多少呢?由于收割的时候,超时时间设置为 0,所以有多少已完成的 IO 就收割多少,最多可以收割 iodepth_batch_complete 个 IO。随着收割,IO 队列里面的 IO 数就少了,那么需要补充新的 IO。什么时候补充呢?当 IO 数目降到 iodepth_low 值的时候,就重新填充,保证 OS 可以看到至少 iodepth_low 数目的 io 在电梯口排队着。
  • libaio,这个是追求 IOPS 的;对于延时为主的测试,建议使用 psync。
4) IO size: 总共要读写的数据大小。例如:size=200G
5) IO depth:测试的队列深度,队列深度表示控制器可同时发送给被测设备的指令数目。在 ATA 指令集的 Read/Write FP DMA Queue 指令中,可以给每个读写指令带上标签。当队列深度为1时,控制器需要等待被测设备完成指令并返回结果后才可执行下一条指令;当队列深度大于 1 时,控制器可以向被测设备发出多条指令,并给每个指令附带一个标签作为指令号,此时被测设备并不按照指令号的顺序返回结果,从而可根据自身 Firmware 算法进行优化,寻找最容易得到的结果并反馈给控制器。当某一条指令的结果返回之后,该标签被回收并可再次利用。针对 SATA硬盘或 SSD,需要控制器工作在 AHCI 模式并且操作系统加载 AHCI 驱动程序且被测设备本身要支持 NCQ 才能支持此种访问模式。按照上述原理,在队列深度增加时,被测设备的访问效率将有明显提升,故性能也有相应提升。 默认对于每个文件来说是 1,可以设置一个更大的值来提高并发度。例如:iodepth=1
6) IO type:表示我们将执行 buffered io 还是 direct/raw io。 ZFS 和 Solaris 不支持direct io,在 windows 上同步 IO 引擎不支持 direct io。 Direct=0 时,则表明采用 buffered io。例如:direct=1
7) Num files: 表示负载将分发到几个文件中。例如:nrfiles=8
8) Num threads: 创建多少个进程/线程来并发执行。例如:numjobs=1。numjobs 建议等于 CPU 的 core 值,即 HT=off。
9) Runtime: 测试运行时间。例如:runtime=30
10)数据填充方式: 当进行数据写入时,可选择写入数据的形式。此选项可选三种模式,分别为重复填充、伪随机填充、随机填充。重复填充表示全 0 的数据写入,当 SSD主控支持数据压缩功能时,大量的全 0 数据将以压缩的方式写入 SSD 从而显著提高性能;伪随机填充写入即为伪随机数的填充写入,由于生成随机数需要花费大量时间,故在通常测试中,为减少等待时间,一般选择伪随机数填充。在选择伪随机数写入后,SSD 主控即使支持数据压缩功能也不能够对伪随机数进行压缩;全随机填充即为先进行随机数生成,之后再进行写入。随机数生成会消耗大量时间,通常测试中不采用此种模式。例如:refill_buffers
11) percentage_random:随机和顺序比率;例如:percentage_random=80,80%的随机。
12) time_based :决定脚本的运行时间,即使 file 已被完全读写或写完,也要执行完runtime 规定的时间。它是通过循环执行相同的负载来实现的。例如:time_based。
13) 目标盘符/被测盘符:当需要测试多个磁盘时,需要增加多行以下内容,例如: filename=/dev/sdb; 如果写在配置文件中,则需创建多个job区分多个不同的测试目标设备;

四、使用场景及输出

测试场景:

100%随机,100%读, 4K
fio -filename=/dev/emcpowerb -direct=1 -iodepth 1 -thread -rw=randread -ioengine=psync -bs=4k -size=1000G -numjobs=50 -runtime=180 -group_reporting -name=rand_100read_4k

100%随机,100%写, 4K
fio -filename=/dev/emcpowerb -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=4k -size=1000G -numjobs=50 -runtime=180 -group_reporting -name=rand_100write_4k

100%顺序,100%读 ,4K
fio -filename=/dev/emcpowerb -direct=1 -iodepth 1 -thread -rw=read -ioengine=psync -bs=4k -size=1000G -numjobs=50 -runtime=180 -group_reporting -name=sqe_100read_4k

100%顺序,100%写 ,4K
fio -filename=/dev/emcpowerb -direct=1 -iodepth 1 -thread -rw=write -ioengine=psync -bs=4k -size=1000G -numjobs=50 -runtime=180 -group_reporting -name=sqe_100write_4k

100%随机,70%读,30%写 4K
fio -filename=/dev/emcpowerb -direct=1 -iodepth 1 -thread -rw=randrw -rwmixread=70 -ioengine=psync -bs=4k -size=1000G -numjobs=50 -runtime=180 -group_reporting -name=randrw_70read_4k

 

结果报告查看:

 
[root@rac01-node02]# fio -filename=/dev/sdc4 -direct=1 -iodepth 1 -thread -rw=randrw -rwmixread=70 -ioengine=psync -bs=4k -size=1000G -numjobs=50 -runtime=180 -group_reporting -name=randrw_70read_4k_local
randrw_70read_4k_local: (g=0): rw=randrw, bs=4K-4K/4K-4K/4K-4K, ioengine=psync, iodepth=1
...
fio-2.1.10
Starting 50 threads
Jobs: 21 (f=21): [____m____m_m___m____mmm__mmm__mm_m_mmm_m__m__m_m_m] [3.4% done] [7004KB/2768KB/0KB /s] [1751/692/0 iops] [eta 01h:27m:00s]
randrw_70read_4k_local: (groupid=0, jobs=50): err= 0: pid=13710: Wed May 31 10:23:31 2017
  read : io=1394.2MB, 64=0.0%
     submit    : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     complete  : 0=0.0%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, 64=0.0%, >=64=0.0%
     issued    : total=r=356911/w=153820/d=0, short=r=0/w=0/d=0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
   READ: io=1394.2MB, aggrb=7926KB/s, minb=7926KB/s, maxb=7926KB/s, mint=180113msec, maxt=180113msec
  WRITE: io=615280KB, aggrb=3416KB/s, minb=3416KB/s, maxb=3416KB/s, mint=180113msec, maxt=180113msec

Disk stats (read/write):
  sdc: ios=356874/153927, merge=0/10, ticks=8668598/310288, in_queue=8978582, util=99.99%

 

五、结果分析

 

io=总共执行了多少size的IO
bw=平均IO带宽
iops=IOPS
runt=线程运行时间
lat=响应时间
slat=提交延迟  submission latency提交延迟,意思为“how long did it take to submit this IO to the kernel for processing?”
clat=完成延迟  completion latency完成延迟,意思为“This is the time that passes between submission to the kernel and when the IO is complete, not including submission latency”
clat percentiles  描述IO整体延迟latency distribution,为不同百分比IO的clat。
                    可以根据这些百分比数来绘制图形,从整体上分析IO系统的IO延时。这是非常有用的一个用来描述IO系统延时情况的指标,例如可以比较TP99、TP90等性能指标。

cpu=利用率
IO depths=io队列  这个iodepth设置是用来控制在任意给定时刻,有多少个IO被提交给OS。
                    这个参数完全是应用端来设置,与IO设备的IO queue不同。
                    summit和complete表明fio一次提交多少个IO,或一次完成多少个IO。
IO submit=单个IO提交要提交的IO数
IO complete=Like the above submit number, but for completions instead.
IO issued=The number of read/write requests issued, and how many of them were short.
IO latencies=IO完延迟的分布

aggrb=group总带宽
minb=最小.平均带宽.
maxb=最大平均带宽.
mint=group中线程的最短运行时间.
maxt=group中线程的最长运行时间.

ios=所有group总共执行的IO数.
merge=总共发生的IO合并数.
ticks=Number of ticks we kept the disk busy.
io_queue=花费在队列上的总共时间.
util=磁盘利用率

 

 

六、扩展之IO队列深度

在某个时刻,有N个inflight的IO请求,包括在队列中的IO请求、磁盘正在处理的IO请求。N就是队列深度。
加大硬盘队列深度就是让硬盘不断工作,减少硬盘的空闲时间。
加大队列深度 -> 提高利用率 -> 获得IOPS和MBPS峰值 ->注意响应时间在可接受的范围内,
增加队列深度的办法有很多,使用异步IO,同时发起多个IO请求,相当于队列中有多个IO请求,多线程发起同步IO请求,相当于队列中有多个IO请求。
增大应用IO大小,到达底层之后,会变成多个IO请求,相当于队列中有多个IO请求 队列深度增加了。
队列深度增加了,IO在队列的等待时间也会增加,导致IO响应时间变大,这需要权衡。

为何要对磁盘I/O进行并行处理呢?主要目的是提升应用程序的性能。这一点对于多物理磁盘组成的虚拟磁盘(或LUN)显得尤为重要。
如果一次提交一个I/O,虽然响应时间较短,但系统的吞吐量很小。
相比较而言,一次提交多个I/O既缩短了磁头移动距离(通过电梯算法),同时也能够提升IOPS。
假如一部电梯一次只能搭乘一人,那么每个人一但乘上电梯,就能快速达到目的地(响应时间),但需要耗费较长的等待时间(队列长度)。
因此一次向磁盘系统提交多个I/O能够平衡吞吐量和整体响应时间。

Linux系统查看默认队列深度:

 
[root@qsdb ~]# lsscsi -l
[0:0:0:0]    disk    DGC      VRAID            0533  /dev/sda 
  state=running queue_depth=30 scsi_level=5 type=0 device_blocked=0 timeout=30
[0:0:1:0]    disk    DGC      VRAID            0533  /dev/sdb 
  state=running queue_depth=30 scsi_level=5 type=0 device_blocked=0 timeout=30
[2:0:0:0]    disk    DGC      VRAID            0533  /dev/sdd 
  state=running queue_depth=30 scsi_level=5 type=0 device_blocked=0 timeout=30
[2:0:1:0]    disk    DGC      VRAID            0533  /dev/sde 
  state=running queue_depth=30 scsi_level=5 type=0 device_blocked=0 timeout=30
[4:2:0:0]    disk    IBM      ServeRAID M5210  4.27  /dev/sdc 
  state=running queue_depth=256 scsi_level=6 type=0 device_blocked=0 timeout=90
[9:0:0:0]    cd/dvd  Lenovo   SATA ODD 81Y3677 IB00  /dev/sr0 
  state=running queue_depth=1 scsi_level=6 type=5 device_blocked=0 timeout=30
 

使用dd命令设置bs=2M进行测试:

dd if=/dev/zero of=/dev/sdd bs=2M count=1000 oflag=direct

记录了1000+0 的读入 记录了1000+0 的写出 2097152000字节(2.1 GB)已复制,10.6663 秒,197 MB/秒

Device: rrqm/s   wrqm/s   r/s   w/s   rsec/s   wsec/s   avgrq-sz   avgqu-sz   await   svctm   %util
sdd      0.00     0.00   0.00  380.60  0.00    389734.40  1024.00  2.39       6.28    2.56    97.42

可以看到2MB的IO到达底层之后,会变成多个512KB的IO,平均队列长度为2.39,这个硬盘的利用率是97%,MBPS达到了197MB/s。
(为什么会变成512KB的IO,你可以去使用Google去查一下内核参数 max_sectors_kb的意义和使用方法 )也就是说增加队列深度,是可以测试出硬盘的峰值的。

5、Linux系统中查看IO命令iostat详解

 
[root@rac01-node01 /]# iostat -xd 3
Linux 3.8.13-16.2.1.el6uek.x86_64 (rac01-node01)     05/27/2017     _x86_64_    (40 CPU)
Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
sda               0.05     0.75    2.50    0.50    76.59    69.83    48.96     0.00    1.17   0.47   0.14
scd0              0.00     0.00    0.02    0.00     0.11     0.00     5.25     0.00   21.37  20.94   0.05
dm-0              0.00     0.00    2.40    1.24    75.88    69.83    40.00     0.01    1.38   0.38   0.14
dm-1              0.00     0.00    0.02    0.00     0.14     0.00     8.00     0.00    0.65   0.39   0.00
sdc               0.00     0.00    0.01    0.00     0.11     0.00    10.20     0.00    0.28   0.28   0.00
sdb               0.00     0.00    0.01    0.00     0.11     0.00    10.20     0.00    0.15   0.15   0.00
sdd               0.00     0.00    0.01    0.00     0.11     0.00    10.20     0.00    0.25   0.25   0.00
sde               0.00     0.00    0.01    0.00     0.11     0.00    10.20     0.00    0.14   0.14   0.00
 

输出参数描述:

rrqms:每秒这个设备相关的读取请求有多少被Merge了(当系统调用需要读取数据的时候,VFS将请求发到各个FS,如果FS发现不同的读取请求读取的是相同Block的数据,FS会将这个请求合并Merge)
wrqm/s:每秒这个设备相关的写入请求有多少被Merge了。
rsec/s:The number of sectors read from the device per second.
wsec/s:The number of sectors written to the device per second.
rKB/s:The number of kilobytes read from the device per second.
wKB/s:The number of kilobytes written to the device per second.
avgrq-sz:平均请求扇区的大小,The average size (in sectors) of the requests that were issued to the device.
avgqu-sz:是平均请求队列的长度。毫无疑问,队列长度越短越好,The average queue length of the requests that were issued to the device.   
await:每一个IO请求的处理的平均时间(单位是微秒毫秒)。这里可以理解为IO的响应时间,一般地系统IO响应时间应该低于5ms,如果大于10ms就比较大了。
这个时间包括了队列时间和服务时间,也就是说,一般情况下,await大于svctm,它们的差值越小,则说明队列时间越短,反之差值越大,队列时间越长,说明系统出了问题。
svctm:表示平均每次设备I/O操作的服务时间(以毫秒为单位)。如果svctm的值与await很接近,表示几乎没有I/O等待,磁盘性能很好。
如果await的值远高于svctm的值,则表示I/O队列等待太长,系统上运行的应用程序将变慢。
%util: 在统计时间内所有处理IO时间,除以总共统计时间。例如,如果统计间隔1秒,该设备有0.8秒在处理IO,而0.2秒闲置,那么该设备的%util = 0.8/1 = 80%,
所以该参数暗示了设备的繁忙程度,一般地,如果该参数是100%表示磁盘设备已经接近满负荷运行了(当然如果是多磁盘,即使%util是100%,因为磁盘的并发能力,所以磁盘使用未必就到了瓶颈)。

 

 
 
 
 
 
 
本文参考自:
https://www.cnblogs.com/cloudphoenix/p/6542909.html
https://www.cnblogs.com/raykuan/p/6914748.html
posted @ 2022-04-22 14:43  augusite  阅读(2119)  评论(1编辑  收藏  举报