简介

一般我们测试硬盘或者存储的性能的时候,会用Linux系统自带的dd命令,因为是自带命令,简单易使用,因此一些客户喜欢使用dd命令来测试磁盘的读写性能。

但是用dd命令来测试性能,有如下问题:

1. dd命令的IO模型单一,只能测试顺序IO,不能测试随机IO。

2. dd命令可设置的参数较少,并且测试结果不一定能反映出磁盘的真实性能。

3. dd命令的设计初衷就不是用例测试性能的,在dd的手册中可以看到。

4. 无法设置队列深度,因此不推荐用dd命令来测试最大读IOPS。

 

因此,我们要使用更专业的磁盘性能测试工具来测试,目前主流的第三方IO测试工具有fio、iometer和Orion。

iometer 是Windows下使用方便,Orion是Oracle的IO测试软件,而FIO是我们比较常用的在Linux系统下(当然在Windows下安装cygwin也可以运行FIO)的IO系统Benchmark和压力测试工具,可以模拟不同IO场景下的IO负载。

FIO支持 13种不同的 I/O 引擎, 包括:sync,mmap, libaio, posixaio, SG v3, splice, null, network,syslet,guasi,solarisaio 等等。

根据实际业务的场景,一般将 I/O 的表现分为四种场景,随机读、随机写、顺序读、顺序写。FIO 工具允许指定具体的应用模式,配合多线程对磁盘进行不同深度的测试。

 

安装

FIO默认已经存在于yum仓库里,所以我们yum安装即可

yum install -y libaio-devel
yum install -y fio-3.7-2.el7.x86_64

运行格式

fio [options] [job options] <job file(s)>

 

 

工作原理

使用 FIO 工具测试硬盘性能,首先确定待模拟IO负载的IO参数,如I/O type,Block size,I/O engine,I/O depth,Target file/device等

然后需要编写一个作业(命令行或者脚本job file)来预定义 FIO 将要以什么样的模式来执行任务。

作业中可以包含任意数量的线程或文件,一个作业的典型模式是定义一部分的全局共享参数global 段和一个(或多个)作业的job 段实体对象(即硬盘)。

运行 FIO 作业时,FIO 会自上而下解析作业中的配置,并做相应的调整去执行任务。

测试完成后,输出测试结果,对FIO测试结果进行分析,评估IO系统的设计或优化方案或确定debug思路。

 

 

FIO 中的常用选项如下
filename: 支持文件系统或者裸设备(硬盘或分区),-filename=/dev/sda2 或 -filename=/tmp/fio_read_test.txt 或 -filename=/dev/sda
direct: 1表示测试过程绕过机器自带的buffer,相当于o_direct,0表示使用bufferio
runtime:测试时长,单位秒,如果不写则直接写5GB文件
time_based: 如果在runtime指定的时间还没到时文件就被读写完成,将继续重复直到runtime时间结束,加上这个参数防止job提前结束。
size: 向硬盘中写入/读取测试文件的大小,可以是绝对值MB,KB,GB,也可以是百分比,占所测磁盘的百分比%,
group_reporting:汇总所有的信息,而不是每个job都显示具体的结果
thread:fio默认会使用fork()创建job进程,如果这个选项设置的话,fio将使用pthread_create来创建线程
numjobs:创建的线程或进程数量,建议等于 CPU 的 core 值,,如果要测试吞吐量,那么numjobs要设置小一点
rw: 读写方式,顺序读read,顺序写write,随机读randread ,随机写randwrite,混合读写randrw,如果要测试吞吐量,需要设为顺序读read,顺序写write
rwmixread:70,混合读写7:3 ,配合rw = randrw
rwmixwrite:30 在混合读写的模式下7:3
bs: 单次io的块大小,一般测试4k, 8k, 64k, 128k, 1M
bsrange: =512-2048 同上,提定数据块的大小范围
allow_mounted_write:允许虚拟机写入
ioengine:FIO 工作时使用的引擎。引擎决定了使用什么样的 I/O,同步模式、异步模式,如果要使用libaio引擎,需要yum install -y libaio-devel包,要使用异步模式引擎才能设置iodepth
iodepth:I/O 引擎如果使用异步模式,要保持多少的队列深度,没有设置则使用操作系统默认队列深度,如果要测试吞吐量,那么iodepth要设置小一点
nrfiles:每个进程/线程生成文件的数量,表示负载将分发到几个文件中。

 

 

 

使用测试

编写 FIO 任务文件

要执行一个 FIO 任务前,需要先定义一个 FIO 任务作业。

按参数输入方式分为如下两种作业定义方式:
(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方式一次测试多个磁盘,每个磁盘写在不同的job段,官方推荐这种方式):如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。

 

FIO 任务脚本模板,如果filename写在global段其他job段不写filename,则跟命令行一样,一次只测试一个磁盘

iostat如何检测磁盘有没有延时 测试磁盘io性能_sed

cat fio.conf
[global]
ioengine=libaio
iodepth=4
direct=1
runtime=60
time_based=1
size=1G
group_reporting=1
thread=1
numjobs=2
bs=4k
name='test'
allow_mounted_write=1
nrfiles=1
#filename=/data/total.txt

[seqread]
rw=read
filename=/data/fio_read_test.txt


[seqwrite]
rw=write
filename=/data/fio_write_test.txt

iostat如何检测磁盘有没有延时 测试磁盘io性能_sed

 

上面的job_file文件转换为命令行形式就是下面的样子

iostat如何检测磁盘有没有延时 测试磁盘io性能_sed

#read 顺序读
fio -ioengine=libaio -direct=1 -bs=4k  -nrfiles=1 -thread -rw=read -size=1G -filename=/data/fio_read_test.txt -name='test' -iodepth=4 -runtime=30 -numjobs=2 -time_based=1 -allow_mounted_write=1 -group_reporting

#write 顺序写
fio -ioengine=libaio -direct=1 -bs=4k  -nrfiles=1 -thread -rw=write -size=1G -filename=/data/fio_write_test.txt -name='test' -iodepth=4 -runtime=30 -numjobs=2 -time_based=1 -allow_mounted_write=1  -group_reporting

iostat如何检测磁盘有没有延时 测试磁盘io性能_sed

 

 

测试结果解读

iostat如何检测磁盘有没有延时 测试磁盘io性能_sed

--------------------------------------------------------------------
展示测试的基本信息
fio fio.conf 
seqread: (g=0): rw=read, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=4
...
seqwrite: (g=0): rw=write, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=4
...
fio-3.7
Starting 4 threads

--------------------------------------------------------------------
总共4个job, seqread是2线程,所以是2,seqwrite是2线程,所以是2,加起来4

Jobs: 4 (f=4): [R(2),W(2)][100.0%][r=89.3MiB/s,w=188KiB/s][r=22.9k,w=47 IOPS][eta 00m:00s]
--------------------------------------------------------------------
重点看这里
IOPS     最大IOPS
BW      最大带宽
延时时间
slat表示磁盘需要多久将io提交到kernel做处理
clat表示命令提交到了内核,提交到内核到io完成的时间。
Lat表示从io结果提创建到clat完成,一般说时延,就是看这个值。
clat各个延时所占的百分比。




seqread: (groupid=0, jobs=4): err= 0: pid=11385: Thu Sep 22 17:09:31 2022
   read: IOPS=20.5k, BW=80.0MiB/s (83.9MB/s)(4803MiB/60015msec)
    slat (nsec): min=1, max=163324k, avg=53835.94, stdev=297380.37
    clat (usec): min=50, max=1179.2k, avg=330.98, stdev=4855.59
     lat (usec): min=99, max=1179.2k, avg=386.15, stdev=4864.96
    clat percentiles (usec):
     |  1.00th=[   113],  5.00th=[   116], 10.00th=[   119], 20.00th=[   123],
     | 30.00th=[   127], 40.00th=[   131], 50.00th=[   137], 60.00th=[   141],
     | 70.00th=[   153], 80.00th=[   314], 90.00th=[   367], 95.00th=[   388],
     | 99.00th=[   490], 99.50th=[   898], 99.90th=[ 58983], 99.95th=[ 90702],
     | 99.99th=[160433]
   bw (  KiB/s): min=    7, max=85555, per=50.24%, avg=41180.02, stdev=16441.93, samples=238
   iops        : min=    1, max=21388, avg=10294.83, stdev=4110.45, samples=238


--------------------------------------------------------------------
同上
 write: IOPS=41, BW=165KiB/s (169kB/s)(9952KiB/60190msec)
    slat (usec): min=13, max=42741, avg=118.82, stdev=1177.06
    clat (msec): min=2, max=1708, avg=193.14, stdev=118.53
     lat (msec): min=2, max=1708, avg=193.26, stdev=118.48
    clat percentiles (msec):
     |  1.00th=[   33],  5.00th=[   77], 10.00th=[   97], 20.00th=[  120],
     | 30.00th=[  142], 40.00th=[  161], 50.00th=[  182], 60.00th=[  205],
     | 70.00th=[  228], 80.00th=[  255], 90.00th=[  288], 95.00th=[  321],
     | 99.00th=[  472], 99.50th=[  726], 99.90th=[ 1703], 99.95th=[ 1703],
     | 99.99th=[ 1703]
   bw (  KiB/s): min=    7, max=  230, per=50.76%, avg=83.76, stdev=35.04, samples=235
   iops        : min=    1, max=   57, avg=20.55, stdev= 8.71, samples=235
   
--------------------------------------------------------------------
lat指的是时延,50=0.06%,表示有0.06%的io时延小于50ms。
  lat (usec)   : 100=0.08%, 250=77.12%, 500=21.64%, 750=0.40%, 1000=0.09%
  lat (msec)   : 2=0.09%, 4=0.09%, 10=0.11%, 20=0.03%, 50=0.05%
  lat (msec)   : 100=0.09%, 250=0.17%, 500=0.04%, 750=0.01%, 1000=0.01%
  
--------------------------------------------------------------------
usr表示用户cpu占用率,sys表示系统cpu占用率,ctx为上下文切换次数,majf是主要页面错误数量,   minf是次要页面错误数量

  
 cpu          : usr=3.85%, sys=27.45%, ctx=20221, majf=0, minf=18
--------------------------------------------------------------------
队列深度,Submit和complete表示同一时段fio发送和完成的io数据量。Issued为发送的io数量,latency用于调节吞吐量直到达到预设的延迟目标。
 
  IO depths    : 1=0.1%, 2=0.1%, 4=100.0%, 8=0.0%, 16=0.0%, 32=0.0%, >=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 rwts: total=1229695,2488,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=4

--------------------------------------------------------------------
最后是结果的汇总
util:磁盘利用率

Run status group 0 (all jobs):
   READ: bw=80.0MiB/s (83.9MB/s), 80.0MiB/s-80.0MiB/s (83.9MB/s-83.9MB/s), io=4803MiB (5037MB), run=60015-60015msec
  WRITE: bw=165KiB/s (169kB/s), 165KiB/s-165KiB/s (169kB/s-169kB/s), io=9952KiB (10.2MB), run=60190-60190msec

Disk stats (read/write):
  sdb: ios=1229695/2526, merge=0/8, ticks=287074/481664, in_queue=767911, util=100.00%

--------------------------------------------------------------------

iostat如何检测磁盘有没有延时 测试磁盘io性能_sed

 

 

 

关于IO队列深度

在某个时刻,有N个inflight的IO请求,也就是缓冲的IO请求,包括在队列中的IO请求,而N就是队列深度。

加大硬盘队列深度就是让硬盘不断工作,减少硬盘的空闲时间。

加大队列深度 -> 提高IO利用率 -> 获得更高的IOPS和MBPS峰值 ,但是队列深度增加了,IO在队列的等待时间也会增加,导致IO响应时间变大,这个是值得考虑的问题。

就好比一部电梯一次只能搭乘一人,那么每个人一但乘上电梯,就能快速达到目的楼层(单个IO响应时间),但其他人需要耗费较长的等待时间(总IO响应时间)

如果增加电梯容量(队列深度)一次搭乘五个人,那么低楼层的人能快速到达目的楼层(单个IO响应时间),高楼层的人需要耗费多一点点时间(总IO响应时间)

 

为何要对磁盘I/O进行队列化处理呢?主要目的是提升应用程序的性能。这一点对于多物理磁盘组成的虚拟磁盘(或LUN)显得尤为重要。

如果一次提交一个I/O,虽然响应时间较短,但系统的吞吐量很小。相比较而言,一次提交多个I/O既缩短了磁头移动距离(通过电梯算法),同时也能够提升IOPS。

因此一次向磁盘系统提交多个I/O能够平衡吞吐量和整体响应时间。

在Centos7下下查看系统默认的队列深度

iostat如何检测磁盘有没有延时 测试磁盘io性能_sed

lsscsi -l
[1:0:0:0]    cd/dvd  NECVMWar VMware IDE CDR10 1.00  /dev/sr0 
  state=running queue_depth=1 scsi_level=6 type=5 device_blocked=0 timeout=30
[2:0:0:0]    disk    VMware   Virtual disk     1.0   /dev/sda 
  state=running queue_depth=32 scsi_level=3 type=0 device_blocked=0 timeout=180
[2:0:1:0]    disk    VMware   Virtual disk     1.0   /dev/sdb 
  state=running queue_depth=32 scsi_level=3 type=0 device_blocked=0 timeout=180

iostat如何检测磁盘有没有延时 测试磁盘io性能_sed

 

 

 

 

FIO 任务命令行模板,filename需要指定被测磁盘所在分区路径

iostat如何检测磁盘有没有延时 测试磁盘io性能_sed

#read 顺序读 吞吐量
fio -ioengine=libaio -direct=1 -bs=4k -thread -rw=read -size=10G -nrfiles=1 -filename=fio_readputth_test.txt -name='fio read test' -iodepth=2 -runtime=120 -numjobs=4 -time_based=1 -allow_mounted_write=1 -group_reporting

#write 顺序写 吞吐量
fio -ioengine=libaio -direct=1 -bs=4k -thread -rw=write -size=10G -nrfiles=1 -filename=fio_writeputth_test.txt -name='fio write test' -iodepth=2 -runtime=120 -numjobs=4 -time_based=1 -allow_mounted_write=1 -group_reporting

 

#read 顺序读
fio -ioengine=libaio -direct=1 -bs=4k -thread -rw=read -size=2G -nrfiles=1 -filename=fio_read_test.txt -name='fio read test' -iodepth=4 -runtime=60 -numjobs=8 -time_based=1 -allow_mounted_write=1 -group_reporting

#write 顺序写
fio -ioengine=libaio -direct=1 -bs=4k -thread -rw=write -size=2G -nrfiles=1 -filename=fio_write_test.txt -name='fio write test' -iodepth=4 -runtime=60 -numjobs=8 -time_based=1 -allow_mounted_write=1 -group_reporting

#readwrite 顺序混合读写
fio -ioengine=libaio -direct=1 -bs=4k -thread -rw=readwrite -size=2G -nrfiles=1 -filename=fio_readwrite_test.txt -name='fio readwrite test' -iodepth=4 -runtime=60 -numjobs=8 -time_based=1 -allow_mounted_write=1 -group_reporting

#randread 随机读
fio -ioengine=libaio -direct=1 -bs=4k -thread -rw=randread -size=2G -nrfiles=1 -filename=fio_randread_test.txt -name='fio randread test' -iodepth=4 -runtime=60 -numjobs=8 -time_based=1 -allow_mounted_write=1 -group_reporting

#randwrite 随机写
fio -ioengine=libaio -direct=1 -bs=4k -thread -rw=randwrite -size=2G -nrfiles=1 -filename=fio_randwrite_test.txt -name='fio randwrite test' -iodepth=4 -runtime=60 -numjobs=8 -time_based=1 -allow_mounted_write=1 -group_reporting

#randrw 随机混合读写
fio -ioengine=libaio -direct=1 -bs=4k -thread -rw=randrw -size=2G -nrfiles=1 -filename=fio_randrw_test.txt -name='fio randrw test' -iodepth=4 -runtime=60 -numjobs=8 -time_based=1 -allow_mounted_write=1 -group_reporting

iostat如何检测磁盘有没有延时 测试磁盘io性能_sed

 

最后,我们可以把结果汇总到表格,方便把报告递给领导

iostat如何检测磁盘有没有延时 测试磁盘io性能_iostat如何检测磁盘有没有延时_11