iostat 实例分析

一、iostat 命令查看

%iowait并不能反应磁盘瓶颈

iowait实际测量的是cpu时间:
%iowait = (cpu idle time)/(all cpu time)

这 个文章说明:高速cpu会造成很高的iowait值,但这并不代表磁盘是系统的瓶颈。唯一能说明磁盘是系统瓶颈的方法,就是很高的read/write时 间,一般来说超过20ms,就代表了不太正常的磁盘性能。为什么是20ms呢?一般来说,一次读写就是一次寻到+一次旋转延迟+数据传输的时间。由于,现 代硬盘数据传输就是几微秒或者几十微秒的事情,远远小于寻道时间2~20ms和旋转延迟4~8ms,所以只计算这两个时间就差不多了,也就是 15~20ms。只要大于20ms,就必须考虑是否交给磁盘读写的次数太多,导致磁盘性能降低了。

作者的文章以AIX系统为例,使用其工具 filemon来检测磁盘每次读写平均耗时。在Linux下,可以通过iostat命令还查看磁盘性能。其中的svctm一项,反应了磁盘的负载情况,如 果该项大于15ms,并且util%接近100%,那就说明,磁盘现在是整个系统性能的瓶颈了。

iostat来对linux硬盘IO性能进行了解

以前一直不太会用这个参数。现在认真研究了一下iostat,因为刚好有台重要的服务器压力高,所以放上来分析一下.下面这台就是IO有压力过大的服务器

$iostat -x 1
Linux 2.6.33-fukai (fukai-laptop)          _i686_    (2 CPU)
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
5.47    0.50    8.96   48.26    0.00   36.82

Device:         rrqm/s   wrqm/s     r/s     w/s   rsec/s   wsec/s avgrq-sz avgqu-sz   await  svctm  %util
sda               6.00   273.00   99.00    7.00  2240.00  2240.00    42.26     1.12   10.57   7.96  84.40
sdb               0.00     4.00    0.00  350.00     0.00  2068.00     5.91     0.55    1.58   0.54  18.80


01.直接执行iostat不接参数,是指系统从启动到现在为止的统计数据。
02    
03    2.间隔参数代表每次报告的间隔时间,同样第一条是系统从启动到现在为止的统计数据,后续的每条都是iostat执行间隔时间内的统计数据。
04    
05    3.iostat创建3种类型报告,cpu使用率,设备使用率,网络文件系统报告。
06    
07    4.cpu使用率报告:
08    (1).%user:用户态所占用的CPU使用率百分比(应用程序)。
09    (2).%nice:用户态执行nice优先级所占用的CPU使用率百分比。
10    (3).%system:内核态所占用的CPU使用率百分比(内核)
11    (4).%iowait:CPU处于idle状态等待磁盘IO请求所占用的百分比。
12    (5).%steal:当hypervisor服务另一个(虚拟)CPU时,(虚拟)CPU强制等待的时间占比。
13    (6).%idle:没有磁盘请求时,CPU的空闲时间占比。
14    
15    5.设备使用率报告:
16    (1).Device:监测的设备或分区名称
17    (2).tps:代表每秒的传输数(transfer),传输数可以是单个的IO请求或合并多个逻辑请求到单个IO请求。
18    (3).Blk_read/s:每秒读取的block数,block相当于扇区的大小,即512字节。旧内核可能不确定。
19    (4).Blk_wrtn/s:每秒写入的block数。
20    (5).Blk_read:总读取的block数。
21    (6).Blk_wrtn:总写入的block数。
22    (7).kB_read/s kB_wrtn/s kB_read kB_wrtn MB_read/s MB_wrtn/s MB_read MB_wrtn: 同上,只是单位不同
23    (8).rrqm/s:每秒合并的读请求数。
24    (9).wrqm/s:每秒合并的写请求数。
25    (10).r/s:每秒读请求数。
26    (11).w/s:每秒写请求数。
27    (12).rsec/s:每秒的读扇区数。
28    (13).wsec/s:每秒的写扇区数。
29    (14).rkB/s:每秒读的kB数。
30    (15).wkB/s:每秒写的kB数。
31    (16).rMB/s:每秒读的MB数。
32    (17).wMB/s:每秒写的MB数。
33    (18).avgrq-sz:平均请求的大小(扇区)。
34    (19).avgqu-sz:平均队列长度。
35    (20).await:IO请求发送给设备和设备执行请求的时间(毫秒)。
36    (21).svctm:设备执行请求的时间(毫秒),此项不准,不可信。
37    (22).%util:I/O请求发送到设备期间,占用CPU时间的百分比。
38    
39    6.网络文件系统(NFS)报告
40    显示每个挂载的网络文件系统统计数据
41    (1).Filesystem:挂载的NFS服务器的主机名和目录
42    (2).rBlk_nor/s:使用read(2)系统调用接口读取的block数,block大小是512byte。
43    (3).wBlk_nor/s:使用write(2)系统调用接口写入的block数,block大小是512byte。
44    (4).rBlk_dir/s:使用O_DIRECT标志位读取的block数。
45    (5).wBlk_dir/s:使用O_DIRECT标志位写入的block数。
46    (6).rBlk_svr/s:NFS客户端通过NFS读请求从服务端读取的block数。
47    (7).wBlk_svr/s:NFS客户端通过NFS写请求往服务端写入的block数。
48    (8).rkB_nor/s wkB_nor/s rkB_dir/s wkB_dir/s rkB_svr/s wkB_svr/s rMB_nor/s wMB_nor/s rMB_dir/s wMB_dir/s rMB_svr/s wMB_svr/s,同上,单位不同而已
49    (9).ops/s:每秒到文件系统的总操作数
50    (10).rops/s:每秒到文件系统的读操作数
51    (11).wops/s:每秒到文件系统的写操作数
52    
53    选项:
54    -c     显示CPU使用率报告。
55    -d     显示设备使用率报告。
56    -h     使-n的NFS报告对人更易读。
57    -k     使统计数据以KB来表示,而不是扇区。
58    -m     使统计数据以MB来表示,而不是扇区。
59    -N     显示注册的设备映射名字,在使用逻辑卷LVM2时很好用。
60    -n     显示网络文件系统报告
61    -p [ { device [,...] | ALL } ] 后面接要监测的设备,例如sda
62    -t     显示时间戳,时间戳的格式受 S_TIME_FORMAT 环境变量影响。
63    -V     显示版本号然后退出
64    -x     显示扩展的统计数据,需要/proc/diskstats,挂载的sysfs,/proc/partitions等支持。
65    -z     如果统计时间内没有活动,iostat就不输出
66    
67    环境
68    iostat命令会受以下环境变量影响
69    S_TIME_FORMAT 如果这个环境变量存在,会影响iostat的时间戳格式,遵循ISO 8601格式
70    
71    例子
72    iostat 显示自启动以来的CPU和设备的报告。
73    iostat -d 2 每2秒间隔持续显示报告
74    iostat -d 2 6 每2秒间隔持续显示报告,显示6次
75    iostat -x hda hdb 2 6 为hda和hdb显示报告,每2秒间隔,显示6次
76    iostat -p sda 2 6 为sda及子分区显示报告,每2秒间隔,显示6次
77    
78    BUGS
79    /proc 文件系统必须以挂载,iostat依赖它。扩展的统计数据只对2.5以上的内核可用。
80    svctm已经没有意义,由于I/O统计是通过block层计算出来的,我们并不知道磁盘驱动器什么时候开始处理一个请求。
81    因此,下一个版本将删除svctm这项。
82    
83    文件
84    /proc/stat 包含系统统计数据
85    /proc/uptime 包含系统uptime.
86    /proc/partitions  包含磁盘统计数据,2.5内核支持
87    /proc/diskstats 包含磁盘统计数据,2.5内核支持
88    /sys contains 块设备的统计数据 (post 2.5 kernels).
89    /proc/self/mountstats 包含网络文件系统统计数据
90    


即 delta(use)/s/1000 (因为use的单位为毫秒)

如果 %util 接近 100%,说明产生的I/O请求太多,I/O系统已经满负荷,该磁盘可能存在瓶颈。

idle小于70% IO压力就较大了,一般读取速度有较多的wait。

同时可以结合vmstat 查看查看b参数(等待资源的进程数)和wa参数(IO等待所占用的CPU时间的百分比,高过30%时IO压力高)

另外 await 的参数也要多和 svctm 来参考。差的过高就一定有 IO 的问题。

avgqu- sz 也是个做 IO 调优时需要注意的地方,这个就是直接每次操作的数据的大小,如果次数多,但数据拿的小的话,其实 IO 也会很小.如果数据拿的 大,才IO 的数据会高。也可以通过 avgqu-sz × ( r/s or w/s ) = rsec/s or wsec/s.也就是讲,读定速度 是这个来决定的。

另外还可以参考

svctm 一般要小于 await (因为同时等待的请求的等待时间被重复计算 了),svctm 的大小一般和磁盘性能有关,CPU/内存的负荷也会对其有影响,请求过多也会间接导致 svctm 的增加。await 的大小一般取 决于服务时间(svctm) 以及 I/O 队列的长度和 I/O 请求的发出模式。如果 svctm 比较接近 await,说明 I/O 几乎没有等 待时间;如果 await 远大于 svctm,说明 I/O 队列太长,应用得到的响应时间变慢,如果响应时间超过了用户可以容许的范围,这时可以考虑 更换更快的磁盘,调整内核 elevator 算法,优化应用,或者升级 CPU。

队列长度(avgqu-sz)也可作为衡量系统 I/O 负荷的指标,但由于 avgqu-sz 是按照单位时间的平均值,所以不能反映瞬间的 I/O 洪水。

别人一个不错的例子(I/O 系统 vs. 超市排队)

举 一个例子,我们在超市排队 checkout 时,怎么决定该去哪个交款台呢? 首当是看排的队人数,5个人总比20人要快吧? 除了数人头,我们也常常 看看前面人购买的东西多少,如果前面有个采购了一星期食品的大妈,那么可以考虑换个队排了。还有就是收银员的速度了,如果碰上了连 钱都点不清楚的新手, 那就有的等了。另外,时机也很重要,可能 5 分钟前还人满为患的收款台,现在已是人去楼空,这时候交款可是很爽啊,当然,前提是那过去的 5 分钟里所 做的事情比排队要有意义 (不过我还没发现什么事情比排队还无聊的)。

I/O 系统也和超市排队有很多类似之处:

r/s+w/s 类似于交款人的总数

平均队列长度(avgqu-sz)类似于单位时间里平均排队人的个数

平均服务时间(svctm)类似于收银员的收款速度

平均等待时间(await)类似于平均每人的等待时间

平均I/O数据(avgrq-sz)类似于平均每人所买的东西多少

I/O 操作率 (%util)类似于收款台前有人排队的时间比例。

我们可以根据这些数据分析出 I/O 请求的模式,以及 I/O 的速度和响应时间。

下面是别人写的这个参数输出的分析

# 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 输出表明秒有 28.57 次设备 I/O 操作: 总IO(io)/s = r/s(读) +w/s(写) = 1.02+27.55 = 28.57 (次/秒) 其中写操作占了主体 (w:r = 27:1)。

平均每次设备 I/O 操作只需要 5ms 就可以完成,但每个 I/O 请求却需要等上 78ms,为什么? 因为发出的 I/O 请求太多 (每秒钟约 29 个),假设这些请求是同时发出的,那么平均等待时间可以这样计算:

平均等待时间 = 单个 I/O 服务时间 * ( 1 + 2 + … + 请求总数-1) / 请求总数

应用到上面的例子: 平均等待时间 = 5ms * (1+2+…+28)/29 = 70ms,和 iostat 给出的78ms 的平均等待时间很接近。这反过来表明 I/O 是同时发起的。

每秒发出的 I/O 请求很多 (约 29 个),平均队列却不长 (只有 2 个 左右),这表明这 29 个请求的到来并不均匀,大部分时间 I/O 是空闲的。

一秒中有 14.29% 的时间 I/O 队列中是有请求的,也就是说,85.71% 的时间里 I/O 系统无事可做,所有 29 个 I/O 请求都在142毫秒之内处理掉了。

delta(ruse+wuse)/delta(io) = await = 78.21 =& gt; delta(ruse+wuse)/s =78.21 * delta(io)/s = 78.21*28.57 = 2232.8,表明每秒内 的I/O请求总共需要等待2232.8ms。所以平均队列长度应为 2232.8ms/1000ms = 2.23,而 iostat 给出的平均队列长 度 (avgqu-sz) 却为 22.35,为什么?! 因为 iostat 中有 bug,avgqu-sz 值应为 2.23,而不 是 22.35。


二、使用iotop查看具体进程的io请求

有时我们希望知道到底哪个进程产生了IO,这个时候就需要iotop这个工具了。
它的输出和top命令类似,简单直观。


01    名称
02           iotop - 简单的top类I/O监视器
03    总览
04           iotop [OPTIONS]
05    描述
06           iotop根据Linux内核(需要2.6.20及以上)来监测I/O,并且能显示当前进程/线程的I/O使用率。
07           Linux内核build的事后哦,需要开启CONFIG_TASK_DELAY_ACCT和CONFIG_TASK_IO_ACCOUNTING选项,这些选项依赖于CONFIG_TASKSTATS。
08           在采样周期里,iotop按列显示每个进程/线程的I/O读写带宽,同时也显示进程/线程做swap交换和等待I/O所占用的百分比。
09           每一个进程都会显示I/O优先级(class/level),另外在最上面显示每个采样周期内的读写带宽。
10           使用左右箭头来改变排序,r用来改变排序顺序,o用来触发--only选项,p用来触发--processes选项。
11           a用来触发--accumulated选项,q用来退出,i用来改变进程或线程的监测优先级,其它任继健是强制刷新。
12    
13    选项
14           --version 显示版本号然后退出
15           -h, --help 显示帮助然后退出
16           -o, --only 只显示正在产生I/O的进程或线程。除了传参,可以在运行过程中按o生效。
17           -b, --batch 非交互模式,一般用来记录日志
18           -n NUM, --iter=NUM 设置监测的次数,默认无限。在非交互模式下很有用
19           -d SEC, --delay=SEC 设置每次监测的间隔,默认1秒,接受非整形数据例如1.1
20           -p PID, --pid=PID 指定监测的进程/线程
21           -u USER, --user=USER 指定监测某个用户产生的I/O
22           -P, --processes 仅显示进程,默认iotop显示所有线程
23           -a, --accumulated 显示累积的I/O,而不是带宽
24           -k, --kilobytes 使用kB单位,而不是对人友好的单位。在非交互模式下,脚本编程有用。
25           -t, --time 加上时间戳,非交互非模式。
26           -q, --quiet 禁止头几行,非交互模式。有三种指定方式。
27                  -q     只在第一次监测时显示列名
28                  -qq    永远不显示列名。
29                  -qqq   永远不显示I/O汇总。
30    参见
31           ionice(1), top(1), vmstat(1)
32    作者
33           iotop was written by Guillaume Chazarain.
34           This manual page was started by Paul Wise for the Debian project and is
35           placed in the public domain.


三、开启block_dump使用dmesg查看

Linux内核里提供了一个block_dump参数用来把block读写(WRITE/READ)状况dump到日志里,这样可以通过dmesg命令来查看,具体操作步骤是:

# sysctl vm.block_dump=1
or
# echo 1 > /proc/sys/vm/block_dump

  然后就可以通过 dmesg 就可以观察到各个进程 IO 活动的状况了


四、使用python脚本查看


#!/usr/bin/python
# Monitoring per-process disk I/O activity
# written by http://www.vpsee.com
 
import sys, os, time, signal, re
 
class DiskIO:
    def __init__(self, pname=None, pid=None, reads=0, writes=0):
        self.pname = pname
        self.pid = pid
        self.reads = 0
        self.writes = 0
 
def main():
    argc = len(sys.argv)
    if argc != 1:
        print "usage: ./iotop"
        sys.exit(0)
 
    if os.getuid() != 0:
        print "must be run as root"
        sys.exit(0)
 
    signal.signal(signal.SIGINT, signal_handler)
    os.system('echo 1 > /proc/sys/vm/block_dump')
    print "TASK              PID       READ      WRITE"
    while True:
        os.system('dmesg -c > /tmp/diskio.log')
        l = []
        f = open('/tmp/diskio.log', 'r')
        line = f.readline()
        while line:
            m = re.match(\
                '^(\S+)\((\d+)\): (READ|WRITE) block (\d+) on (\S+)', line)
            if m != None:
                if not l:
                    l.append(DiskIO(m.group(1), m.group(2)))
                    line = f.readline()
                    continue
                found = False
                for item in l:
                    if item.pid == m.group(2):
                        found = True
                        if m.group(3) == "READ":
                            item.reads = item.reads + 1
                        elif m.group(3) == "WRITE":
                            item.writes = item.writes + 1
                if not found:
                    l.append(DiskIO(m.group(1), m.group(2)))
            line = f.readline()
        time.sleep(1)
        for item in l:
            print "%-10s %10s %10d %10d" % \
                (item.pname, item.pid, item.reads, item.writes)
 
def signal_handler(signal, frame):
    os.system('echo 0 > /proc/sys/vm/block_dump')
    sys.exit(0)
 
if __name__=="__main__":
    main()


你可能感兴趣的:(iostat)