当前位置:网站首页 > 性能测试 > 正文

FIO 磁盘性能测试_fio测试硬盘

FIO 磁盘性能测试

fio 是一个开源压力测试工具,主要用来测试硬盘 io 性能。这个工具的可定制性非常强,可以根据测试者的想法进行各种混合 io 测试,它支持 13 种不同类型 io 引擎(libaio、sync、mmap、posixaio、network 等等)。它可以测试块设备或文件,可以通过多线程或进程模拟各种 io 操作,可以测试统计 iops、带宽和时延等性能。我们主要使用 fio 工具进行存储性能测试。

磁盘性能基础知识

  1. 寻道时间,是指将读写磁头移动至正确的磁道上所需要的时间。寻道时间越短,I/O 操作越快,目前磁盘的平均寻道时间一般在 3-15ms;
  2. 旋转延迟,是指盘片旋转将请求数据所在扇区移至读写磁头下方所需要的时间。旋转延迟取决于磁盘转速,通常使用磁盘旋转一周所需时间的 1/2 表示。比如,7200rpm 的磁盘平均旋转延迟大约为 60*1000/7200/2 = 4.17ms
  3. 内部接口传送时间,即从磁盘盘片到内部磁盘缓冲传送的时间,量级是 440 微秒。
  4. 外部接口传输时间,即从磁盘缓冲到接口传送的时间,量级是 110 微秒。

对于一个确定的磁盘,旋转延迟保持不变,而内部接口传送时间和外部接口传输时间微秒级别,基本可以忽略不计,那么影响 HDD 性能核心就是寻道时间;

对磁盘性能的度量有两个指标:IOPS,IO 吞吐/带宽;

  1. IOPS 指的是每秒可以完成的 IO 服务的次数,一次 IO 服务主要的耗时是寻道时间上,如果是大量的随机 IO,那么每次寻道时间都处于上限值,IOPS 下降;
    • 理论上下限 = [1000ms/(3ms+4.17ms),1000ms/(15ms+4ms)] = [139,53];但是真实环境下,一个磁盘 IOPS 的值受每次 IO 读写的数据大小,比如 512B,4k,1M 等各个值下 IOPS 肯定是有区别的;
  2. IO 吞吐表示在指定时间内,完成的 IO 读写数据字节数,它的值和每次 IO 读写的数据大小有密切关系,也从而会影响 IOPS,比如每次读写数据块较大,从而最大化的降低了寻道带来的开销,从而提高吞吐,但是此时 IOPS 就会减小;

实例说明:写入 10000 个大小为 1kb 的文件到硬盘上,耗费的时间要比写入 10 个 1mb 大小的文件多得多。

  1. 虽然数据总量都是 10mb,但是 10000 个 1kb 大小的文件在磁道上数据分布不连续,可能需要上千个 IOPS 才能完成数据的全部读取,大大增加了寻道时间,耗时长。
    • 种情况用来指导 HDD IOPS 性能测试,即小数据块多请求次数测试。
  2. 相比较而言,10 个 1mb 大小的文件在磁道上数据分布连续性更好,可能只需要十几个 IOPS 就能完成数据的全部读取,大大降低了寻道时间,耗时少。
    • 这种情况用来指导 HDD IO 吞吐/带宽性能测试,即大数据块少请求次数测试。

对磁盘性能 IOPS 和 IO 吞吐的追求与业务有密切关系,比如大文件存储希望吞吐可以做到最高,而小文件或随机读写的业务更加追求 IOPS;如果业务对吞吐和 IOPS 都有一定依赖,那么就需要找到一个合适的 blocksize 来设置每次 IO 大小,从而在 IOPS 和吞吐之间均衡,即(IOPS × block_size = IO 吞吐);

提供 IO 性能的方法:

  1. IO 合并,即将多个 IO 请求进行合并;
  2. IO 拆封,采用 raid 等磁盘阵列,将一个普通 IO 请求,并发的分发到多个物理磁盘,提供 IO 吞吐
  3. IO 大 cache,每次 IO 写操作都不直接写到物理设备,而是存储在 IO cache 中,直到 cache 满了再更新到磁盘设备中;
  4. IO 预读 Buffer,通过 IO 预判读,虽然一次只读 4k,但是预判 512k 到 buffer,当然这个对 4k 以下的应用来说是一个坑。

NOTE:在计算机程序设计当中,往往从时间局部性、空间局部性及缓存命中率几个角度对程序进行性能优化,而且在计算机领域,这种局部性原理都是通用的。

fio 安装

Ubuntu 环境

sudo apt-get install fio gunplot 
git clone https://github.com/axboe/fio.git 
 ./configure --cc=arm-linux-gnueabihf-gcc --prefix=./build make make install 

确认系统安装是否有 libaio 和 libaio-devel,如果没有安装这两个包,fio 工具不能使用异步的 libaio 引擎。如果在安装 fio 前未安装 libaio 和 libaio-devel,那么安装了 libaio 和 libaio-devel 后需要重新编译安装 fio,不然也无法使用异步引擎 libaio。

使用指导

参数 参数值 解释
filename 设备名或文件名如:裸设备 /dev/sdb,文件 /home/test.img 定义测试对象,一般是设备或者文件。如果想测试裸盘设备 /dev/sdb,可以设置 filename=/dev/sdb;如果想要测试文件系统性能,则可以设置 filename=/home/test.img
name 测试名称 定义测试名称。必填项,本地 fio 测试的名称,对性能没有影响。
rw 测试类型,可选值有:read,write,rw,randread,randwrite,randrw 定义测试的读写类型:read-顺序读,write-顺序写,rw(readwrite)- 混合顺序读写,randread- 随机读,randwrite-随机写,randrw-混合随机读写。对于读写混合类型来说,默认读写比例为 1:1
rwmixwrite rwmixread 混合读写中读写占用比例,可选值为[0,100] 定义在混合读写模式中,写或读所占的比例。举例来说,rwmixread 值为 10,则表示读写比为 10:90。
ioengine io 引擎选择,可选值有 sync、libaio、psync、vsync、mmap 等等 定义 fio 如何下发 io 请求。sync:基本的 read、write io。 libaio:linux 原生的异步 io,linux 只支持 non-buffer 情况下的排队操作。默认值为 psync,该模式为同步 io 模型,异步 io 模型 libaio,一般结合 direct=1 使用。
direct 0 或 1 定义是否使用 direct io:值为 0,表示使用 buffered io;值为 1,表示使用 direct io。
bs 带单位数字 定义 io 的块大小,单位是 k,K,m,M。默认值为 4k。
numjobs 正整数 定义测试的进程/线程数,默认值为 1,如果指定了 thread 参数,则使用单进程多线程模式,否则使用多进程模式。
iodepth 正整数 定义每个进程/线程可以同时下发的 io 任务数,默认为 1,适用于异步 io 场景,同步 io 场景要等前一个 io 任务完成才能下发下一个任务,所以 iodepth 并不起作用。

其他相关参数:

参数 参数值 解释
size 整数 定义 io 操作的数据量,除非指定了 runtime 这个参数,fio 会将指定大小的数据量全部读写完成,才会停止测试。 该参数的值,可以是带单位的数字,比如 size=2G,表示读/写的数据量为 2G;也可以是百分比,比如 size=20%,表示读写的数据量占该设备/文件的 20% 的空间;除了表示 io 操作数据量,size 还表示 io 操作的范围,与 offset 配合一起读写[offset,offset+size]范围内的数据
runtime 正整数 定义测试时间,以秒为单位。对于指定的文件设备,如果在测试时间内完成了读写操作,则会保持相同的负载循环进行读写。
ramp_time 正整数 定义测试的热身时间,以秒为单位。热身时间不计入测试统计。
thinktime 正整数 设置当一个 io 完成后,等待多少时间再下发另一个 io,单位为微妙。一般用于模拟应用等待、处理事务等。
time_based NA 默认值为 0,如果设置了该参数,则本次测试会一直运行到 runtime 结束为止。
offset 定义测试起始位置 fio 在硬盘测试的起始位置,配合顺序读写使用。不同的 offset 对 HDD 的性能影响大,对 SSD 理论无影响。
group_reporting NA 对于测试并发数大于 1 的情况,如果想要将所有进程/线程的测试结果进行统计,输出总的测试结果,而不是各自单独输出,可以使用此参数。
cpus_allowed 指定 cpu 的 core fio 测试的所有进程/线程可以选择的 cpu 核,可以是单独一个或者多个核,也可以是一个范围。
output 结果输出路径 结果输出文件,默认直接把结果打印到命令行,设置后会把结果写进目标文件,命令行则不显示结果。
命令行测试方法
# fio -name=mytest \ -filename=/dev/sdb \ -direct=1 \ -iodepth=20 \ -thread \ -rw=randread \ -ioengine=libaio \ -bs=16k \ -size=5G \ -numjobs=2 \ -runtime=300 \ -group_reporting \ 
  • name=mytest:本次测试的名称 mytest,自已定义。
  • filename=/dev/sdb:测试裸盘的名称,通常选择需要测试的盘的 data 目录,是没有文件系统的裸盘。
  • direct=1:测试过程绕过机器自带的 buffer。使测试结果更真实。
  • iodepth=20:每个线程每次下发 20 个 io 任务。
  • rw=randread:测试随机读的 I/O。
  • ioengine=libaio:io 引擎使用 libaio 模式。
  • bs=16k:单次 io 的块文件大小为 16k。
  • size=5G:本次的测试文件大小为 5G,以每次 16k 的 io 进行测试。
  • numjobs=2:本次的测试线程为 2。
  • runtime=300:测试时间为 300 秒,如果不写则一直将 5G 文件分 16k 每次写完为止。
  • group_reporting:关于显示结果的,汇总每个进程的信息。

顺序读:

fio -name=mytest -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=read -ioengine=libaio -bs=16k -size=5G -numjobs=2 -runtime=300 -group_reporting 

顺序写:

fio -name=mytest -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=write -ioengine=libaio -bs=16k -size=5G -numjobs=2 -runtime=300 -group_reporting 

随机写:

fio -name=mytest -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=randwrite -ioengine=libaio -bs=1k -size=5G -numjobs=2 -runtime=300 -group_reporting 

混合顺序读写:

fio -name=mytest -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=rw -ioengine=libaio -bs=16k -size=5G -numjobs=2 -runtime=300 -group_reporting 

混合随机读写:

fio -name=mytest -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=randrw -ioengine=libaio -bs=16k -size=5G -numjobs=2 -runtime=300 -group_reporting 

测试文件系统

fio -name=mytest -filename=/test/test.img -direct=1 -iodepth=20 -thread -rw=randread -ioengine=libaio -bs=16k -size=5G -numjobs=2 -runtime=300 -group_reporting 
  • filename=/test/test.img:测试文件的名称,为已经做过文件系统的空间。这种方式通常是将一块裸盘,进行分区、格式化,然后挂载到此目录。
配置文件方式

fio 也可以通过执行 job 方式,同时执行多个不同的文件,使用很少的命令执行测试。测试命令:fio job。

job 文件的编写参考:

[global] ioengine=libaio direct=1 size=8g filesize=500g time_based rw=randrw rwmixread=70 bs=4k runtime=120 [job1] filename=/dev/sdb numjobs=16 iodepth=1 [job2] filename=/dev/sdc numjobs=16 iodepth=1 
  • job 文件里的每一个 job 都是同时执行的,global 部分是全局的。
  • 如果想要分开各自执行并统计结果,就要计算一下时间使用 startdelay 参数控制。
  • 使用命令行同时测试多个设备,压力会分布不均。但是通过 job 文件方式配置可以解决,这就相当于开两个窗口同时执行多条命令了。
  • 在 job 配置文件中,增加 group_reporting 参数,在测试完成后就可以看到统计的性能数据了。
测试结果分析
# fio -name=hdd7k -filename=/dev/sdb -direct=1 -iodepth=20 -thread -rw=rw -ioengine=libaio -bs=4k -size=10G -numjobs=10 -runtime=300 -group_reporting hdd7k: (g=0): rw=rw, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=20 ... fio-3.29-7-g01686 Starting 10 threads Jobs: 10 (f=9): [f(10)][100.0%][r=10.9MiB/s,w=10.7MiB/s][r=2785,w=2742 IOPS][eta 00m:00s] hdd7k: (groupid=0, jobs=10): err= 0: pid=73595: Thu Jan 13 11:19:06 2022 read: IOPS=3258, BW=12.7MiB/s (13.3MB/s)(3820MiB/msec) slat (usec): min=2, max=547, avg= 6.47, stdev= 4.04 clat (usec): min=27, max=4722.3k, avg=33300.91, stdev=77842.79 lat (usec): min=47, max=4722.3k, avg=33307.62, stdev=77842.14 clat percentiles (usec): | 1.00th=[ 725], 5.00th=[ 1745], 10.00th=[ 2737], | 20.00th=[ 3228], 30.00th=[ 3687], 40.00th=[ 4621], | 50.00th=[ 5866], 60.00th=[ 7832], 70.00th=[ 14615], | 80.00th=[ 39060], 90.00th=[ ], 95.00th=[ ], | 99.00th=[ ], 99.50th=[ ], 99.90th=[ ], | 99.95th=[], 99.99th=[] bw ( KiB/s): min= 200, max=91945, per=100.00%, avg=13408.99, stdev=1441.98, samples=5840 iops : min= 50, max=22985, avg=3351.87, stdev=360.48, samples=5840 write: IOPS=3261, BW=12.7MiB/s (13.4MB/s)(3823MiB/msec); 0 zone resets slat (usec): min=3, max=532, avg= 6.68, stdev= 4.17 clat (usec): min=38, max=4725.1k, avg=28027.57, stdev=69203.56 lat (usec): min=50, max=4725.2k, avg=28034.48, stdev=69202.94 clat percentiles (usec): | 1.00th=[ 848], 5.00th=[ 1860], 10.00th=[ 2802], | 20.00th=[ 3294], 30.00th=[ 3752], 40.00th=[ 4621], | 50.00th=[ 5800], 60.00th=[ 7439], 70.00th=[ 11994], | 80.00th=[ 27919], 90.00th=[ ], 95.00th=[ ], | 99.00th=[ ], 99.50th=[ ], 99.90th=[ ], | 99.95th=[ ], 99.99th=[] bw ( KiB/s): min= 208, max=91939, per=100.00%, avg=13475.63, stdev=1444.81, samples=5818 iops : min= 52, max=22984, avg=3368.52, stdev=361.18, samples=5818 lat (usec) : 50=0.01%, 100=0.01%, 250=0.06%, 500=0.32%, 750=0.53% lat (usec) : 1000=0.74% lat (msec) : 2=4.20%, 4=28.23%, 10=32.04%, 20=8.83%, 50=8.44% lat (msec) : 100=4.75%, 250=10.40%, 500=1.10%, 750=0.23%, 1000=0.07% lat (msec) : 2000=0.05%, >=2000=0.01% cpu : usr=0.18%, sys=0.45%, ctx=, majf=0, minf=210 IO depths : 1=0.1%, 2=0.1%, 4=0.1%, 8=0.1%, 16=100.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.1%, 64=0.0%, >=64=0.0% issued rwts: total=,,0,0 short=0,0,0,0 dropped=0,0,0,0 latency : target=0, window=0, percentile=100.00%, depth=20 Run status group 0 (all jobs): READ: bw=12.7MiB/s (13.3MB/s), 12.7MiB/s-12.7MiB/s (13.3MB/s-13.3MB/s), io=3820MiB (4005MB), run=-msec WRITE: bw=12.7MiB/s (13.4MB/s), 12.7MiB/s-12.7MiB/s (13.4MB/s-13.4MB/s), io=3823MiB (4009MB), run=-msec Disk stats (read/write): sdb: ios=/, merge=/, ticks=/, in_queue=, util=100.00% 

Fio 正在运行时,会显示任务的状态信息,如下:

# 运行中 Jobs: 10 (f=10): [M(10)][2.7%][r=2508KiB/s,w=2856KiB/s][r=627,w=714 IOPS][eta 04m:52s] # 已完成 Jobs: 10 (f=9): [f(10)][100.0%][r=10.9MiB/s,w=10.7MiB/s][r=2785,w=2742 IOPS][eta 00m:00s] 
  • 第一组方括号内的字符表示每个线程的当前状态。 第一个字符是任务文件中定义的第一个任务,以此类推。 可能的值(按典型的生命周期顺序)是:
P Thread setup, but not started. C Thread created. I Thread initialized, waiting or generating necessary data. p Thread running pre-reading file(s). / Thread is in ramp period. R Running, doing sequential reads. r Running, doing random reads. W Running, doing sequential writes. w Running, doing random writes. M Running, doing mixed sequential reads/writes. m Running, doing mixed random reads/writes. D Running, doing sequential trims. d Running, doing random trims. F Running, currently waiting for fsync(2). V Running, doing verification of written data. f Thread finishing. E Thread exited, not reaped by main thread yet. - Thread reaped. X Thread reaped, exited with an error. K Thread reaped, exited due to signal. 
  • 运行中这一行的意思是:执行 10 个任务,打开文件描述符 10 个,正在运行混合顺序读写共 10 个任务,已完成 2.7%,读 2508KiB/s,写 2856KiB/s;读 IOPS 627,写 IOPS 714,评估需要 04m:52s 完成所有任务。
  • Fio 完成或 ctrl+c 取消都会输出所有结果信息,下面一一解释关键参数:
hdd7k: (groupid=0, jobs=10): err= 0: pid=73595: Thu Jan 13 11:19:06 2022 
  • 定义的任务名称,groupid,聚合的任务数,最后一个错误 id(0 表示没有错误)和 pid,完成时间。
  • read/write/trim:IOPS 表示每秒平均 IOPS;BW 表示平均带宽,BW 后面的数据是 2 进制带宽,括号中是 10 进制带宽;最后两个值表示(二进制总 IO 大小/总运行时间)
  • slat:提交任务延迟(submission latency),也就是异步 IO 指令生成消耗的时间(min 最小时间,max 最大时间,avg 平均时间,stdev 标准差)。对于同步 I/O,这一行不会显示,因为 slat 实际上是完成延迟(completion latency)。这个值的单位可以是纳秒、微秒或毫秒,由 fio 自动选择合适的单位。在 --minimal 模式下,延迟总是以微秒计算。
  • clat:完成延迟(completion latency),这表示从提交 IO 指令到内核再到 IO 指令全部运行所需要的时间,不包括提交任务延迟(submission latency);同步 IO 情况下 clat 通常等于(或非常接近)0,因为从提交到完成仅仅表示 CPU 时间(IO 指令已经生成了)。
  • lat:总延迟(total latency),这表示从 fio 创建 IO 单元到 IO 指令全部运行所花费的时间。
  • clat percentiles:完成延迟百分比分布,注意,从源码来看,它不是 slat + clat;它有自己的结构体描述。理解思路参考下面的 lat(nsec/usec/msec)。
  • bw:基于样本的带宽统计数据。最好是在同一磁盘同一组线程中取样,才具有实际意义,因为磁盘竞争访问才符合实际情况。
  • iops:基于样本的 IOPS 统计数据。同 bw。
  • lat(nsec/usec/msec):IO 完成延迟(I/O completion latencies)的分布情况,这表示从 IO 离开 fio 到它完成的时间。与上面单独的 read/write/trim 部分不同,这里和其余部分中的数据适用于报告组的所有 IO。50=0.01% 意味着在 50us 以下完成了 0.01% 的 IO,100=0.01% 意味着 0.01% 的 IO 需要 50 到 99us 才能完成。依次类推。
  • cpu:CPU 使用率。 用户和系统时间,以及该线程所经历的上下文切换次数、系统和用户时间的使用情况,最后是主要和次要页面错误的数量。 CPU 利用率数字是报告组中任务的平均值,而上下文和故障计数器是求和的。
  • IO depths:任务生命周期内 IO 深度的分布。 这些数字被分成 2 的幂,每个条目涵盖了从该值到低于下一个条目的深度,例如,16= 涵盖了从 16 到 31 的深度。请注意,深度分布条目所覆盖的范围可能与等效的 submit/complete 分布条目所覆盖的范围不同。
  • IO submit:在一个提交调用中提交了多少个 IO 块。 每个条目表示这个数量,直到前一个条目。例如,4=100% 意味着我们每次提交调用提交的 IO 在 1 到 4 之间。 请注意,提交分布条目所覆盖的范围可以不同于等效深度分布条目所覆盖的范围。
  • IO complete:同理 submit,表示完成的 IO 块。
  • IO issued rwt:发布的 read/write/trim 请求数量以及缺少和丢掉的数量。
  • IO latency:这些值用于 latency_target 和相关选项。 当使用这些选项时,该片段描述满足指定时延目标所需的 IO 深度。

以上是报告中的详细参数说明,下面对总结报告数据说明一下:

Run status group 0 (all jobs): READ: bw=12.7MiB/s (13.3MB/s), 12.7MiB/s-12.7MiB/s (13.3MB/s-13.3MB/s), io=3820MiB (4005MB), run=-msec WRITE: bw=12.7MiB/s (13.4MB/s), 12.7MiB/s-12.7MiB/s (13.4MB/s-13.4MB/s), io=3823MiB (4009MB), run=-msec Disk stats (read/write): sdb: ios=/, merge=/, ticks=/, in_queue=, util=100.00% 
  • bw:该组中所有线程的最小和最大带宽紧随该组中所有线程的总带宽。 括号外的值是 2 进制的格式,括号内的值是10 进制格式的等价值。
  • io:整合该组中所有线程执行的 IO。 格式与 bw 相同。
  • run:该组中线程的最小和最长运行时间。
  • ios:所有线程组的 IO 数。
  • merge:IO 调度器执行的合并次数。
  • ticks:保持磁盘忙碌的 ticks 数。
  • in_queue:在磁盘队列中总耗时。
  • util:磁盘利用率。 值为 100% 表示我们一直保持磁盘繁忙,50% 表示磁盘有一半时间处于空闲状态。

注意事项

测试注意事项
  1. fio 测试会对系统的硬盘进行读写,会存在破坏客户数据的风险,因此在生产环境上要谨慎使用,不建议对生产环境的硬盘进行裸盘读写测试。
  2. 如果需要进行测试,最好创建一个文件,然后对指定的文件进行读写。
  3. 如果需要做性能分析或调优,测试过程中需要收集 iostat 信息,例如:iostat -xd 1 1000 > result.log。
性能调优注意事项
  1. FIO 进行性能对比测试的时候,一定要保持参数一致,否则参数差异会导致结果差异。
  2. 在测试文件系统时,要先写一个大文件,然后再做硬盘 IO 读写测试。避免小数据块 IO 模型下读取不到文件而读到其他地方去。
  3. 如果是 SSD,测试过程中最好进行绑核设置,因为 IOPS 太大,如果线程运行在 CPU0 或者是有其他应用占用的核,那么可能会达不到最佳性能。绑核使用 taskset -c 31 ./fio… 命令实现。对应使用哪个核,可以执行 numactl -H 查询核的信息。
到此这篇FIO 磁盘性能测试_fio测试硬盘的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • Neo4j性能测试_neo4j 性能测试2024-10-30 21:17:35
  • 性能测试_手机测试性能网址2024-10-30 21:17:35
  • Lighthouse(灯塔)—— Chrome浏览器强大的性能测试工具_灯塔在线插件2024-10-30 21:17:35
  • 【unity小技巧】unity性能优化以及如何进行性能测试2024-10-30 21:17:35
  • 移动web性能测试工具有哪些呢?_移动web性能测试工具有哪些呢2024-10-30 21:17:35
  • 性能测试报告模板_软件测试用例模板和例子2024-10-30 21:17:35
  • 深入探讨React Native的性能测试策略2024-10-30 21:17:35
  • 浅谈游戏中的性能测试_浅谈游戏中的性能测试论文2024-10-30 21:17:35
  • 软件性能测试基本概述_软件性能测试基本概述怎么写2024-10-30 21:17:35
  • 9个最佳性能测试工具(2024)_主流的性能测试工具2024-10-30 21:17:35
  • 全屏图片