http://www.idcat.cn
在Linux系统上,查看磁盘的负载情况,咱们一般使用iostat监控工具,iostat的详细介绍查看另外的培训资料。其中很重要的参数就是await,await表示单个I/O所需的平均时间,但它同时包含了I/O Scheduler所消耗的时间和硬件所消耗的时间,所以不能作为硬件性能的指标。那如何才能分辨一个io从下发到返回整个时间上,是硬件层耗时多还是在io调度上耗时多呢?如何查看io在各个时间段所消耗的时间呢?那么,blktrace在这种场合就能派上用场,因为它能记录I/O所经历的各个步骤,从中可以分析是IO Scheduler慢还是硬件响应慢,以及各个时间段所用时间。
blktrace的原理
一个I/O请求进入block layer之后,可能会经历下面的过程:
Remap: 可能被DM(Device Mapper)或MD(Multiple Device, Software RAID) remap到其它设备
Split: 可能会因为I/O请求与扇区边界未对齐、或者size太大而被分拆(split)成多个物理I/O
Merge: 可能会因为与其它I/O请求的物理位置相邻而合并(merge)成一个I/O
被IO Scheduler依照调度策略发送给driver
被driver提交给硬件,经过HBA、电缆(光纤、网线等)、交换机(SAN或网络)、最后到达存储设备,设备完成IO请求之后再把结果发回。
blktrace能记录I/O所经历的各个步骤,来看一下它记录的数据,包含9个字段,下图标示了其中8个字段的含义,大致的意思是“哪个进程在访问哪个硬盘的哪个扇区,进行什么操作,进行到哪个步骤,时间戳是多少”:
-第一个字段:8,0 这个字段是设备号 major device ID和minor device ID。-第二个字段:3 表示CPU
-第三个字段:11 序列号
-第四个字段:0.009507758 Time Stamp是时间偏移
-第五个字段:PID 本次IO对应的进程ID
-第六个字段:Event,这个字段非常重要,反映了IO进行到了那一步
-第七个字段:R表示 Read, W是Write,D表示block,B表示BarrierOperation
-第八个字段:223490+56,表示的是起始block number 和 number of blocks,即我们常说的Offset 和 Size(扇区??)
-第九个字段: 进程名
其中第六个字段非常有用:每一个字母都代表了IO请求所经历的某个阶段。
A 映射值对应设备 IO was remapped to a different device
B IO反弹,由于32位地址长度限制,所以需要copy数据到低位内存,这会有性能损耗。IO bounced
C IO完成 IO completion
D 将IO发送给驱动 IO issued to driver
F IO请求,前合并 IO front merged with request on queue
G 获取 请求 Get request
I IO插入请求队列 IO inserted onto request queue
M IO请求,后合并 IO back merged with request on queue
P 插上块设备队列(队列插入机制) Plug request
Q io被请求队列处理代码接管。 IO handled by request queue code
S 等待发送请求。 Sleep request
T 由于超时而拔出设备队列 Unplug due to timeout
U 拔出设备队列 Unplug request
X 开始新的扇区 Split
其中最重要的几个阶段如下:
Q – 即将生成IO请求
|
G – IO请求生成
|
I – IO请求进入IO Scheduler队列
|
D – IO请求进入driver
|
C – IO请求执行完毕
根据以上步骤对应的时间戳就可以计算出I/O请求在每个阶段所消耗的时间:
Q2G – 生成IO请求所消耗的时间,包括remap和split的时间;
G2I – IO请求进入IO Scheduler所消耗的时间,包括merge的时间;
I2D – IO请求在IO Scheduler中等待的时间;
D2C – IO请求在driver和硬件上所消耗的时间;
Q2C – 整个IO请求所消耗的时间(Q2I + I2D + D2C = Q2C),相当于iostat的await。
如果I/O性能慢的话,以上指标有助于进一步定位缓慢发生的地方:
D2C可以作为硬件性能的指标;
I2D可以作为IO Scheduler性能的指标。
blktrace的用法
blktrace centos7安装
yum install blktrace -y
会自动生成blktrace blkparse btt 3个工具,其中,blktrace收集数据,blkparce分析数据,btt汇总数据。
blktrace的用法
使用blktrace需要挂载debugfs:
$ mount -t debugfs debugfs /sys/kernel/debug
利用blktrace查看实时数据的方法,比如要看的硬盘是sdb:
$ blktrace -d /dev/sdb -o – | blkparse -i –
需要停止的时候,按Ctrl-C。
个人常用方法:
blktrace -d /dev/sdc
生成数据: 应用结束后,手动终止监控,会生成cpu数量的文件
blkparse -i sdc -d sdc.blktrace.bin
分析数据: btt
btt -i sdc.blktrace.bin -A |less
汇总后,部分截图
根据以上步骤对应的时间戳就可以计算出I/O请求在每个阶段所消耗的时间:
Q2G – 生成IO请求所消耗的时间,包括remap和split的时间;
G2I – IO请求进入IO Scheduler所消耗的时间,包括merge的时间;
I2D – IO请求在IO Scheduler中等待的时间;D2C – IO请求在driver和硬件上所消耗的时间;
Q2C – 整个IO请求所消耗的时间(Q2I + I2D + D2C = Q2C),相当于iostat的await。
如果I/O性能慢的话,以上指标有助于进一步定位缓慢发生的地方:
D2C可以作为硬件性能的指标;I2D可以作为IO Scheduler性能的指标。
btt 可以生成iops,bw等很多数据
man btt
btt
[ -a | –seek-absolute ]
[ -A | –all-data ]
[ -B | –dump-blocknos= ]
[ -d | –range-delta= ]
[ -D
[ -e
[ -h | –help ]
[ -i | –input-file= ]
[ -I | –iostat= ]
[ -l | –d2c-latencies= ]
[ -L | –periodic-latencies= ]
[ -m | –seeks-per-second= ]
[ -M | –dev-maps=
[ -o | –output-file= ]
[ -p | –per-io-dump= ]
[ -P | –per-io-trees= ]
[ -q | –q2c-latencies= ]
[ -Q | –active-queue-depth= ]
[ -r | –no-remaps ]
[ -s | –seeks= ]
[ -S | –iostat-interval= ]
[ -t | –time-start= ]
[ -T | –time-end= ]
[ -u | –unplug-hist= ]
[ -v | –verbose ]
[ -V | –version ]
[ -X | –easy-parse-avgs ]
[ -z | –q2d-latencies= ]
[ -Z | –do-active ]
测试
1、4k direct=1
通过fio对本地磁盘sdc挂载点进行4K小文件顺序写测试,期间监控磁盘信息。完成fio测试后,手动终止监控程序,然后进行数据分析。并与iostat的数据进行对比分析。
fio 开启测试
[root@node1 samba-test]# fio -filename=./4k_file -direct=1 -iodepth=1 -thread -rw=write -ioengine=libaio -bs=4k -size=3G -numjobs=4 -times=300 -group_reporting -name=mytest2
mytest2: (g=0): rw=write, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=1
…
fio-2.2.5
Starting 4 threads
注意:
1、direct=1即o_direct直写io。不会经过系统缓存,即没有合并
2、numjobs=4 bs=4k 对应iostat即队列深度为4.
3、bs=4k 对应iostat扇区数为8
[root@node1 ~]# blktrace -d /dev/sdc 开启监控磁盘sdc
iostat监控
FIO运行结束后,终止blktrace进程
收集数据
[root@node1 fioblk]# blktrace -d /dev/sdc
^C=== sdc ===
CPU 0: 5504242 events, 258012 KiB data
CPU 1: 5693967 events, 266905 KiB data
Total: 11198209 events (dropped 0), 524917 KiB data
[root@node1 fioblk]# ll
total 524920
-rw-r–r– 1 root root 264203656 Apr 27 14:08 sdc.blktrace.0
-rw-r–r– 1 root root 273310432 Apr 27 14:08 sdc.blktrace.1
[root@node1 fioblk]#
会看到生成文件数以当前cpu个数命名。
生成数据
blkparse -i sdc -d sdc.blktrace.bin
会将所有数据汇总到文件sdc.blktrace.bin里面。期间我截图了如下,代表3个io的过程。对比上面介绍。
WS表示同步写
分析数据
生成所有数据,后面介绍针对性的数据。
[root@node1 fioblk]# btt -i sdc.blktrace.bin -A |less
Q2G – 生成IO请求所消耗的时间,包括remap和split的时间;本例0.009ms
G2I – IO请求进入IO Scheduler所消耗的时间,包括merge的时间;0.001ms
I2D – IO请求在IO Scheduler中等待的时间;0.0009ms
D2C – IO请求在driver和硬件上所消耗的时间;0.7ms
因此:
Q2C – 整个IO请求所消耗的时间(Q2I + I2D + D2C = Q2C),相当于iostat的await。 几乎0.78ms与iostat wait值一致。在io scheduler上几乎没有消耗任何时间。
生成iops 和bw每秒值
btt -i sdc.blktrace.bin -q sdc.q2c_latencytotal
-rw-r–r– 1 root root 2759 Apr 27 14:45 8,32_iops_fp.dat
-rw-r–r– 1 root root 4340 Apr 27 14:45 8,32_mbps_fp.dat
-rw-r–r– 1 root root 264203656 Apr 27 14:08 sdc.blktrace.0
-rw-r–r– 1 root root 273310432 Apr 27 14:08 sdc.blktrace.1
-rw-r–r– 1 root root 537514088 Apr 27 14:16 sdc.blktrace.bin
-rw-r–r– 1 root root 30244692 Apr 27 14:45 sdc.q2c_latency_8,32_q2c.dat
-rw-r–r– 1 root root 2759 Apr 27 14:45 sys_iops_fp.dat
-rw-r–r– 1 root root 4340 Apr 27 14:45 sys_mbps_fp.dat
生成io大小分布
btt -i sdc.blktrace.bin -B sdc.offset
[root@node1 fioblk]# ll
total 1183168
-rw-r–r– 1 root root 2759 Apr 27 14:57 8,32_iops_fp.dat
-rw-r–r– 1 root root 4340 Apr 27 14:57 8,32_mbps_fp.dat
-rw-r–r– 1 root root 264203656 Apr 27 14:08 sdc.blktrace.0
-rw-r–r– 1 root root 273310432 Apr 27 14:08 sdc.blktrace.1
-rw-r–r– 1 root root 537514088 Apr 27 14:16 sdc.blktrace.bin
-rw-r–r– 1 root root 53125272 Apr 27 14:57 sdc.offset_8,32_c.dat
-rw-r–r– 1 root root 53125272 Apr 27 14:57 sdc.offset_8,32_w.dat
本例只有write,所有生成了_w _c 写是w 读是r c是w+c 本例c和w一样大。查看文件内容
0.000002716 52608272 52608280
0.000129844 52608000 52608008
0.000324497 52607688 52607696
0.000927928 52606144 52606152
0.001015187 52608280 52608288
0.001449302 52608008 52608016
第一行为时间,第二个为其实扇区大小,第3行为每个io结束扇区大小。可以算出每个io为8个扇区大小,既4k 与测试实际相符合。
2、1M direct=1
fio运行
[root@node1 samba-test]# fio -filename=./1m_file -direct=1 -iodepth=1 -thread -rw=write -ioengine=libaio -bs=1M -size=3G -numjobs=4 -times=300 -group_reporting -name=mytest2
mytest2: (g=0): rw=write, bs=1M-1M/1M-1M/1M-1M, ioengine=libaio, iodepth=1
…
fio-2.2.5
Starting 4 threads
mytest2: Laying out IO file(s) (1 file(s) / 3072MB)
注意:
1、bs=1M ,sdc块设备最大单次io大小为512,因此会分为2个io写入,numjobs=4,因此队列深度为2*4 = 8
[root@node1 1m]# cat /sys/block/sdc/queue/max_sectors_kb
512
[root@node1 ~]# cat /sys/block/sdc/queue/nr_requests
128 磁盘最大队列深度。
2、bs=1M 最大io数为512kb,除以512byte扇区大小为操作扇区数1024
与iostat一致。
blktrace取值
[root@node1 1m]# blktrace -d /dev/sdc
iostat取值
fio运行完成后,生成数据,并分析数据
生成数据
[root@node1 1m]# blkparse -i sdc -d sdc.blktrace.bin
可以看到Q-C一个完整的io路径。右边起始扇区+1024个扇区数。与实际相符合。
分析数据
[root@node1 1m]# btt -i sdc.blktrace.bin -A | less
Q2G – 生成IO请求所消耗的时间,包括remap和split的时间;
本例0.7ms 因为有块分为2部分。1m分为2个512kb io,单个512kb io 到扇区后得与扇区对齐等等
G2I – IO请求进入IO Scheduler所消耗的时间,包括merge的时间;
0.009ms 没有进行合并 绕过缓存了。
I2D – IO请求在IO Scheduler中等待的时间;
2.8ms
D2C – IO请求在driver和硬件上所消耗的时间;
34.5 ms
因此:
Q2C – 整个IO请求所消耗的时间(Q2I + I2D + D2C = Q2C),相当于iostat的await。 为38.1ms与iostat wait值几乎一致。在io scheduler上几乎没有消耗任何时间。时间花费在driver硬件层。
生成iops 和bw每秒值
btt -i sdc.blktrace.bin -q sdc.q2c_latency
[root@node1 1m]# ll
total 14508
-rw-r–r– 1 root root 913 Apr 27 15:40 8,32_iops_fp.dat
-rw-r–r– 1 root root 1731 Apr 27 15:40 8,32_mbps_fp.dat
-rw-r–r– 1 root root 3295528 Apr 27 15:20 sdc.blktrace.0
-rw-r–r– 1 root root 3894704 Apr 27 15:20 sdc.blktrace.1
-rw-r–r– 1 root root 7190232 Apr 27 15:28 sdc.blktrace.bin
-rw-r–r– 1 root root 452027 Apr 27 15:40 sdc.q2c_latency_8,32_q2c.dat
-rw-r–r– 1 root root 913 Apr 27 15:40 sys_iops_fp.dat
-rw-r–r– 1 root root 1731 Apr 27 15:40 sys_mbps_fp.dat
生成io大小分布
btt -i sdc.blktrace.bin -B sdc.offset
[root@node1 1m]# ll
total 16148
-rw-r–r– 1 root root 913 Apr 27 15:44 8,32_iops_fp.dat
-rw-r–r– 1 root root 1731 Apr 27 15:44 8,32_mbps_fp.dat
-rw-r–r– 1 root root 3295528 Apr 27 15:20 sdc.blktrace.0
-rw-r–r– 1 root root 3894704 Apr 27 15:20 sdc.blktrace.1
-rw-r–r– 1 root root 7190232 Apr 27 15:28 sdc.blktrace.bin
-rw-r–r– 1 root root 835954 Apr 27 15:44 sdc.offset_8,32_c.dat
-rw-r–r– 1 root root 835954 Apr 27 15:44 sdc.offset_8,32_w.dat
-rw-r–r– 1 root root 452027 Apr 27 15:40 sdc.q2c_latency_8,32_q2c.dat
-rw-r–r– 1 root root 913 Apr 27 15:44 sys_iops_fp.dat
-rw-r–r– 1 root root 1731 Apr 27 15:44 sys_mbps_fp.dat
只有写,即_w 与_c一致。查看_w文件sdc.offset_8,32_w.dat
127.845942908 65054784 65055808
127.846185883 65055808 65056832
127.952831800 65056832 65057856
127.953065986 65057856 65058880
127.955207647 65058880 65059904
第一行为时间,第二个为其实扇区大小,第3行为每个io结束扇区大小。可以算出每个io为1024个扇区大小,既1M 与测试实际相符合。
3、4k direct=0
[root@node1 samba-test]# fio -filename=./4k_file_direct0 -direct=0 -iodepth=1 -thread -rw=write -ioengine=libaio -bs=4k -size=3G -numjobs=4 -times=300 -group_reporting -name=mytest2
mytest2: (g=0): rw=write, bs=4K-4K/4K-4K/4K-4K, ioengine=libaio, iodepth=1
…
fio-2.2.5
Starting 4 threads
mytest2: Laying out IO file(s) (1 file(s) / 3072MB)
direct=0后 启动io buffer
iostat监控
blktrace监控数据
生成数据
[root@node1 1m]# blkparse -i sdc -d sdc.blktrace.bin
4k对象大小,在io路径变成1024 既最大512kb字节,很明显进行IO合并,大部分io动作都是Q G I来回循环,即生成io请求到io请求队列,就返回继续申请新的io到队列,整体由内核kworker进行io任务的调度,在内存swapper中完成io写入。W表示异步写。
分析数据
[root@node1 1m]# btt -i sdc.blktrace.bin -A | less
Q2G – 生成IO请求所消耗的时间,包括remap和split的时间;
本例5.6ms
G2I – IO请求进入IO Scheduler所消耗的时间,包括merge的时间;
0.009ms 没有进行合并。
I2D – IO请求在IO Scheduler中等待的时间;
635 ms
D2C – IO请求在driver和硬件上所消耗的时间;
188 ms
Q2C – 整个IO请求所消耗的时间(Q2I + I2D + D2C = Q2C),相当于iostat的await。 为828.6ms与iostat wait值几乎一致。在io scheduler上消耗太多时间。在driver硬件层耗时相对少一些,但是也很大了达到188ms。
其中M2D io合并时间平均4.5s.
生成io大小分布
btt -i sdc.blktrace.bin -B sdc.offset
93.463618386 74670904 74671928
93.463870465 74671928 74672952
93.464140190 74672952 74673976
93.464445965 74673976 74675000
93.464740898 74675000 74676024
93.465027976 74676024 74677048
93.465313376 74677048 74678072
同样的是以1024个扇区为最小io。