fio 磁盘性能测试

FIO是测试IOPS的非常好的工具,用来对硬件进行压力测试和验证,支持13种不同的I/O引擎,包括:sync,mmap, libaio, posixaio, SG v3, splice, null, network, syslet, guasi, solarisaio 等等。

apt-get install libaio-dev fio
yum install libaio-devel fio

3.1 fio参数

  • filename: 测试对象设备或文件名称;
  • direct: 测试过程绕过机器自带的buffer,使测试结果更真实;
  • rw: 读写模式,有顺序写write、顺序读read、随机写randwrite、随机读randread等;
  • bs: 单次io的块文件大小;
  • bsrange=512-2048 同上,提定数据块的大小范围;
  • size: 测试文件大小为5g,以每次4k的io进行测试;
  • iodepth: 队列深度,只有使用libaio时才有意义,这是一个可以影响IOPS的参数;
  • ioengine: 负载引擎,我们一般使用libaio,发起异步IO请求;
  • numjobs=30 本次的测试线程为30;
  • runtime: 测试时间(秒),如果不写则一直将$zise文件分$bs每次写完为止;
  • rwmixwrite=30 在混合读写的模式下,写占30%;
  • group_reporting 关于显示结果的,汇总每个进程的信息;
  • lockmem=1g 只使用1g内存进行测试;
  • zero_buffers 用0初始化系统buffer;
  • nrfiles=8 每个进程生成文件的数量;

3.2 fio.sh

Note: 脚本命令格式:./fio.sh 2G 40G /mnt/test_file

fio.sh
#!/bin/bash
# 该脚本分别测试目标目录所在磁盘的"随机读,写","连续,写"性能.
# mrco@xbits.net 2019-09-07
cd `dirname $0`
test_file=$3
size_4k=$1
size_128k=$2
runtime=60
iodepth=64
direct=1
out1=/tmp/fio1.json
out2=/tmp/fio2.json
out3=/tmp/fio3.json
out4=/tmp/fio4.json
out5=/tmp/fio5.json
out6=/tmp/fio6.json
fio_out="`dirname $3`/fio.out"
 
benchmark(){
# 4k iodepth=1
fio --name=randread-1 --rw=randread --iodepth=1 --ioengine=libaio --thread --direct=${direct} --norandommap --bs=4k --size=${size_4k} --runtime=${runtime} --filename=${test_file} --minimal --output-format=json > ${out1}
fio --name=randwrite-1 --rw=randwrite --iodepth=1 --ioengine=libaio --thread --direct=${direct} --norandommap --bs=4k --size=${size_4k} --runtime=${runtime} --filename=${test_file} --minimal --output-format=json > ${out2}
# 4k iodepth=64
fio --name=randread-1 --rw=randread --iodepth=${iodepth} --ioengine=libaio --thread --direct=${direct} --norandommap --bs=4k --size=${size_4k} --runtime=${runtime} --filename=${test_file} --minimal --output-format=json > ${out3}
fio --name=randwrite-${iodepth} --rw=randwrite --iodepth=${iodepth} --ioengine=libaio --thread --direct=${direct} --norandommap --bs=4k --size=${size_4k} --runtime=${runtime} --filename=${test_file} --minimal --output-format=json > ${out4}
# seq read
fio --name=seq-read --rw=read --iodepth=${iodepth} --ioengine=libaio --thread --direct=${direct} --norandommap --bs=128k --size=${size_128k} --runtime=${runtime} --filename=${test_file} --minimal --output-format=json > ${out5}
# seq write
fio --name=seq-write --rw=write --iodepth=${iodepth} --ioengine=libaio --thread --direct=${direct} --norandommap --bs=128k --size=${size_128k} --runtime=${runtime} --filename=${test_file} --minimal --output-format=json > ${out6}
# 提取测试结果
randread_iops=`jq .jobs[0].read.iops ${out1} | numfmt --to=iec`
randread_bw="`jq .jobs[0].read.bw ${out1}` KB/s"
randread_iops_1=`jq .jobs[0].read.iops ${out3} | numfmt --to=iec`
randread_bw_1="`jq .jobs[0].read.bw ${out3}` KB/s"
randwrite_iops=`jq .jobs[0].write.iops ${out2} | numfmt --to=iec`
randwrite_bw="`jq .jobs[0].write.bw ${out2}` KB/s"
randwrite_iops_1=`jq .jobs[0].write.iops ${out4} | numfmt --to=iec`
randwrite_bw_1="`jq .jobs[0].write.bw ${out4}` KB/s"
seqread_iops=`jq .jobs[0].read.iops ${out5} | numfmt --to=iec`
seqread_bw="`jq .jobs[0].read.bw ${out5}` KB/s"
seqwrite_iops=`jq .jobs[0].write.iops ${out6} | numfmt --to=iec`
seqwrite_bw="`jq .jobs[0].write.bw ${out6}` KB/s"
# 将结果写入文件
echo "Project,IOPS(read),bandwidth(read),IOPS(write),bandwidth(write)" > /tmp/output.txt
echo "4K,$randread_iops,$randread_bw,$randwrite_iops,$randwrite_bw" >> /tmp/output.txt
echo "4K-$iodepth,$randread_iops_1,$randread_bw_1,$randwrite_iops_1,$randwrite_bw_1" >> /tmp/output.txt
echo "128k-seq,$seqread_iops,$seqread_bw,$seqwrite_iops,$seqwrite_bw" >> /tmp/output.txt
}
 
clean_files(){
# 删除测试文件
rm -f ${test_file} /tmp/fio*.json /tmp/output.txt
}
 
# 处理输出为表格,https://stackoverflow.com/questions/12768907/how-to-align-the-columns-of-tables-in-bash
printTable()
{
    local -r delimiter="${1}"
    local -r data="$(removeEmptyLines "${2}")"
 
    if [[ "${delimiter}" != '' && "$(isEmptyString "${data}")" = 'false' ]]
    then
        local -r numberOfLines="$(wc -l <<< "${data}")"
 
        if [[ "${numberOfLines}" -gt '0' ]]
        then
            local table=''
            local i=1
 
            for ((i = 1; i <= "${numberOfLines}"; i = i + 1))
            do
                local line=''
                line="$(sed "${i}q;d" <<< "${data}")"
 
                local numberOfColumns='0'
                numberOfColumns="$(awk -F "${delimiter}" '{print NF}' <<< "${line}")"
 
                # Add Line Delimiter
                if [[ "${i}" -eq '1' ]]
                then
                    table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")"
                fi
 
                # Add Header Or Body
                table="${table}\n"
                local j=1
 
                for ((j = 1; j <= "${numberOfColumns}"; j = j + 1))
                do
                    table="${table}$(printf '#| %s' "$(cut -d "${delimiter}" -f "${j}" <<< "${line}")")"
                done
 
                table="${table}#|\n"
 
                # Add Line Delimiter
                if [[ "${i}" -eq '1' ]] || [[ "${numberOfLines}" -gt '1' && "${i}" -eq "${numberOfLines}" ]]
                then
                    table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")"
                fi
            done
 
            if [[ "$(isEmptyString "${table}")" = 'false' ]]
            then
                echo -e "${table}" | column -s '#' -t | awk '/^\+/{gsub(" ", "-", $0)}1'
            fi
        fi
    fi
}
 
removeEmptyLines()
{
    local -r content="${1}"
    echo -e "${content}" | sed '/^\s*$/d'
}
 
repeatString()
{
    local -r string="${1}"
    local -r numberToRepeat="${2}"
    if [[ "${string}" != '' && "${numberToRepeat}" =~ ^[1-9][0-9]*$ ]]
    then
        local -r result="$(printf "%${numberToRepeat}s")"
        echo -e "${result// /${string}}"
    fi
}
 
isEmptyString()
{
    local -r string="${1}"
    if [[ "$(trimString "${string}")" = '' ]]
    then
        echo 'true' && return 0
    fi
    echo 'false' && return 1
}
 
trimString()
{
    local -r string="${1}"
    sed 's,^[[:blank:]]*,,' <<< "${string}" | sed 's,[[:blank:]]*$,,'
}
 
output(){
printTable ',' "$(cat /tmp/output.txt)" | tee ${fio_out}
}
 
#source printtable.sh
#printf "\t%-15s %-15s %-15s %-15s %-15s %-15s %-15s\n" 测试项目 读IOPS 带宽 写IOPS 带宽
#printf "\t%-10s %-10s %-5s %-14s %-10s %-4s %-3s\n" 4K $randread_iops $randread_bw $randwrite_iops $randwrite_bw
#printf "\t%-10s %-10s %-5s %-14s %-10s %-4s %-3s\n" 4K-$iodepth $randread_iops_1 $randread_bw_1 $randwrite_iops_1 $randwrite_bw_1
#printf "\t%-10s %-10s %-5s %-14s %-10s %-4s %-3s\n" 128k-seq $seqread_iops $seqread_bw $seqwrite_iops $seqwrite_bw
 
# 逻辑处理
if [[ $# -lt 3 ]];then
	echo "命令格式: ./fio size_4k size_128k /path/to/test_file"
	exit
else
	if [[ -n `which jq` ]] && [[ -n `which fio` ]];then
		benchmark
		output
		clean_files
	else
		echo -e '\033[32mNo jq or fio found,try to install... \033[0m'
		yum install -y jq fio > /dev/null 2>&1
		apt install -y jq fio > /dev/null 2>&1
		apt-get install -y jq fio > /dev/null 2>&1
		echo -e '\033[32mBenchmarking... \033[0m'
		benchmark
		output
		clean_files
	fi
fi

3.3 更多实例

#随机读
fio -filename=/dev/sdb1 -direct=1 -iodepth 1 -thread -rw=randread -ioengine=psync -bs=16k -size=200G -numjobs=10 -runtime=1000 -group_reporting -name=mytest 
#顺序读
fio -filename=/dev/sdb1 -direct=1 -iodepth 1 -thread -rw=read -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=1000 -group_reporting -name=mytest 
#随机写
fio -filename=/dev/sdb1 -direct=1 -iodepth 1 -thread -rw=randwrite -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=1000 -group_reporting -name=mytest 
#顺序写
fio -filename=/dev/sdb1 -direct=1 -iodepth 1 -thread -rw=write -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=1000 -group_reporting -name=mytest 
#混合随机读写 
fio -filename=/dev/sdb1 -direct=1 -iodepth 1 -thread -rw=randrw -rwmixread=70 -ioengine=psync -bs=16k -size=200G -numjobs=30 -runtime=100 -group_reporting -name=mytest -ioscheduler=noop

3.3.1 4K随机写测试

fio -ioengine=libaio -bs=4k -direct=1 -thread -rw=randwrite -size=100G -filename=/dev/vdb -name="EBS 4KB randwrite test" -iodepth=32 -runtime=60
  • 蓝色方框表示IOPS是5900,在正常的误差范围内;
  • 绿色方框表示IO请求的平均响应时间为5.42ms;
  • 黄色方框表示95%的IO请求的响应时间小于等于 6.24 ms;

3.3.2 4K随机读测试

fio -ioengine=libaio -bs=4k -direct=1 -thread -rw=randread -size=100G -filename=/dev/vdb -name="EBS 4KB randread test" -iodepth=8 -runtime=60

3.3.3 512KB顺序写测试

测试512KB顺序写,以判断最大MBPS(吞吐率)是多少,测试参数和测试结果如下所示.

fio -ioengine=libaio -bs=512k -direct=1 -thread -rw=write -size=100G -filename=/dev/vdb -name="EBS 512KB seqwrite test" -iodepth=64 -runtime=60
  • 蓝色方框表示MBPS为174226KB/s,约为170MB/s.


  • linux/performance/磁盘性能测试_fio.txt
  • 最后更改: 2019/09/07 16:49
  • 由 mrco