Linux系统出现了性能问题,一般我们可以通过top、iostat、free、vmstat、iotop、iftop等命令来查看初步定位问题。其中iostat可以给我们提供丰富的IO状态数据,但正确理解iostat输出信息很重要。

一、sysstat

iostat命令是由sysstat包提供的,sysstat包提供有iostat、mpstat、sar、sa等命令,这一章我们就说iostat命令,iostat 命令用来监视系统输入/输出设备负载,这通过观察与它们的平均传送速率相关的物理磁盘的活动时间来实现。iostat命令生成的报告可以用来更改系统配置来更好地平衡物理磁盘和适配器之间的输入/输出负载。由iostat命令生成的第一份报告提供了关于自从系统被引导后的时间统计信息。后继的每一份报告都包含自上一次报告以来的时间。每次运行 iostat 命令时,就报告所有的统计信息。

$ yum install sysstat

1

$yuminstallsysstat

二、基本使用

$ man iostat

-c

显示CPU利用率报告.

-d

显示IO设备利用率报告.

-h

使设备利用率报告更易于被人阅读.

-k

使某些使用block为单位的列强制使用k为单位显示每秒统计信息.

-m

使某些使用block为单位的列强制使用m为单位显示每秒统计信息.

-N

显示任何已注册设备映射器名称, 用于查看LVM2统计.

-p [ { device [,...] | ALL } ]

显示设备块及其分区统计信息.

-T

此选项必须与选项g一起使用, 并指示只有该组的全局统计信息才能显示, 而不是组中单个设备的统计信息.

-t

打印显示的每个报表的时间, 时间戳格式可能取决于S_TIME_FORMAT环境变量(见下文).

-V

打印版本号然后退出.

-x

显示扩展统计信息.

-y

如果在给定的时间间隔内显示多个记录, 则从系统启动后省略第一个报表.

-z

告诉iostat省略在采样期间没有活动的任何设备的输出.

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

$maniostat

-c

显示CPU利用率报告.

-d

显示IO设备利用率报告.

-h

使设备利用率报告更易于被人阅读.

-k

使某些使用block为单位的列强制使用k为单位显示每秒统计信息.

-m

使某些使用block为单位的列强制使用m为单位显示每秒统计信息.

-N

显示任何已注册设备映射器名称,用于查看LVM2统计.

-p[{device[,...]|ALL}]

显示设备块及其分区统计信息.

-T

此选项必须与选项g一起使用,并指示只有该组的全局统计信息才能显示,而不是组中单个设备的统计信息.

-t

打印显示的每个报表的时间,时间戳格式可能取决于S_TIME_FORMAT环境变量(见下文).

-V

打印版本号然后退出.

-x

显示扩展统计信息.

-y

如果在给定的时间间隔内显示多个记录,则从系统启动后省略第一个报表.

-z

告诉iostat省略在采样期间没有活动的任何设备的输出.

例如:

$ iostat -d

Linux 2.6.32-431.el6.x86_64 (opsdb-hzbj-02) 03/08/2017 _x86_64_(40 CPU)

Device: tps Blk_read/s Blk_wrtn/s Blk_read Blk_wrtn

sda 44.13 80.76 88.32 22072668 1105513172

sdb 3.02 27.15 113.89 339800785 1425580084

1

2

3

4

5

6

$iostat-d

Linux2.6.32-431.el6.x86_64(opsdb-hzbj-02)03/08/2017_x86_64_(40CPU)

Device:tpsBlk_read/sBlk_wrtn/sBlk_readBlk_wrtn

sda44.1380.7688.32220726681105513172

sdb3.0227.15113.893398007851425580084

Device

此列提供了在/dev目录中列出的设备 (或分区) 名称。

tps

指示每秒向设备发出的I/O读、写请求数量,也可以称之为IOPS。“一次传输”表示“一次I/O请求”,在内核层系统会把多个逻辑请求可能会合并为“一次I/O请求”,为了减少I/O请求次数,比如两个4k请求合并成一个8k请求。

Blk_read/s (kB_read/s, MB_read/s)

指每秒从设备中读取的块数量,块等于扇区,等于512字节。如果使用选项-k,那么会显示:读取块数量乘以512字节就是每秒读取数据大小。

Blk_wrtn/s (kB_wrtn/s, MB_wrtn/s)

指每秒从设备中写入的块数量,块等于扇区,等于512字节。如果使用选项-k,那么会显示:写入块数量乘以512字节就是每秒写入数据大小。

Blk_read (kB_read, MB_read)

取样时间间隔内读取的总块数,块等于扇区,等于512字节。如果使用选项-k,那么会显示:读取块数量乘以512字节就是每秒读取数据大小。

Blk_wrtn (kB_wrtn, MB_wrtn)

取样时间间隔内写入的总块数,块等于扇区,等于512字节。如果使用选项-k,那么会显示:写入块数量乘以512字节就是每秒写入数据大小。

上面的例子中,我们可以看到磁盘sda以及它的各个分区的统计数据,磁盘每秒传输次数平均约44.13(TPS)。因为是瞬间值,所以总TPS并不严格等于各个分区TPS的总和。每秒磁盘读取约80/s KB,写入约88/s KB。

Tips:高版本iostat已经默认使用k为单位显示统计信息,如在CentOS 7系统就是以k为单位显示统计信息。

三、详细参数说明

使用-x参数我们可以获得更多扩展统计信息,其中参数-d表示只显示设备(磁盘)使用状态;-k表示某些使用block为单位的列强制使用Kilobytes为单位;1表示数据显示每隔1秒刷新一次,10表示共显示10次。

$ iostat -d -x -k 1 10

Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s rkB/s wkB/s avgrq-sz avgqu-sz await svctm %util

sda 1.56 28.31 7.80 31.49 42.51 2.92 21.26 1.46 1.16 0.03 0.79 2.62 10.28

1

2

3

$iostat-d-x-k110

Device:rrqm/swrqm/sr/sw/srsec/swsec/srkB/swkB/savgrq-szavgqu-szawaitsvctm%util

sda1.5628.317.8031.4942.512.9221.261.461.160.030.792.6210.28

rrqm/s & wrqm/s

每秒对该设备的读/写请求被合并次数。如果两个I/O操作发生在相邻的数据块时,它们可以被合并成一个,以提高效率。从而两次4K的读在它最终被处理到磁盘上之前可能会变成一次8K的读,最终交给磁盘。所以它将被计算 (和排队) 作为一个 I/O,此字段使你知道此操作的频率。合并的操作通常是I/O scheduler(也叫elevator)负责的,不同的调度器合并方式可能有所不同。

r/s & w/s

每秒读/写的次数(IOPS)。

rsec/s & wsec/s

每秒读写的扇区数,扇区数乘以512字节等于每秒读取的数据量。

rKB/s & wKB/s

每秒读/写数据量,单位有KB或MB。

avgrq-sz

平均每次I/O操作的数据量(扇区数为单位)。

avgqu-sz

平均等待处理的I/O请求队列长度。

await

每一次I/O请求读写处理的平均耗时时间(单位是毫秒),但它不能反映硬盘设备的性能,因为await不仅包括硬盘设备处理I/O的时间,还包括了在队列中等待的时间。I/O请求在队列中的时候尚未发送给硬盘设备,即队列中的等待时间不是硬盘设备消耗的,所以说await体现不了硬盘设备的速度,内核的问题比如I/O调度器什么的也有可能导致await变大。那么有没有哪个指标可以衡量硬盘设备的性能呢?非常遗憾的是,iostat(1)和sar(1)都没有,这是因为它们所依赖的/proc/diskstats不提供这项数据。内核文件/proc/diskstats并未把队列等待时间和硬盘处理时间分开,所以凡是以它为基础的工具都不可能分别提供disk service time以及与queue有关的值。

虽然不能用来真正衡量硬盘设备的性能的指标?但平时也是可以用来衡量I/O调度器和硬件整体的I/O响应时间,一般地系统I/O响应时间应该低于5ms,如果大于10ms就比较大了。

r_await

每一次I/O请求读处理的平均时间(单位是毫秒),这包括队列中的请求所用的时间和为它们提供服务的时间。

w_await

每一次I/O请求写处理的平均时间(单位是毫秒),这包括队列中的请求所用的时间和为它们提供服务的时间。

svctm

平均每次I/O请求的处理时间(毫秒为单位)。警告!不必太信任此字段,在未来版本会被移除。

然而对于那些熟悉其它UNIX系统的人来说它是很容易被误读的。比如在HP-UX上 avserv(相当于Linux上的svctm)是最重要的I/O指标,反映了硬盘设备的性能,它是指I/O请求从SCSI层发出、到I/O完成之后返回SCSI层所消耗的时间,不包括在SCSI队列中的等待时间,所以avserv体现了硬盘设备处理I/O的速度,又被称为disk service time,如果avserv很大,那么肯定是硬件出问题了。然而Linux上svctm的含义截然不同,事实上在iostat(1)和sar(1)的man page上都说了不要相信svctm,该指标将被废弃。

%util

%util表示该设备有I/O(即非空闲)的时间比率,不考虑I/O有多少,只考虑有没有。%util取值公式为在统计时间内CPU所有处理I/O时间除以总共统计时间的占比。例如,如果统计间隔1秒,CPU有0.8秒在处理I/O,而0.2秒闲置,那么该设备的%util = 0.8/1 = 80%,即被I/O消耗的CPU百分比,所以该参数暗示了设备的繁忙程度。一般地,如果该参数是100%表示设备已经可能接近满负荷运行了。

由于现代硬盘设备都有并行处理多个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)有没有哪个指标可以衡量硬盘设备的饱和程度呢?很遗憾,没有。

以上各值之间也存在联系,我们可以由一些值计算出其他数值,例如:%util = (r/s+w/s) * (svctm/1000)

对于上面的例子有:%util = (7.8+31.49)*(2.62/1000) = 0.098

await多大才算有问题?

await是单个I/O所消耗的时间,包括硬盘设备处理I/O的时间和I/O请求在kernel队列中等待的时间,正常情况下队列等待时间可以忽略不计,姑且把await当作衡量硬盘速度的指标吧,那么多大算是正常呢?

对于SSD,从0.0x毫秒到1.x毫秒不等,具体看产品手册;

大致来说一万转的机械硬盘是8.38毫秒,包括寻道时间、旋转延迟、传输时间。

在实践中,要根据应用场景来判断await是否正常,如果I/O模式很随机、I/O负载比较高,会导致磁头乱跑,寻道时间长,那么相应地await要估算得大一些;如果I/O模式是顺序读写,只有单一进程产生I/O负载,那么寻道时间和旋转延迟都可以忽略不计,主要考虑传输时间,相应地await就应该很小,甚至不到1毫秒。在以下实例中,await是7.50毫秒,似乎并不大,但考虑到这是一个dd测试,属于顺序读操作,而且只有单一任务在该硬盘上,这里的await应该不到1毫秒才算正常:

Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util

sdg 0.00 0.00 133.00 0.00 2128.00 0.00 16.00 1.00 7.50 7.49 99.60

1

2

Device:rrqm/swrqm/sr/sw/srsec/swsec/savgrq-szavgqu-szawaitsvctm%util

sdg0.000.00133.000.002128.000.0016.001.007.507.4999.60

对磁盘阵列来说,因为有硬件缓存,写操作不等落盘就算完成,所以写操作的service time大大加快了,如果磁盘阵列的写操作不在一两个毫秒以内就算慢的了;读操作则未必,不在缓存中的数据仍然需要读取物理硬盘,单个小数据块的读取速度跟单盘差不多。

常用的统计命令如下:

# 查看TPS和吞吐量信息

$ iostat -dk 1 10

# 查看设备使用率(%util)、响应时间(await)

$ iostat -dxk 1 10

# 查看cpu状态

$ iostat -c 1 10

# 查看指定设备

$ iostat -x sda 2 6

1

2

3

4

5

6

7

8

9

10

11

# 查看TPS和吞吐量信息

$iostat-dk110

# 查看设备使用率(%util)、响应时间(await)

$iostat-dxk110

# 查看cpu状态

$iostat-c110

# 查看指定设备

$iostat-xsda26

四、/proc/diskstats

对于统计类的命令,主要是收集系统中的参数,然后人性化地展示给我们观看,如iostat命令大概收集了几个主要系统文件如下:

/proc/stat #包含系统统计数据.

/proc/uptime #包含系统运行时间.

/proc/diskstats #包含磁盘统计数据.

/sys #包含块设备统计数据.

/proc/self/mountstats #包含文件系统统计数据.

/dev/disk #包含设备名称.

1

2

3

4

5

6

/proc/stat#包含系统统计数据.

/proc/uptime#包含系统运行时间.

/proc/diskstats#包含磁盘统计数据.

/sys#包含块设备统计数据.

/proc/self/mountstats#包含文件系统统计数据.

/dev/disk#包含设备名称.

iostat中磁盘信息实际上从/proc/diskstats文件读取的相关数据。要真正理解iostat的输出结果,应该从理解/proc/diskstats开始。对于/proc/diskstats文件,基本信息如下。

$ cat /proc/diskstats

1 0 ram0 0 0 0 0 0 0 0 0 0 0 0

...

7 0 loop0 0 0 0 0 0 0 0 0 0 0 0

...

8 0 sda 134771 316517 5865412 121367 1522470 1383112 23244672 475329 0 365221 596371

8 1 sda1 680 105 24042 98 16 6 176 3 0 74 101

1

2

3

4

5

6

7

$cat/proc/diskstats

10ram000000000000

...

70loop000000000000

...

80sda134771316517586541212136715224701383112232446724753290365221596371

81sda168010524042981661763074101

用于显示磁盘、分区和统计信息:sda为整个硬盘的统计信息,sda1为第一个分区的统计信息,ramdisk设备为通过软件将RAM当做硬盘来使用的一项技术。

从左至右分别对应主设备号,次设备号和设备名称。后续的11个字段中除了第9个字段,所有的其他字段都是从启动时的累积值。

第1字段:读磁盘的次数,成功完成读的总次数。

第2字段:合并读完成次数。为了效率,可能会合并相邻的读和写。从而两次4K的读在它最终被处理到磁盘上之前可能会变成一次8K的读,最终交给磁盘。所以它将被计算 (和排队) 作为一个 I/O,此字段使你知道此操作的频率。

第3字段:读扇区的次数,成功读过的扇区总次数。

第4字段:读花费的毫秒数,这是所有读操作所花费的毫秒数。

第5字段:写完成的次数,成功写完成的总次数。

第6字段:合并写完成次数。为了效率,可能会合并相邻的读和写。从而两次4K的读在它最终被处理到磁盘上之前可能会变成一次8K的读,最终交给磁盘。所以它将被计算 (和排队) 作为一个 I/O,此字段使你知道此操作的频率。

第7字段:写扇区的次数,成功写扇区总次数。

第8字段:写花费的毫秒数,这是所有写操作所花费的毫秒数。

第9字段:正在处理的输入/输出请求数,就是I/O的当前进度,只有这个字段应该是0,随着请求被交给适当的队列并随着它们的完成而递减而递增。

第10字段:执行I/O操作花费的毫秒数,只要字段9为非0,这个字段会增长。

第11字段:执行I/O操作花费的毫秒数,在每次I/O开始、I/O完成、I/O合并时都会增加此字段,或读取正在进行的I/O数量的这些统计数据(字段9)乘以自上次更新此字段以来所花的毫秒数。这可以为I/O完成时间和可能累积的积压提供简单的度量。