程序出现性能问题的时候,我们怎么去排查呢?我之前一接到告警就先想是不是程序哪里出问题了,其实这样是不对的。应该首先排查运行的操作系统当时的状况是否健康。毕竟程序运行非常受环境影响,比方说网络IO突然变慢了,可能是对方的服务器出现问题,也可能是网卡流量突然增大,导致等待时间过长。比如程序突然卡了,会不会是因为CPU负载增高了。下面我总结了一下,当程序出现性能问题,我们应该从哪些方面来排查呢。
我们先从CPU利用率、系统负载、内存使用情况、磁盘IO、网络IO等方面来了解一下linux的运行环境系统分析。
一. CPU利用率
命令为: cat /proc/stat |grep cpu
结果类似如下:
cpu 179584151 98 68568703 21032948653 45790 5837 6734236 0 0 0
cpu0 10215468 2 3003061 872428513 3152 726 467113 0 0 0
cpu1 7555721 1 3100525 875188647 1668 828 479287 0 0 0
cpu2 7512959 16 3465364 874751170 715 826 523902 0 0 0
cpu3 7328152 9 3652410 876087402 836 0 170879 0 0 0
cpu4 7152786 4 3294056 876698848 638 0 170233 0 0 0
cpu5 6935775 6 3038514 877141786 7839 10 179215 0 0 0
cpu6 8697735 1 3032698 875377195 4212 0 178054 0 0 0
......
根据机器配置,可能有多个CPU。第一行的数值表示的是CPU总的使用情况,其他各行是多核机器中每个具体CPU的参数。
我们分别看看上面各列参数的含义:
1.user:从系统启动开始累计到当前时刻,用户态的CPU时间 ,不包含 nice值为负的进程。
2.nice:从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间
3.system:从系统启动开始累计到当前时刻,内核态时间
4.idle:从系统启动开始累计到当前时刻,除硬盘IO等待时间以外其它等待时间
5.iowait:从系统启动开始累计到当前时刻,硬盘IO等待时间
6.irq:从系统启动开始累计到当前时刻,硬中断时间
7.softirq:从系统启动开始累计到当前时刻,软中断时间
8.steal:在虚拟环境下 CPU 花在处理其他作业系统的时间,Linux 2.6.11 开始才开始支持。
9.guest:在 Linux 内核控制下 CPU 为 guest 作业系统运行虚拟 CPU 的时间,Linux 2.6.24 开始才开始支持。
这里先解释一下什么是nice值:
系统中运行的每个进程都有一个优先级(亦称“nice 值”),其范围从 -20 (最高优先级)到 19 (最低优先级)。
默认情况下,进程的优先级是 0 (“基本”调度优先级)。优先级比较大的进程(nice 值比较小,最低到 -20)相对优先级比较小的进程(直到 19)
将比较频繁地被调度运行,因此就拥有更多的进程周期。一般用户只能降低它们自己进程的优先级别,并限于 0 到 19 之间。
超级用户(root)可以将任何进程的优先级设定为任何值。
我们看看CPU利用率是怎么计算的:
cpu_total=user+nice+system+idle+iowait+irq+softirq =》总时间
cpu_used=user+nice+system+irq+softirq=》使用时间
根据这两个值在不同世间点上的采集结果求差值再除以时间间隔,就可以得到一个时间段内的cpu使用率。可见总的时间就是使用时间加上硬盘IO的时间以及CPU空闲时间。
其他相关的命令:
cat /proc/cpuinfo
cat /proc/pid(此处要填写进程id)/stat
二. CPU负载
命令为: uptime
结果类似如下:
11:32:45 up 102 days, 19:28, 2 users, load average: 0.44, 0.44, 0.37
load average后面的3个值分别对应的是CPU分别在1分钟、5分钟和15分钟的平均负载。如果是单CPU的系统,这个值如果为1,那么就是接近100%,我们理想的值要让负载保持在0.7以下。如果超过1,那么就要排查一下原因了,把负载降下来。对应单CPU的系统,这个值可以为n*0.7,n为系统的CPU个数。
其他相关命令:
top
三. 系统内存
命令为:free -m
结果类似如下:
total used free shared buffers cached
Mem: 3320 3185 134 0 43 2557
-/+ buffers/cache: 584 2735
Swap: 2055 2055 0
其他相关命令:
cat /proc/(进程ID)/smaps
四. 磁盘IO
命令为:iostat -x 10
结果类似如下:
Linux XXXXXXXXXXXXX 01/10/17 _x86_64_ (4 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
0.82 0.00 0.53 0.63 0.00 98.02
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
vda 0.05 4.76 0.07 2.06 2.45 54.57 26.78 0.09 42.35 2.88 0.61
vdb 0.00 9.11 0.25 6.02 6.56 121.11 20.37 0.10 15.63 3.06 1.92
另一台机器上:
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.01 2.13 0.01 2.36 0.17 169.20 143.33 0.00 1.83 3.59 1.83 0.07 0.02
首先能看到avg-cpu这一行,前三列分别是用户态,nice值,内核态所占时间比。然后是硬盘io等待的时间,其他时间以及空闲时间。
下面的行
每一列的含义如下:
rrqm/s: 每秒进行merge的读操作数目。
wrqm/s: 每秒进行merge的写操作数目。
r/s: 每秒完成的读 I/O 设备次数。
w/s: 每秒完成的写 I/O 设备次数。
rkB/s: 每秒读K字节数。是 rsect/s 的一半,因为扇区大小为512字节
wkB/s: 每秒写K字节数。是 wsect/s 的一半
rsec/s: 每秒读扇区数。(这个和上面的rkB/s其实是可以相互计算的)
wsec/s: 每秒写扇区数。(这个和上面的wkB/s其实是可以相互计算的)
avgrq-sz: 平均每次设备I/O操作的数据大小 (扇区)
avgqu-sz: 平均I/O队列长度。
await: 平均每次设备I/O操作的等待时间 (毫秒)
svctm: 平均每次设备I/O操作的服务时间 (毫秒)
%util: 一秒中有百分之多少的时间用于 I/O 操作,或者说一秒中有多少时间 I/O 队列是非空的。
根据这些参数我们可以理解当前IO的性能状况:
1)如果%util 接近100%,说明产生的I/O请求太多,I/O系统已经满负荷,该磁盘可能存在瓶颈
2) svctm的大小一般和磁盘性能有关,CPU/内存的负荷也会对其有影响,请求过多也会间接导致 svctm 的增加。
3)await的大小一般取决于服务时间(svctm) 以及 I/O 队列的长度和 I/O 请求的发出模式。一般来说svctm < await,因为同时等待的请求的等待时间被重复计算了。如果svctm 比较接近await,说明I/O 几乎没有等待时间
4)如果await 远大于svctm,说明I/O队列太长,应用得到的响应时间变慢
5)队列长度(avgqu-sz)也可作为衡量系统 I/O 负荷的指标,但由于 avgqu-sz 是按照单位时间的平均值,所以不能反映瞬间的 I/O 洪水。
6)如果响应时间超过了用户可以容许的范围,这时可以考虑更换更快的磁盘,调整内核elevator算法,优化应用,或者升级 CPU。
7)如果%util很大,而rkB/s和wkB/s很小,一般是因为磁盘存在较多的磁盘随机读写,最好把磁盘随机读写优化成顺序读写。
五. 网络IO
命令为:cat /proc/net/dev
结果类似如下:
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
eth0: 1617072305 26950894 0 26950821 0 0 0 20 20132 85 0 0 0 0 0 0
eth1: 105406227830901 73602963634 0 1 1685 0 0 02556488698778 25535054407 0 0 0 0 0 0
bond0: 105407844903272 73629914529 0 26950822 1685 0 0 20 2556488718910 25535054492 0 0 0 0 0 0
lo: 301581754 1155867 0 0 0 0 0 0 301581754 1155867 0 0 0 0 0 0
重点看的是Receive_bytes,Receive_packets,Transmit_bytes,Transmit_packets。分别表示的是如流量和出流量。分别对每个网卡(比如eth0,eth1)取两个时间点求差,就可以得到网络流速和带宽。
网络IO中还有一个就是连接数,鉴于目前TCP是主流,我们只看系统的TCP连接数。
命令为:cat /proc/net/snmp
结果类似如下:
Ip: Forwarding DefaultTTL InReceives InHdrErrors InAddrErrors ForwDatagrams InUnknownProtos InDiscards InDelivers OutRequests OutDiscards OutNoRoutes ReasmTimeout ReasmReqds ReasmOKs ReasmFails FragOKs FragFails FragCreates
Ip: 2 64 5677934 0 0 0 0 0 4167838 4633344 0 10 0 0 0 0 0 0 0
Icmp: InMsgs InErrors InCsumErrors InDestUnreachs InTimeExcds InParmProbs InSrcQuenchs InRedirects InEchos InEchoReps InTimestamps InTimestampReps InAddrMasks InAddrMaskReps OutMsgs OutErrors OutDestUnreachs OutTimeExcds OutParmProbs OutSrcQuenchs OutRedirects OutEchos OutEchoReps OutTimestamps OutTimestampReps OutAddrMasks OutAddrMaskReps
Icmp: 116424 33808 0 116401 0 0 0 12 11 0 0 0 0 0 50742 0 50731 0 0 0 0 0 11 0 0 0 0
IcmpMsg: InType3 InType5 InType8 OutType0 OutType3
IcmpMsg: 116401 12 11 11 50731
Tcp: RtoAlgorithm RtoMin RtoMax MaxConn ActiveOpens PassiveOpens AttemptFails EstabResetsCurrEstab InSegs OutSegs RetransSegs InErrs OutRsts InCsumErrors
Tcp: 1 200 120000 -1 190217 1883 25602 1763 3 2821900 3894685 557672 2 11325 0
Udp: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors
Udp: 262 19 0 587723 0 0 0
UdpLite: InDatagrams NoPorts InErrors OutDatagrams RcvbufErrors SndbufErrors InCsumErrors
UdpLite: 0 0 0 0 0 0 0
看红色字体CurrEstab 3 表示当前系统有3个TCP连接。
相关命令:
iftop, iptraf, ntop, tcpdump
有了以上的基础参数,我们就可以采集到系统的基本健康状态。如果这些都稳定,我们的程序还是有性能或其他问题,那就要分析程序了。
一般对于我们的网络程序,常用的模式是收包,处理(可能包括磁盘IO,网络IO,数据库IO),返回。对于这种程序,衡量性能的标准经常用吞吐量(throughput)和时延(latency)。吞吐量反映的是程序每秒能接受多少个请求,也就是并发量,而时延反映的是每个请求处理需要多长时间。这两个指标要结合起来才能反映系统的状态。比如说某系统的吞吐量为100W,就是说1s内系统能接受100W个请求,也许是建立了100W个TCP连接,但是系统时延是10s,就是说从请求进到系统,到处理完毕花费了10s,这样的情况下系统也是不怎么样的。单纯吞吐量做大很容易,只要内存够大,能接受更多的请求,开多个进程大量的请求排队就可以了。问题是在大吞吐量下还要求低时延,这个就有挑战性了。因为你请求多了,排队多了,进程多了导致进程调度也慢了,排队的时间就更长了,所以时延就自然下降了,记住时延是从请求进入到系统算起的,包括了在TCP系统缓存里的等待时间,在应用队列里的等待时间。 所以我们做系统往往是追求吞吐量和时延之间的平衡。
除了这两个指标外,还有错误率(成功率)、资源占用情况(CPU占用率、内存占用等)、系统稳定情况等也是衡量程序性能的指标。
对于时延,我们不要用平均值来衡量,因为平均值往往反映不了真实的情况。比如10次请求时延,9次1ms,一次1s,你说这个系统的平均时延是100ms,好像有问题。可以描述为90%的请求时延为1ms,10%的请求时延为1s。最为正确的统计做法是用百分比分布统计,也就是英文中的TP – Top Percentile ,TP50的意思在,50%的请求都小于某个值,TP90表示90%的请求小于某个时间。比如路透做的金融系统响应时间的性能测试的要求是这样的,99.9%的请求必须小于1ms,所有的平均时间必须小于1ms。两个条件的限制(摘自http://coolshell.cn/articles/17381.html,更多的关于吞吐量和时延的讨论也可以参考该文章)。
关于吞吐量,其实用并发数描述也不完全准确,还是和并发上面的流量有关。比如1s中接受了100W的请求,但是每个请求只有1个字节,那吞吐量是1Mbps,而同样这100W个请求,每个请求是1000字节,那吞吐量就是1Gbps。只是有时候我们往往用并发数来描述吞吐量就够了,因为每个包大小都差不多,并发数这个相对量正比例于实际吞吐量。