转自:http://bean-li.github.io/dive-into-iostat/
作者:manuscola
前言Iostat是检查块设备运行状态的重要工具。相信大部分使用Linux的同学都用过或者听说过这个工具。但是对于这个工具,误解最多,大多数人都处于一种朦胧的状态。现在我们从浅入深的介绍一下这个工具,它的输出是什么意思,它的能力边界,以及对这个工具常见的误解。
输出的基本用法和基本含义
iostat的用法比较简单。一般来说,用法如下:
iostat -mtx 2
意思是,每2秒收集一组数据:
-m以每秒兆字节为单位显示统计信息。-t打印显示的每个报告的时间。时间戳格式可能取决于S _ TIME _ F ORMAT环境变量的值(见下文)。-x显示扩展统计数据。
输出结果如下:
请注意,上图是对sdc单盘(RAID卡上的单盘)的4KB随机写入测试:
fio-name = rand write-rw = rand write-bs = 4k-size = 20G-runtime = 1200-ioengine = libaio-IODE PTH = 64-num jobs = 1-rate _ IOPs = 5000-filename =/dev/SDF-direct = 1-group _ reporting
所以上图只有sdc忙。
如何读取iostat的输出,每个参数的含义是什么,在磁盘上反映了什么信息?
第一列Device很好理解,就是这一行描述的是哪个设备。
Rrqm/s:每秒合并的读取操作数
Wrqm/s:每秒合并的写操作数
R/s:每秒的读取操作数
W/s:每秒的写操作数
人民币/秒:每秒读取兆字节
WMB/秒:每秒写入兆字节
Avgrq-sz:每个输入输出的平均扇区数量,即所有请求的平均大小,以扇区为单位(512字节)
Avgqu-sz:平均完成的IO请求数,即含义山的请求队列平均长度
等待:每个输入输出所需的平均时间,包括队列中的等待时间和磁盘控制器处理此请求的有效时间。
R_wait:每次读取操作所需的平均时间,不仅包括硬盘设备读取操作的时间,还包括在内核队列中的时间。
W_wait:每次写操作所需的平均时间,不仅包括硬盘设备写操作时间,还包括队列中的等待时间。
Svctm:表面上是每个IO请求的服务时间,不包括等待时间,但实际上这个指标已经被放弃了。其实iostat工具的输出项都不代表硬盘设备的平均IO时间。
%util:工作时间或忙碌时间占总时间的百分比
Avgqu-sz和繁忙程度
首先,我们使用超市购物来比较iostat的输出。当我们在超市结账时,通常会有很多人排队。排队长度在一定程度上反映了收银台的繁忙程度。那么这个变量就反映在avgqu-sz的输出上,值越大,排队等待处理的io越多。
我们做4K随机IO,但是iodepth=1,检查fio的指令和iostat的输出:
fio-name = rand write-rw = rand write-bs = 4k-size = 20G-runtime = 1200-ioengine = libaio-IODE PTH = 1-num jobs = 1-filename =/dev/SDC-direct = 1-group _ reporting
同样是4K随机IO,我们设置iodepth=16来检查fio的指令和iostat的输出:
fio-name = rand write-rw = rand write-bs = 4k-size = 20G-runtime = 1200-ioengine = libaio-IODE PTH = 16-num jobs = 1-filename =/dev/SDC-direct = 1-group _ reporting
请注意,内核中有一个输入/输出调度器队列。我们看到,因为avgqu-sz的大小不同,所以一个IO时间(wait)也不同。就好像你久而久之就在排队,一个队是空的,另一个队的长度是16,所以很明显,长队16的时候有点忙。
avgrq-sz
avgrq-sz值反映了用户的IO-Pattern。我们经常会在意来自用户的IO是大IO还是小IO,所以avgrq-sz就体现了这个元素。这意味着,平均而言,这段时间内所有请求的平均大小是以扇区为单位的,即(512字节)。
上图中,sdc的avgrq-sz始终为8,即8个扇区= 8*512(Byte) = 4KB,因为我们用fio播放io时,用的是bs=4k。
让我们在bs=128k时测试fio指令:
fio-name = rand write-rw = rand write-bs = 128k-size = 20G-runtime = 1200-ioengine = libaio-IODE PTH = 1-num jobs = 1-filename =/dev/SDC-direct = 1-group _ reporting
注意sdc中avgrq-sz的值,变成了256,也就是256扇区= 256* 512字节= 128KB,等于fio测试时我们发布的bs = 128k。
请注意,该值不是任意的,它由内核参数控制:
root @ node-186:~ # cat/sys/block/SDC/queue/max _ sectors _ kb 256
这个值不是最大发出的IO是256KB,也就是512个扇区。当我们用fio测试sdc时,如果bs=256k,iostat输出中的avgrq-sz将变成512扇区。但是如果bs = 512K继续增加,iostat输出中的avgrq-sz不会继续增加,仍然是512,也就是说512个扇区。
fio-name = rand write-rw = rand write-bs = 512k-size = 20G-runtime = 1200-ioengine = libaio-IODE PTH = 1-num jobs = 1-filename =/dev/SDC-direct = 1-group _ reporting
注意,原来512KB等于1024扇区,avgrq-sz应该是1204,但是由于内核的max_sectors_kb控制参数,不可能:
另一个需要注意且不难理解的现象是io请求越大,需要的时间就越长。对于块设备,时间分为两部分:
寻求
读或写操作
请注意,这里的寻道不能简单理解为磁盘磁头旋转到指定位置,因为备份块设备可能是RAID或SSD,我们理解写入前的准备工作。准备工作完成后,写4K和写128KB,显然,写128KB的工作量更大,所以很容易理解,随机写128KB比随机写4K给块设备带来更高的负载。
与生活中的例子相比,当你排队超过时间时,你会首先看排队的长度来评估时间。如果排队的人差不多长,你要注意前面顾客篮子里的东西的数量。如果前面的每个客户手里都拿着一两件物品,而另一个团队的几乎每个人都推着装满物品的购物车,你可能就知道该排队了。因为商品越多,处理单个客户的时间就越长。IO也一样。
Rrqm/s和wrqm/s
块设备有相应的调度算法。如果两个输入输出出现在相邻的数据块中,它们可以合并成一个输入输出。
这个简单可以理解为快递员要给一个18层的公司所有员工发快递,每层都有一些包裹。对于快递员来说,最好的办法是把同一楼层的包裹以相似的位置投递,否则如果不采用这个算法,就用原来的一个去发一个(也就是noop算法),那么这个快递员可能会先把一个送到18楼,然后还要去2楼再发一个包裹。,
Linux中常见的调度算法有noopdeline和cfq。这里就不展开了。
root @ node-186:~ # cat/sys/block/SDC/queue/scheduler[noop]截止日期cfq
类比总结
我们以加班购物为例。比如一家三口去购物,每人买自己的东西,最终会在收银台领取。你当然可以为每个人买单,但你也可以把所有的购物都收集在一起,由一个人来做。也就是说,三个收银事件合并成一个。
在这一点上,我们以加班购物收银员为例,引入avgqu-sz与排队长度的类比,avgrq-sz与每个人购物车中的商品数量的类比,rrqm/s和wrqm/s类似于收集一个家庭购买的商品并一次性付款。有svctm和%util没有介绍。
根据我们的故事,我们自然可以将svctm与收银员为每位顾客服务所需的平均时间进行比较,并将%util与收银员的繁忙程度进行比较。
注意这个类比是错误的,因为类似的类比很容易让人陷入误解。Svctm不能简单理解为块设备处理单个IO的有效时间,也不能理解为%util达到100%时,磁盘饱和,无法升级。这是两个常见的误区。
Svctm和%util是iostat最容易误导的两个输出。为了准确评估块设备的能力,我们希望得到这样一个值,即从向块设备层发送io到完成io的时间,不包括队列中的其他等待时间。表面上,svctm就是这个值。其实并不是这样。
Linux下iostat输出的Svctm没有这个意思,不应该丢弃这个指标。Iostat和sar手册页都有这方面的警告:
向设备发出的输入/输出请求的平均服务时间(毫秒)。警告!不要再信任此字段。此字段将在未来的sysstat版本中删除。
那么iostat输出中的svctm是怎么来的,util %是怎么计算出来的,iostat输出的所有字段都是从哪里得到信息的?
iostat输出的数据源是diskstats
Iostat数据来自Linux操作系统的/proc/diskstats:
请注意procfs中的前三个字段:主设备号、从设备号和设备名称。话不多说。
从第四个字段开始,介绍该设备的相关统计:
(rd_ios):读取操作的次数
(rd_merges):合并的读取操作数。如果两个读操作读取相邻的数据块,它们可以合并成一个。
(rd_sectors):读取的扇区数量
(rd_ticks):读取操作消耗的时间(毫秒)。每个读取操作都是从__make_request()到end_that_request_last()定时的,包括队列中的等待时间。
(wr_ios):写入操作的次数
(wr_merges):合并的写入操作数
(wr_sectors):写入的扇区数量
(wr_ticks):写操作消耗的时间(毫秒)
(in_flight):当前未完成的I/O的数量。当输入/输出请求进入队列时,该值增加1,当输入/输出结束时,该值减少1。注意:这是当输入输出请求进入队列时,而不是当它们被提交到硬盘设备时
该设备用于处理输入/输出的挂钟时间
(time_in_queue):字段#10的加权值(io_ticks)
这些字段大多来自内核中的以下数据:
除了飞行中来自:
内核相关代码如下:
io_ticks和time_in_queue
这些字段大部分很好理解,有一点难理解的是io_ticks。乍一看,很明显rd _ ticks和wr _ ticks已经存在。为什么需要io_ticks?注意rd_ticks和wr_ticks加起来是每个io消耗的时间,但是硬盘设备一般可以并行处理多个IOs,所以rd_ticks和wr_ticks的总和一般大于挂钟时间。IO_ticks不关心队列中有多少IOs,只关心设备有IO的时间。也就是我不考虑IO的数量,只考虑IO是否存在。在实际操作中,当in_flight不为0时,计时保持不变,而当in_flight等于0时,时间不累计到io_ticks。
接下来比较难理解的是time_in_queue的值,它是用当前IO数(即in_flight的值)乘以自然时间间隔计算出来的。表面上看,变量的名字是time_in_queue,但实际上不仅仅是队列中的等待时间。
有些人不理解排队时间,但我相信在小学读过下面这句话的孩子会理解排队时间:
因为你上课讲话,让老师批评你5分钟,全班50人,你浪费了全班250分钟。
这段话形象地介绍了time_in_queue的计算规则,即自然时间只过去了5分钟,但是对于所有排队的同学,哦不,所有IO都需要加权计算:
计算方法很简单:
当请求队列为空时:
Io_ticks不增加
排队时间不增加
part->;立即更新图章
当请求队列不是空时:
Io_ticks现在增加-第->:部分时间戳
排队时间通过将队列中的输入输出数量乘以(现在-部分->:标记)而增加
part->;立即更新图章
注意调用part_round_stats_single函数的时机:
在新的输入输出请求中插入队列(合并请求不计算在内)
完成输入输出请求
空太抽象了,但我们还是举个例子介绍一下io_ticks和time_in_queue的计算:
注意,以上总时间为53.90小时,在3.9秒的自然时间内有IO,即IO队列的非空时间为3.9秒。
Io_ticks由iostat用来计算%util,time_in_queue由iostat用来计算avgqu-sz,即平均队列长度。
其实不难理解,当队列不是空时,总时间的比例是%util
更新/proc/diskstats中的其他数据项
既然我们已经介绍了io_ticks和time_in_queue,那么我们也简单介绍一下其他领域的获取。
在每个IO结束时,调用blk_account_io_done函数,负责更新rd_ios/wr_ios和rd_ticks/wr_ticks,包括更新in_flight。
请注意,part_round_stats调用上一节中描述的part_round_stats_single函数:
读写扇区数量的计数在blk_account_io_completion函数中实现:
关于合并部分的统计,统计在blk_account_io_start函数中执行:
iostat输出的计算
注意/proc/diskstats已经准备好了所有的资料。对于iostat程序,它会处理这些数据,向客户展示更友好、更有意义的价值。其实iostat的源代码很短,属于开源软件sysstat,文件总大小1619行。
所有的数据都收集好了,剩下的都计算好了。以下项目的计算非常简单:
rrqm/s
wrqm/s
r/s
w/s
人民币/秒
wMB/s
这几项的计算很简单,就是采样两次,用上一次的值减去上一次的值,再除以时间间隔得到平均值。因为/proc/diskstats中的相应值是累加的,所以采样时间间隔中的新值可以通过从最后一个值中减去前一个值来获得。不要深入细节。
Avgrq-sz计算
注意avgrq-sz来自xds的argsz变量,由这个函数计算:
注意,nr_IOs来自下面的operatIOn,即读io和写io之和
SDC . NR _ IOs = ioi->;rd_ios + ioi->wr _ iossdp.nr_ios = ioj->。rd_ios + ioj->。wr _ ios
那么xds->:arqsz计算的意思如下:
xds->。Arqsz=(读取的扇区总数+写入的扇区总数)/(读取的IO数+写入的IO数)xds->:arqsz =(SDC->;nr_ios-sdp->nr_ios)?((SDC->;rd_sect-sdp->。rd _ Sect)+(SDC->;wr_sect-sdp->。wr _ Sect))/((double)(SDC->;nr_ios-sdp->NR _ IOs)):0.0;
OK很好理解,计算很合理。
Avgqu-sz计算
平均队列长度的计算使用diskstats中time_in_queue的值。
这个值的计算来源于这句话:
s _ VALUE(ioj->;rq_ticks,ioi->rq_ticks,itv) / 1000.0)
Rq是diskstats中的排队时间。
我们考虑以下场景。如果有突发的IO请求,250个IO请求同时到来,后面没有新的请求到来。在这种情况下,每个请求的处理时间为4毫秒,因此所有输入输出的平均等待时间为:
平均等待时间=单个请求处理时间*(1+2+3+4...+(请求总数-1))/请求总数
对于我们的例子,平均等待时间是4 * 125 = 500 ms。
那么所有IO花费的总时间是250*500=125000毫秒,除以1000毫秒:
125000/1000 = 125
也就是平均排队长度为125,明显直观。队列最前面的IO认为队列长度为0,第二个IO认为队列长度为1,第三个IO认为队列长度为2,最后一个认为队列长度为249。
让我们从另一个角度来考虑,即diskstats中time_in_queue的思想。
第一个IO完成时,队列中有250个IO,全部等待4ms,即第二个IO完成时time_in_queue+= (250*4),time_in_queue += (249*4),所有IO完成时time _ in _ queue = 4。
根据time_in_queue/1000,各种方式得到平均队列长度。
等待、r_wait和w_wait计算
这没什么好说的:
wait =((所有读IO的时间)+(所有写IO的时间))/((读请求的数量)+(写请求的数量))
只需注意,所有读取IO的时间和所有写入IO的时间都包括IO在队列中的时间。现在是磁盘控制器处理输入输出的时候,这不能是一厢情愿的想法。
注意,可以说await比较高,所以任意判断这个板块的能力很差吗?答案是否定的,Await不能反映硬盘设备的性能。Await不能反映硬盘设备的性能。await不能反映硬盘设备的性能。重要的话说三遍。
我们考虑两种输入输出模型:
250个输入输出请求同时进入等待队列
依次启动250个输入输出请求,在前一个输入输出完成后启动下一个输入输出
第一种情况等待时间高达500毫秒,第二种情况等待时间只有4毫秒,但都是同一个磁盘。
但是,请注意,等待是一个非常重要的参数,它指示用户发起的输入输出请求的平均延迟:
等待= io的平均处理时间+io在队列中的平均等待时间
所以这个指标是很重要的一个。
%util和磁盘设备饱和
注意%util是最容易误导的参数。很多新手看到%util等于100%,说硬盘容量已经到顶了,这是不对的。
%util数据来自diskstats中的IO_ticks。这个值不关心队列中等待的io数量,只关心队列中是否有IO。
这个类比和加班排队结账的本质区别是,现代硬盘有并行处理多个IO的能力,而收银员没有。收银员不能同时处理10个客户的结账任务,消耗的总时间和一个客户差不多。但是磁盘可以。所以,即使%util达到100%,也不代表设备饱和。
最简单的例子就是一个硬盘处理一个IO请求需要0.1秒,它有能力同时处理10个IO请求。但当依次提交10个请求时,完成这10%的请求需要1秒,在1秒的采样周期内%util达到100%。但是如果一次提交10个,硬盘0.1秒就可以完成。此时%util只有10%。
因此,在上面的例子中,当一秒钟内有10个输入输出时,即IOPS=10,则%util达到100%。这并不意味着这个圆盘的IOPS只能达到10。事实上,即使%util达到100%,硬盘可能仍然有很多空闲容量来处理更多的请求,也就是说,它没有饱和。
下一节有四张图,显示IOPS为1000时%util为100%,但并不意味着这个盘的IOPS为1000,实际上可以达到2000,3000,5000 IOPS。当% util为100%时,按r/s或w/s计算圆盘的IOPS是错误的。
那么有没有衡量硬盘设备饱和度的指标呢?不幸的是,iostat没有一个单一的指标来衡量磁盘设备的饱和度。
svctm的计算
就iostat的功能而言,%util会带来一些误解和困扰,而svctm会带来更多的误解。长期以来,人们希望知道块设备处理单个IO的服务时间,这直接反映了硬盘的能力。
回到超市收银员的类比,如果收银员是老手,操作流程和效率高,那么大家肯定更愿意排队。但如果收银员是新手,对各种操作都不熟悉,速度慢,效率低,那么同样数量的任务会花费更长的时间。所以IO的平均服务时间(不包括排队时间)很有意义。
但是服务时间与iostat无关,iostat没有任何参数提供这个信息。而svctm的输出给人如此美妙的期待,却只能让人空开心。
从现在开始,我们要记住,我们不能从svctm中得到期望的服务时间值,这实际上是没有意义的。其实这个值不是独立的,是根据其他值计算出来的。
在随机小IO(4K)fio测试中,我们会看到以下现象:IOPS为1000时,iosta输出的svctm为1(ms),IOPS为2000时,iostat输出的svctm为0.5(ms),IOPS为3000时,iostat输出的svctm为0.33。实际上,没有其他原因,因为在这种情况下%util是100%,即当采样周期为1秒时,tput由fio-rate-IOPs指定为1000、2000和3000,因此svctm分别计算为1、0.5和0.33。
所以从这个例子来看,把IOstat输出中的svctm看成io的处理时间是相当不可靠的。为了防止误解,可以直接忽略该参数。
既然svctm不能反映IO处理时间,那么有没有一个参数可以衡量块设备的平均IO处理时间?可惜iostat做不到。但是,只要思想不滑坡,解决起来总是比较困难。工件blktrace可能得到这个设备的平均IO处理时间。
然后我们就可以进入另一个世界了。
尾声
Iostat能给我们提供这么多信息。通过分析,我们期望得到块设备处理IO的时间,这取决于工具块跟踪。Blktrace可以讲IO路径分段,分别统计每个分段消耗的时间。
本文大量引用了易被误读的vmunix的IOSTAT,对diskstats进行了深入分析。第二篇文章给出了一个非常详细的输入输出路径流程图,非常有用。在第二篇文章中,随着代码的演变,有一些变化。本文介绍了相对较新的Linux内核代码。同时,第二篇文章在io_ticks和time_in_queue的计算上也有错误,已经改正。不过这两篇文章都是很优秀的文章。向前辈致敬。
商务合作请加微信云威帮555
1.《util 误解很多,带你深入理解iostat》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《util 误解很多,带你深入理解iostat》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/junshi/1224251.html