可以分为
繁忙程度 util
响应速度 await
获取数据
/proc/diskstats
以Linux为例,该服务器上有一块vda设备,该vda设备的diskstats如下
root@iZj6c9cop51kep9kwnw7p1Z:~# cat /proc/diskstats | grep vda
253 0 vda 656666 270 16523794 2344432 3339086 2233028 63578608 7583352 0 784384 9927392
253 1 vda1 656583 270 16519674 2344040 3300685 2233028 63578608 7563340 0 764820 9906996
前三位是设备相关标识。从第四位开始到第七位是读相关,第八位到第十一位是写相关,后三位分别是IO等待、处理IO时间、处理IO加权时间。
4:rd_ios 读操作的次数。
5:rd_merges 合并读操作的次数。如果两个读操作读取相邻的数据块时,可以被合并成一个,以提高效率。合并的操作通常是I/O scheduler(也叫elevator)负责的。
6:rd_sectors 读取的扇区数量。
7:rd_ticks 读操作消耗的时间(以毫秒为单位)。每个读操作从__make_request()开始计时,到end_that_request_last()为止,包括了在队列中等待的时间。
8:wr_ios 写操作的次数。
9:wr_merges 合并写操作的次数。
10:wr_sectors 写入的扇区数量。
11:wr_ticks 写操作消耗的时间(以毫秒为单位)。
12:in_flight 当前未完成的I/O数量。在I/O请求进入队列时该值加1,在I/O结束时该值减1,是I/O请求进入队列时,而不是提交给硬盘设备时。
13:io_ticks 该设备用于处理I/O的自然时间(wall-clock time)。
14:time_in_queue 对字段io_ticks的加权值。字段io_ticks是自然时间,不考虑当前有几个I/O,而time_in_queue是用当前的I/O数量(即in_flight)乘以自然时间。虽然该字段的名称是time_in_queue,但并不真的只是在队列中的时间,其中还包含了硬盘处理I/O的时间。iostat在计算avgqu-sz时会用到这个字段。
请注意io_ticks与rd_ticks和wr_ticks的区别,rd_ticks和wr_ticks是把每一个I/O所消耗的时间累加在一起,因为硬盘设备通常可以并行处理多个I/O,所以rd_ticks和wr_ticks往往会比自然时间大。而io_ticks表示该设备有I/O(即非空闲)的时间,不考虑I/O有多少,只考虑有没有。在实际计算时,in_flight不为零的时候io_ticks保持计时,字段#9(in_flight)为零的时候io_ticks停止计时。
iostat
在Linux上执行命令,得到信息如下
iostat -x 1
avg-cpu: %user %nice %sys %idle
16.24 0.00 4.31 79.44
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util
/dev/cciss/c0d0:0.00 44.90 1.02 27.55 8.16 579.59 4.08 289.80 20.57 22.35 78.21 5.00 14.29
实际上,iostat的信息是通过获取两次/proc/diskstats上的信息然后计算得出的。
注:下面的公式中“Δ”表示两次取样之间的差值,“Δt”表示采样周期。
tps:每秒I/O次数=[(Δrd_ios+Δwr_ios)/Δt]
r/s:每秒读操作的次数=[Δrd_ios/Δt]
w/s:每秒写操作的次数=[Δwr_ios/Δt]
rkB/s:每秒读取的千字节数=[Δrd_sectors/Δt]*[512/1024]
wkB/s:每秒写入的千字节数=[Δwr_sectors/Δt]*[512/1024]
rrqm/s:每秒合并读操作的次数=[Δrd_merges/Δt]
wrqm/s:每秒合并写操作的次数=[Δwr_merges/Δt]
avgrq-sz:每个I/O的平均扇区数=[Δrd_sectors+Δwr_sectors]/[Δrd_ios+Δwr_ios]
avgqu-sz:平均未完成的I/O请求数量=[Δtime_in_queue/Δt]
(手册上说是队列里的平均I/O请求数量,更恰当的理解应该是平均未完成的I/O请求数量。)
await:每个I/O平均所需的时间=[Δrd_ticks+Δwr_ticks]/[Δrd_ios+Δwr_ios]
(不仅包括硬盘设备处理I/O的时间,还包括了在kernel队列中等待的时间。)
r_await:每个读操作平均所需的时间=[Δrd_ticks/Δrd_ios]
不仅包括硬盘设备读操作的时间,还包括了在kernel队列中等待的时间。
w_await:每个写操作平均所需的时间=[Δwr_ticks/Δwr_ios]
不仅包括硬盘设备写操作的时间,还包括了在kernel队列中等待的时间。
%util:该硬盘设备的繁忙比率=[Δio_ticks/Δt]
表示该设备有I/O(即非空闲)的时间比率,不考虑I/O有多少,只考虑有没有。
psutil
这是一个python的第三方库,也可以获取IO的信息。
In [22]: import psutil
In [23]: psutil.disk_io_counters()
Out[23]: sdiskio(read_count=674028, write_count=3374706, read_bytes=8607638528, write_bytes=33387319296, read_time=2446400, write_time=7583480, read_merged_count=270, write_merged_count=2330676, busy_time=783312)
计算数据
iostat是通过/proc/diskstats中的数据进行计算。而psutil也是获取的/rpc/diskstats中的数据,所以理论上我们是可以通过psutil模拟出iostat的功能。
回到开头,我们先计算磁盘繁忙程度,util。大概的公式如下。实际上,这个util并不能准确的相信,仅供参考,而判断磁盘繁忙程度,目前尚未找到比较好的方法。
In [30]: import time
In [31]: a = time.time()
In [32]: abt = psutil.disk_io_counters()[8]
In [33]: time.sleep(1)
In [34]: b = time.time()
In [35]: bbt = psutil.disk_io_counters()[8]
In [36]: (bbt-abt)/(b-a)
Out[36]: 0.44979996398143424
%util表示该设备有I/O(即非空闲)的时间比率,不考虑I/O有多少,只考虑有没有。由于现代硬盘设备都有并行处理多个I/O请求的能力,所以%util即使达到100%也不意味着设备饱和了。举个简化的例子:某硬盘处理单个I/O需要0.1秒,有能力同时处理10个I/O请求,那么当10个I/O请求依次顺序提交的时候,需要1秒才能全部完成,在1秒的采样周期里%util达到100%;而如果10个I/O请求一次性提交的话,0.1秒就全部完成,在1秒的采样周期里%util只有10%。可见,即使%util高达100%,硬盘也仍然有可能还有余力处理更多的I/O请求,即没有达到饱和状态。那么iostat(1)有没有哪个指标可以衡量硬盘设备的饱和程度呢?很遗憾,没有。
接下来我们计算磁盘响应速度,await。大概公式如下。和util一样,并不是一个可以完全相信的指标,而且硬盘与硬盘之间差异很大。仅作参考。
n [43]: a = time.time()
In [44]: ap = psutil.disk_io_counters()
In [45]: time.sleep(1)
In [46]: b = time.time()
In [47]: bp = psutil.disk_io_counters()
In [48]: ((bp[4]-ap[4])/(b-a) + (bp[5]-ap[5])/(b-a))/((bp[0]-ap[0])/(b-a) + (bp[1]-ap[1])/(b-a))
Out[48]: 0.14285714285714285
await是单个I/O所消耗的时间,包括硬盘设备处理I/O的时间和I/O请求在kernel队列中等待的时间,正常情况下队列等待时间可以忽略不计,姑且把await当作衡量硬盘速度的指标吧,那么多大算是正常呢?
对于SSD,从0.0x毫秒到1.x毫秒不等,具体看产品手册;
对于机械硬盘,可以参考以下文档中的计算方法:
http://cseweb.ucsd.edu/classes/wi01/cse102/sol2.pdf
大致来说一万转的机械硬盘是8.38毫秒,包括寻道时间、旋转延迟、传输时间。
在实践中,要根据应用场景来判断await是否正常,如果I/O模式很随机、I/O负载比较高,会导致磁头乱跑,寻道时间长,那么相应地await要估算得大一些;如果I/O模式是顺序读写,只有单一进程产生I/O负载,那么寻道时间和旋转延迟都可以忽略不计,主要考虑传输时间,相应地await就应该很小,甚至不到1毫秒。
最后
通过反复查找资料和搜索相关信息,都没有找到一个比较好的方法进行磁盘IO监控。因此,并不能依赖某个特定的指标,持续性的收集数据,观察数据的波动性,是比较可靠的办法的。