FIO 有众多参数,如果你尚不熟悉这款测试工具,我们总结了最为关键的一系列参数的用法,帮助你自行构造测试。
项 | 说明 |
---|---|
name | 测试任务名称。 |
directory | 测试数据读写路径,即 JuiceFS 挂载点,以下测试以 /jfs 举例。 |
readwrite 、rw | 读写模式,在针对 JuiceFS 的测试中,一般只需要使用 read 、write 、randread 以及 randwrite 这四种模式来分别测试顺序读、顺序写、随机读以及随机写的性能。 |
blocksize 、bs | 每次读写的块数据大小,测试顺序读写场景时设为 1m 或 4m,随机读写场景可以取值 4k~256k,或者结合实际应用场景来设置。 |
numjobs | 并发度,默认每个任务(job)是一个独立的 fork thread,如果不进行特殊设置,那么多个 job 会各自分批读写文件,不会出现多个 job 读写同一文件的情况,这也是我们推荐的测试设置。 |
openfiles | 如无特殊要求(比如使用 time_based 模式),建议携带 --openfiles=1 参数,让每个线程同一时刻只打开和处理 1 个文件。如果按照 FIO 的默认行为,他会打开 nrfiles 个文件,因此文件量增大,JuiceFS 的缓冲区会迅速被挤占、严重影响性能。除非明确要测同时打开多个文件、反复读取他们的时候,否则默认都是单线程内串行处理。 |
filename_format | 一般避免使用该参数。FIO 实际上不支持多个任务并发读取同一批文件,如果使用 filename_format 参数,会产生单线程、无并发的串行读效果,因此必须调整测试用例、改为每个 job 读取自己专属的一批文件。 |
filename | 一般避免使用该参数。比如在顺序写测试中用该参数规定了写入文件名,会造成 FIO 多线程写入同一文件,造成 FUSE 写流量远大于对象存储 PUT 的情况。 |
size | 每个任务处理的文件的总大小。 |
nrfiles | 在每个任务中生成的文件数量。 |
filesize | 每个任务中生成的单个文件大小,满足 filesize * nrfiles = size ,因此该参数不需要和 size 同时设置。 |
direct | 文件读取过后,内核会利用空闲内存对数据、元数据进行缓存,因此往往只有首次读取产生了 FUSE 请求,后续的都通过内核返回了。因此如果没有特殊要求,建议所有测试都应携带 --direct=1 绕过内核页缓存,专注测试 FUSE 挂载点的能力。但如果希望尽可能贴近实际场景、充分利用空闲内存提升 I/O,此时可以去掉 --direct=1 来利用内核缓存。 |
ioengine | 如无特殊要求都应采用默认的 ioengine=psync 。如果实际环境要运行的应用对接了 aio ,那么不仅需要在 FIO 启用 ioengine=libaio ,JuiceFS 挂载点也需要追加 -o async_dio 参数来进行适配。 |
refill_buffers | 默认情况下,FIO 会在测试任务开始时创建用于生成测试文件的数据片段,并一直重用这些数据。使用这个参数后,会在每次 I/O 提交后重新生成数据,保证生成测试文件内容有充分的随机性。 |
end_fsync | 对于 JuiceFS,write 成功以后只是将数据提交到读写缓冲区,并不代表数据就上传到对象存储了,需要 close 或者 fsync 才能触发持久化。因此对于写测试,建议开启该参数,以准确反映 JuiceFS 的性能。 |
file_service_type | 用来定义测试任务中的文件选取方式,有 random 、roundrobin 、sequential 三种。在本章某些测试用例中使用了 file_service_type=sequential 来保证测试任务及时关闭文件、触发 JuiceFS 将写入数据持久化,在这点上使用 end_fsync 更合适。 |
构造测试
本小节提供一系列 FIO 命令示范,帮助你按照自己的实际环境构造测试。
如果测试集中的文件可以读写复用,那么建议先安排写测试,生成的文件正好可以用于后续的读测试。
顺序写测试:
# 顺序写 1 个大文件
fio --name=big-write --directory=/mnt/fio --group_reporting \
--rw=write \
--direct=1 \
--bs=4m \
--end_fsync=1 \
--numjobs=1 \
--nrfiles=1 \
--size=1G
# 并发顺序写 1 个大文件,因此 16 线程一共写 16 个大文件
fio --name=big-write --directory=/mnt/fio --group_reporting \
--rw=write \
--direct=1 \
--bs=4m \
--end_fsync=1 \
--numjobs=16 \
--nrfiles=1 \
--size=1G
# 并发顺序写多个大文件,并发度 16,每个线程写入 64 个大文件
fio --name=big-write --directory=/mnt/fio --group_reporting \
--rw=write \
--direct=1 \
--bs=4m \
--end_fsync=1 \
--numjobs=16 \
--nrfiles=64 \
--size=1G
顺序读测试:
# 测试之前根据需要,可以选择清空本地数据缓存,以及内核缓存
rm -rf /var/jfsCache/*/raw
sysctl -w vm.drop_caches=3
# 顺序读 1 个大文件
fio --name=big-read-single --directory=/jfs/fio --group_reporting \
--rw=read \
--direct=1 \
--bs=4m \
--numjobs=1 \
--nrfiles=1 \
--size=1G
# 顺序读多个大文件
fio --name=big-read-multiple --directory=/jfs/fio --group_reporting \
--rw=read \
--direct=1 \
--bs=4m \
--numjobs=1 \
--nrfiles=64 \
--size=1G
# 并发顺序读多个大文件,线程数 64,每个线程读 64 个文件
fio --name=big-read-multiple-concurrent --group_reporting \
--directory=/jfs/fio \
--rw=read \
--direct=1 \
--bs=4m \
--numjobs=64 \
--nrfiles=64 \
--openfiles=1 \
--size=1G
如果你的场景需要构造随机读写测试,也可以参考上方的测试命令,需要修改的只有以下参数:
- 使用
rw=[randread|randwrite]
来代表随机读和随机写; - 将
bs
按需设置为 4k~128k,或按照真实业务场景的写入大小来设定。
顺序写
对应的 FIO 配置:
sequential-write.fio
[global]
stonewall
group_reporting
openfiles=1
end_fsync=1
ioengine=sync
rw=write
bs=4M
filesize=1G
directory=/jfs/fio-dir
顺序读
sequential-read.fio
[global]
stonewall
group_reporting
openfiles=1
end_fsync=1
ioengine=sync
rw=read
bs=4M
filesize=1G
directory=/jfs/fio-dir
随机写
random-write.fio
[global]
stonewall
group_reporting
openfiles=1
end_fsync=1
ioengine=sync
rw=write
bs=4M
filesize=1G
directory=/jfs/fio-dir
大文件
在日志收集、数据备份、大数据分析等很多场景中,都需要做大文件顺序读写,这也是 JuiceFS 适用的典型场景。
注意:此处需要事先创建不同块大小的 JuiceFS 文件系统并进行挂载,在测试脚本中对 --directory
参数换成对应的 JuiceFS 挂载点。
大文件顺序读
fio --name=big-file-sequential-read \
--directory=/jfs \
--rw=read --refill_buffers \
--bs=256k --size=4G
大文件顺序写
fio --name=big-file-sequential-write \
--directory=/jfs \
--rw=write --refill_buffers \
--bs=256k --size=4G
大文件并发读
fio --name=big-file-multi-read \
--directory=/jfs \
--rw=read --refill_buffers \
--bs=256k --size=4G \
--numjobs={1, 2, 4, 8, 16}
大文件并发写
fio --name=big-file-multi-write \
--directory=/jfs \
--rw=write --refill_buffers \
--bs=256k --size=4G \
--numjobs={1, 2, 4, 8, 16}
大文件随机读
fio --name=big-file-rand-read \
--directory=/jfs \
--rw=randread --refill_buffers \
--size=4G --filename=randread.bin \
--bs={4k, 16k, 64k, 256k} --pre_read=1
sync && echo 3 > /proc/sys/vm/drop_caches
fio --name=big-file-rand-read \
--directory=/jfs \
--rw=randread --refill_buffers \
--size=4G --filename=randread.bin \
--bs={4k, 16k, 64k, 256k}
为了精准地测试大文件随机读的性能,在这里我们先使用 fio
将文件预读取一遍,然后清除内核缓存(sysctl -w vm.drop_caches=3
),接着使用 fio
进行随机读的性能测试。
在大文件随机读的场景,为了获得更好的性能,建议将挂载参数的缓存大小设置为大于将要读取的文件大小。
大文件随机写
fio --name=big-file-random-write \
--directory=/jfs \
--rw=randwrite --refill_buffers \
--size=4G --bs={4k, 16k, 64k, 256k}
小文件
小文件读
fio --name=small-file-seq-read \
--directory=/jfs \
--rw=read --file_service_type=sequential \
--bs={file_size} --filesize={file_size} --nrfiles=1000
小文件写
在默认的 fio 测试行为中会把文件的关闭操作留在任务最后执行,这样在分布式文件系统中存在因网络异常等因素丢失数据的可能。所以我们在 fio 的测试参数中使用了 --file_service_type=sequential
参数,这样 fio 会在测试任务中保证写完一个文件(执行 flush & close,把数据全部写入对象存储)再进行下一个文件。
fio --name=small-file-seq-write \
--directory=/jfs \
--rw=write --file_service_type=sequential \
--bs={file_size} --filesize={file_size} --nrfiles=1000
小文件并发读
fio --name=small-file-multi-read \
--directory=/jfs \
--rw=read --file_service_type=sequential \
--bs=4k --filesize=4k --nrfiles=1000 \
--numjobs={1, 2, 4, 8, 16}
小文件并发写
fio --name=small-file-multi-write \
--directory=/jfs \
--rw=write --file_service_type=sequential \
--bs=4k --filesize=4k --nrfiles=1000 \
--numjobs={1, 2, 4, 8, 16}