在生产环境中我们经常会遇到系统 iowait 很高的情况,很多同事经常把这个指标高理解为系统出现了磁盘(或者 SSD,NVMe) I/O 瓶颈或者系统 I/O 负载高,这些理解其实都是不准确的,iowait 数值高低需要结合具体场景解读,iowait 高不代表系统有 I/O 瓶颈,单独的高数值不一定是问题,相反,iowait 低也不代表系统没有 I/O 问题。在 Linux 系统中 iowait 是 CPU 空闲(idle)时间的一部分,表示 CPU 因为等待 I/O 操作(主要是块设备 I/O)完成所花时间的百分比,注意:它不是 I/O 本身占用的 CPU 时间,而是 CPU 因为等待 I/O 而空闲的时间。所以 iowait 的大小,不仅仅受到 I/O 的影响,还受到 CPU 的忙闲程度的影响。下面我们通过几个实验来验证这一点。
使用 taskset 命令把一条读磁盘 sda 的 dd 命令绑到 CPU 1 上,为了排除 Page Cache 对 I/O 的影响,我指定了 dd 命令使用 Direct IO 模式(iflag=direct)。
taskset -c 1 dd if=/dev/sda of=/dev/null count=1M bs=4k iflag=direct
然后使用 mpstat 命令查看 CPU 1 上的实时性能数据。
可以看到 iowait 的值高达 90%,下面我们来做一个对比实验,在 CPU1 上面除了运行上面那个 dd 命令之外,再绑定一个死循环(deadloop)程序,使得这个 CPU 一直处于非空闲的状态。
然后 mpstat 的输出就会发生变化,user mode 的 CPU 利用率从 1% 升到了 100%,iostat 的值降到了 0。
可以看到上面两个实验的 I/O 负载基本上是相同的,但是 iowait 的值却差异很大,主要就是第二个实验在保持同样的 I/O 负载的情况下,把 CPU 利用率也给升上去了,这样 dd 进程花在等待 I/O 上的时间就不会被算在 iowait 的时间里了。
除了上面这种情况会导致 iowait 和 I/O 负载不正相关之外,异步 I/O 也会也会影响 iowait 对 I/O 负载的反映情况,因为进程在做异步 I/O 的情况下,并不会被阻塞去等待 I/O 的完成,下面我通过两个 fio 的实验来演示这一点。
这两个 fio 实验的命令如下:
实验一:
fio -filename=/dev/sda -direct=1 -iodepth 4 -thread -rw=read -ioengine=libaio -bs=4k -size=1024G -numjobs=1 -runtime=60 -group_reporting -name=async_io_fio_test
实验二:
fio -filename=/dev/sda -direct=1 -iodepth 4 -thread -rw=read -ioengine=sync -bs=4k -size=1024G -numjobs=1 -runtime=60 -group_reporting -name=sync_io_fio_test
第一个实验我们设置 fio 的 ioengine 为 libaio,这样 fio 就采用异步 I/O 的方式读块设备 /dev/sda,第二个实验在保持其他参数不变的情况下把 fio 的 ioengine 改为 sync,这会让 fio 采用同步 I/O 的方式工作。下面我们来看看这两个实验的效果。
实验一的 fio 测试数据如下,带宽大概为 10M/s(我这硬盘有点老了😂),IOPS (I/O Per Second)为 2615。
实验一的 mpstat 的实时监控数据如下,iowait 几乎为零。
实验二的 fio 测试数据如下,带宽大概为 8M/s,IOPS 为 2129,要小于实验一的 IOPS。
实验二的 mpstat 实时监控输入如下,iowait 为 11%。
通过上面实验一和实验二的对比我们可以发现,异步 I/O 性能比同步 I/O 好,因此异步 I/O 的负载也会更高一些,但是异步 I/O 的 iowait 反而会低一些。所以我们并不能单纯地根据 iowait 来判断系统 I/O 负载的高低。
下面总结一下 iowait 的特性: