FIO磁盘压测工具

fio,又称为Flexible IO Tester,是Jens Axboe编写的应用程序。Jens是Linux Kernel中block IO subsystem的维护者。FIO作为网络文件系统和磁盘的压测工具,多用于机型验证和文件系统的性能对比,本工具可以自动的分发fio命令到集群的机器列表,搜集小文件的iops和大文件的吞吐。

参数说明

rw=[mode]
rwmixwrite=30 在混合读写的模式下,写占30%

mode
read 顺序读
write 顺序写
readwrite 顺序混合读写
randwrite 随机写
randread 随机读
randrw 随机混合读写

fio -numjobs=1 -iodepth=1 -direct=1 -ioengine=libaio -sync=1 -rw=randread -bs=4k -size=100k -time_based -runtime=60 -name=Fio -directory=/mnt/test2 

结果说明

io=执行了多少M的IO
bw=平均IO带宽
iops=IOPS
runt=线程运行时间
slat=Submission Latency。盘需要多久将IO提交到kernel做处理。
clat=Completion Latency。命令提交到kernel到IO做完之间的时间,不包括submission latency
lat=Total Latency,包括 fio 从创建这个 I/O 单元到完成的总的时间。
bw=带宽
cpu=利用率
IO depths=io队列。用来控制同一时刻发送给OS多少个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完延迟的分布

io=总共执行了多少size的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=磁盘利用率

 

Fio: (g=0): rw=randread, bs=(R) 4096B-4096B, (W) 4096B-4096B, (T) 4096B-4096B, ioengine=libaio, iodepth=1
fio-3.7
Starting 1 process
Jobs: 1 (f=1): [r(1)][100.0%][r=4044KiB/s,w=0KiB/s][r=1011,w=0 IOPS][eta 00m:00s]
Fio: (groupid=0, jobs=1): err= 0: pid=22131: Tue Feb  4 21:08:19 2020
   read: IOPS=1042, BW=4171KiB/s (4271kB/s)(244MiB/60001msec)
    slat (nsec): min=5266, max=96336, avg=9332.63, stdev=1772.18
    clat (usec): min=678, max=24547, avg=945.78, stdev=147.56
     lat (usec): min=688, max=24558, avg=955.93, stdev=147.64
    clat percentiles (usec):
     |  1.00th=[  783],  5.00th=[  824], 10.00th=[  857], 20.00th=[  881],
     | 30.00th=[  906], 40.00th=[  922], 50.00th=[  947], 60.00th=[  963],
     | 70.00th=[  979], 80.00th=[ 1004], 90.00th=[ 1037], 95.00th=[ 1057],
     | 99.00th=[ 1139], 99.50th=[ 1172], 99.90th=[ 1418], 99.95th=[ 2606],
     | 99.99th=[ 5800]
   bw (  KiB/s): min= 3840, max= 4432, per=100.00%, avg=4171.03, stdev=118.37, samples=119
   iops        : min=  960, max= 1108, avg=1042.71, stdev=29.57, samples=119
  lat (usec)   : 750=0.22%, 1000=79.80%
  lat (msec)   : 2=19.92%, 4=0.04%, 10=0.03%, 50=0.01%
  cpu          : usr=0.64%, sys=2.22%, ctx=62565, majf=0, minf=32
  IO depths    : 1=100.0%, 2=0.0%, 4=0.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=62562,0,0,0 short=0,0,0,0 dropped=0,0,0,0
     latency   : target=0, window=0, percentile=100.00%, depth=1

Run status group 0 (all jobs):
   READ: bw=4171KiB/s (4271kB/s), 4171KiB/s-4171KiB/s (4271kB/s-4271kB/s), io=244MiB (256MB), run=60001-60001msec

 

示例

hostfile: 配置机器的list, #注释的机器会自动过滤掉

#!/bin/bash
################################################
#Author : Li Qingbing
#Date   : 2019-08-01
################################################
########## Config ###############
hostfile="hosts"
oplist=('randread' 'randwrite' 'read' 'write')
host=($(awk '{print $1}' $hostfile  | grep -v '#'))
blocksize=('1K' '4K' '8K' '16K' '32K' '64K' '128K')
filesize=('1G' '1G' '1G' '1G' '1G' '1G' '1G' )
mountpath='/data/redkv/kv-0'
toolpath='fio'
####################################################
len=${#host[@]}

for(( s = 0; s < ${#blocksize[@]}; s = s + 1 ))
do
    for(( op = 0; op < ${#oplist[@]}; op = op + 1 ))
    do
        cmd="$toolpath -numjobs=1 -iodepth=8 -direct=1 -ioengine=libaio -sync=1 -rw=${oplist[$op]}  -bs=${blocksize[$s]} -size=${filesize[$s]} -time_based -runtime=60 -name=Fio -directory=$mountpath/io"

        for(( client = 2; client <= $len; client = client + 1 ))
        do
            name=$(echo $RANDOM)
            for(( i = 2; i <= $client; i = i + 1 ))
            do
                num=$((i-1))
                echo "[`printf \"%2s\" $i`] ssh ${host[$num]} \"$cmd$i > /tmp/${name}.log &\""
                eval "ssh ${host[$num]} mkdir -p $mountpath/io$i"
                eval "ssh ${host[$num]} \"$cmd$i | tee /tmp/${name}.log &\" &"
            done
        done

        echo "Sleep 65 seconds"
        sleep 65

        for(( i = 1; i <= $client; i = i + 1 ))
        do
            num=$((i-1))
            eval "scp ${host[$num]}:/tmp/${name}.log ${host[$num]}.log.$name.${oplist[$op]}.${blocksize[$s]}.${filesize[$s]}"
        done

    done
done

处理数据

#!/bin/python

import os
import commands
import json
import argparse

g_dataType = ['read', 'write', 'randread', 'randwrite']
g_blockSize = ['1K', '4K', '8K', '16K', '32K', '64K', '128K', '1M']

def scan_files(path):
    if not os.path.exists(path):
        print("Give path cannot be found")
        exit(0)

    cmd = "/bin/ls *.log.*"
    ret, output = commands.getstatusoutput(cmd)
    if ret != 0:
        print("Command execute error, ret: %s, msg: %s" % (ret, output))
        exit(0)
    else:
        return output.split('\n')

def load_hosts(path):
    if not os.path.exists(path):
        print("Given path cannot find host file")
        exit(0)
    data = []
    with open(path) as fd:
        content = fd.read()
        for host in content.split("\n"):
            if not host.startswith("#") and host.strip():
                data.append(host)
    return data

def parse_file(files, path, hosts):
    if len(files) == 0:
        exit(0)

    out = []
    for host in hosts:
        ot = {}
        ot['host'] = host
        ot['data'] = {}
        ret = {}
        for t in g_dataType:
            ret[t] = {}
            for file in files:
                # 10.0.20.236.log.16003.write.16K.1G
                dt = str(file).split(".")
                if dt[-3] == t and host == ".".join(dt[0:4]):
                    ret[t][dt[-2]] = {}
                    with open(path + "/" + file) as fd:
                        content = fd.read().split("\n")
                        data = {}
                        for line in content:
                            # read: IOPS=77.1k, BW=2409MiB/s (2526MB/s)(141GiB/60001msec)
                            if "IOPS=" in line.strip():
                                item = line.strip().replace(',', '').split(' ')
                                data['iops'] = item[1].split('=')[1]
                                data['bw'] = item[2].split('=')[1]

                            # lat (usec): min=1929, max=19675, avg=10419.65, stdev=450.80
                            if line.strip().startswith("lat (usec):") or line.strip().startswith("lat (msec):"):
                                item = line.strip().replace(',', '').split(' ')
                                data['lat'] = {}
                                if item[1] == "(msec):":
                                    data['lat']['min'] = float(item[2].split('=')[1]) * 1000
                                    data['lat']['max'] = float(item[3].split('=')[1]) * 1000
                                    data['lat']['avg'] = float(item[4].split('=')[1]) * 1000
                                else:
                                    data['lat']['min'] = item[2].split('=')[1]
                                    data['lat']['max'] = item[3].split('=')[1]
                                    data['lat']['avg'] = item[4].split('=')[1]
                                data['lat']['stdev'] = item[5].split('=')[1]

                            if line.strip().startswith("clat percentiles"):
                                data['lat']['unit'] = line[-6:-2]
                                continue
                            #  99.00th=[  184], 99.50th=[  190], 99.90th=[  192], 99.95th=[  194]
                            pct =  ""
                            if "99.00th" in line.strip():
                                item = line.strip().strip("|").strip(",").split(",")
                                for v in item:
                                    m = v.split('=')[0][0:6].strip() # 99.00%
                                    n = v.split('=')[1][1:len(v.split('=')[1]) -1].strip()
                                    if data['lat']['unit'] and data['lat']['unit'] == "msec":
                                        n = int(float(n) * 1000)
                                    if m == "99.00":
                                        pct += "\033[31m%9sus\033[0m " % (str(n))
                                    else:
                                        pct += "%9sus " % (str(n))
                                data['lat']['pct'] = pct
                                break
                    ret[t][dt[-2]] = data
            ot['data'] = ret
        out.append(ot)
    return out

def format_data(data):
    if not data:
        exit(0)

    for item in data:
        for t in g_dataType:
            dt = item['data'][t]

            #u = {int(k[:-1]):v for k, v in dt.iteritems()}
            u = dt
            if dt:
                print "*"*140
                print "%40s | %s" % (t, item['host'])
                print "-"*140
                print "%10s %10s %10s %12s %12s %12s %13s %10s %10s %10s %10s" % ("BlockSize", "IOPS", "BandWidth", "Latency-min", "Latency-max", "Latency-avg","Latency-stdev", "Latency-P99", "Latency-P995", "Latency-P999", "Latency-P9995")
                print "-"*140
            else:
                continue
            for k in g_blockSize:
                if k not in u.keys():
                    continue
                print "%10s %10s %10s %10sus %10sus \033[32m%10sus\033[0m %13s %10s" % (k, u[k]['iops'], u[k]['bw'], u[k]['lat']['min'], u[k]['lat']['max'],u[k]['lat']['avg'],u[k]['lat']['stdev'],u[k]['lat']['pct'])
            print "\n"

def format_output_json(data):
    return json.dumps(data, indent=4)

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description='fio test result analysis tool')
    parser.add_argument('-f', '--host', type=str, default='./hosts', help='Host file, one host per line, canbe annotated with #')
    args = parser.parse_args()

    files = scan_files("./")
    hosts = load_hosts(args.host)
    data = parse_file(files, "./", hosts)
    format_data(data)

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值