一、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 %idle5.47 0.50 8.96 48.26 0.00 36.82Device: 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.40sdb 0.00 4.00 0.00 350.00 0.00 2068.00 5.91 0.55 1.58 0.54 18.8001.直接执行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 } ] 后面接要监测的设备,例如sda62 -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 BUGS79 /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 %idle16.24 0.00 4.31 79.44Device: 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/c0d00.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.120 -p PID, --pid=PID 指定监测的进程/线程21 -u USER, --user=USER 指定监测某个用户产生的I/O22 -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 is35 placed in the public domain.三、开启block_dump使用dmesg查看
Linux内核里提供了一个block_dump参数用来把block读写(WRITE/READ)状况dump到日志里,这样可以通过dmesg命令来查看,具体操作步骤是:
# sysctl vm.block_dump=1or# 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()