也谈程序性能测试

        程序出现性能问题的时候,我们怎么去排查呢?我之前一接到告警就先想是不是程序哪里出问题了,其实这样是不对的。应该首先排查运行的操作系统当时的状况是否健康。毕竟程序运行非常受环境影响,比方说网络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/swkB/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。只是有时候我们往往用并发数来描述吞吐量就够了,因为每个包大小都差不多,并发数这个相对量正比例于实际吞吐量。

你可能感兴趣的:(也谈程序性能测试)