一、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()