之前在博文性能分析中科特尔法则(Little’s Law)与其衍生法则的应用有介绍过 Little’s law 和它的简单证明过程,但此文中并没有给出相应的应用实例。本文通过以 Little’s law 角度解读 iostat 命令输出的 avgqu-sz 指标。

查看平均队列长度

最简单的方法是通过命令 iostat -x 1 以每隔一秒一次输出队列长度,如下图所示。



iostat输出详解 iostat util参数_ios


iostat

红框所示的 avgqu-sz 就是平均队列长度了,这里直接引用博文 http://bean-li.github.io/dive-into-iostat/ 中给出的计算方法。

我们换一种思路来考虑,即 diskstats 中 time_in_queue 的思路。当第一个 IO 完成的时候,队列中 250 个 IO,250 个 IO 都等了 4ms,即 time_in_queue + = (2504) ,当第二个 IO 完成的时候,time_in_queue += (2494),当所有 IO 都完成的时候,time_in_queue = 4*(250+249+248….+1), …

根据 time_in_queue/1000,殊途同归地获得了平均队列长度。

第一次读到这段解释时似懂非懂,后来想起来在证明 Little’s Law 的过程中曾使用面积的来替代队列中的总等待时间并除以观察时间得出平均队列长度。通过这个方法来理解 iostat 的计算队列长度会简单得多,而且有利于加深对 Little’s Law 的理解。

借用 Little’s Law 的证明思路

我们回顾下 Little’s law 的证明过程



iostat输出详解 iostat util参数_iostat输出详解_02


Little’s law

Little’s law 中不太好理解的参数是队列中平均任务数,请参考上图中的绿色部分 A(T)。A(T)为 T 观察时间范围内,已经入队到队列中的所有任务等待时间的总和,也可以理解为绿色部分的面积。当由 n(t)导致的不规则绿色区域抹平成长方形时,设 T 为长方形的宽,队列中的平均任务数为高,则 A(T) = T * 队列中平均任务数。设 L(T) = A(T) / T,则 L(T)等同于平均等待任务数(也称为平均队列长度)。

上面一段是博文”性能分析中科特尔法则(Little’s Law)与其衍生法则的应用”的片段。由于观察范围时间 T 可以由我们指定,那我们需要计算出绿色区域面积就可以得出平均队列长度。

那问题变成了我们怎么能获取队列中的所有任务的等待时间呢?

在 Linux 内核(以 4.4 版本为例)中有如下代码,它在每次关键 IO 事件时会被调用



iostat输出详解 iostat util参数_ios_03


IO 事件时会被调用

其中 time_in_queue 就等同于上面所说的”入队到队列中的所有任务等待时间的总和”。

每当发生以下几个 IO 事件时会触发 IO 统计,也就是调用 part_round_stats_single()。

  1. 当有新的 IO 请求入队
  2. 当 IO 请求被已有请求合并(Front merge or Back merge)时
  3. 当完成某个 IO 请求时

在博文 http://bean-li.github.io/dive-into-iostat/中有提到Merge时不会做IO统计函数,但是在kernel 4.4 中可以到由 blk_queue_bio()函数根据情况分别会调用 attempt_front_merge()或 attempt_back_merge()。

每当发生关键 IO 事件;「发起请求,完成请求,请求被合并」 时查看当前有几条正在执行的读请求 IO 或写请求 IO,将此值乘以临近时间差就得出了系统当前处理 IO 时的总耗时。这与 Little’ Law 中通过面积来计算总耗时是一个道理,”绿色面积”中每当一个请求来的时候就上调一格,完成 IO 时就下调一格。在观察时间周期 T 内,以此方法来画出的绿色区域就是请求的总耗时时间。需要再次强调的关键概念是:

  1. 系统同时可以有多个读请求
  2. 系统同时可以有多个写请求
  3. 读请求与写请求可以同时发生

内核函数 part_in_flight()中返回的 IO 数目也是读和写请求的总和。



iostat输出详解 iostat util参数_性能分析_04


内核函数part_in_flight

如何获取平均服务时间?

Linux 的/proc/diskstat 中目前(4.4 版本)只提供了处理完成的 IO 请求数量与 IO 等待时间,并没有提供请求入队的 IO 请求数量。在”性能分析中科特尔法则(Little’s Law)与其衍生法则的应用”也提到过 Utilization law,是指当资源使用率不超过 100%时可认为抵达率与完成率是一致的,这也意味着当资源使用率未达到 100%时可以通过完成率替代抵达率以计算服务平均处理时间。iostat 中用的方法也是利用了这一点,但本质上是 Utilization law 在发挥作用。如果遇到资源使用率达到性能拐点处时就已经不是 job flow balance 状态了(出现排队)。以严谨起见,最好是能获取到真正的请求入队数量,这样可以套用 Little’s law 计算出真正的平均服务时间。目前想到的唯一方法是通过修改内核代码以获取入队请求次数。

W = L / λ

「补充:」

当谈到 Disk IO 的服务时间时注意区分两种情况:

  1. IO 队列服务时间 = 磁盘忙于 IO 请求的时间 + IO 请求在队列中等待时间
  2. 磁盘 IO 服务时间 = 磁盘忙于 IO 请求的时间

Little’s Law 给出的平均服务时间是指 IO 队列服务时间,他是包括了队列中的等待时间。那实践中以哪个指标为主呢?我觉得是根据应用场景不同而不同,

  1. 分析某个 workload characterization 时会侧重看 IO 队列服务时间
  2. 分析某个设备整体情况时会侧重看磁盘 IO 服务时间。基于 Flash 的存储设备出现严重的文件碎片化时此值会暴增。