前言
性能调优其实是一个很大的课题,也是最重要的课题,无论是面试还是工作中都能体现出来。所以,我也将学习记录着调优相关的知识。
先了解一下哪些计算机资源会成为系统的性能瓶颈
CPU
有的应用需要大量计算,他们会长时间、不间断地占用 CPU 资源,导致其他资源无法争夺到 CPU 而响应缓慢,从而带来系统性能问题。例如,代码递归导致的无限循环,正则表达式引起的回溯,JVM 频繁的 FULL GC,以及多线程编程造成的大量上下文切换等,这些都有可能导致 CPU 资源繁忙。
对于CPU的分析我们一般可以:
-
通过 top 命令,来观测 CPU 的性能,比如:输入 top命令,按 1 可以看到上面的指标
CPU 的使用有多个维度的指标,分别说明:
- us 用户态所占用的 CPU 百分比,即引用程序所耗费的 CPU;
- sy 内核态所占用的 CPU 百分比,需要配合 vmstat 命令,查看上下文切换是否频繁;
- ni 高优先级应用所占用的 CPU 百分比;
- wa 等待 I/O 设备所占用的 CPU 百分比,经常使用它来判断 I/O 问题,过高输入输出设备可能存在非常明显的瓶颈;
- hi 硬中断所占用的 CPU 百分比;
- si 软中断所占用的 CPU 百分比;
- st 在平常的服务器上这个值很少发生变动,因为它测量的是宿主机对虚拟机的影响,即虚拟机等待宿主机 CPU 的时间占比,这在一些超卖的云服务器上,经常发生;
- id 空闲 CPU 百分比。 一般比较关注空闲 CPU 的百分比,它可以从整体上体现 CPU 的利用情况。
-
通过负载,评估 CPU 任务执行的排队情况;
评估 CPU 任务执行的排队情况,需要通过负载(load)来完成。除了 top 命令,使用 uptime 命令也能够查看负载情况,load 的效果是一样的,分别显示了最近 1min、5min、15min 的数值。如上图所示,以单核操作系统为例,将 CPU 资源抽象成一条单向行驶的马路,则会发生以下三种情况:
-
马路上的车只有 4 辆,车辆畅通无阻,load 大约是 0.5;
-
马路上的车有 8 辆,正好能首尾相接安全通过,此时 load 大约为 1;
-
马路上的车有 12 辆,除了在马路上的 8 辆车,还有 4 辆等在马路外面,需要排队,此时 load 大约为 1.5。
但是需要注意的是 load 的值达到 1,并不是说系统负载已经到了极限,如果是单核的硬件上没有问题,但是 多核硬件上,就与CPU个数有关了。举个例子:
单核的负载达到 1,总 load 的值约为 1;双核的每核负载都达到 1,总 load 约为 2;四核的每核负载都达到 1,总 load 约为 4。
所以,对于一个 load 到了 10,却是 16 核的机器,你的系统还远没有达到负载极限。 -
-
通过 vmstat,看 CPU 的繁忙程度。 CPU 的繁忙程度,可以通过 vmstat 命令,如下图:
比较关注的几列:
- b 如果系统有负载问题,就可以看一下 b 列(Uninterruptible Sleep),它的意思是等待 I/O,可能是读盘或者写盘动作比较多;
- si/so 显示了交换分区的一些使用情况,交换分区对性能的影响比较大,需要格外关注;
- cs 每秒钟上下文切换(Context Switch)的数量,如果上下文切换过于频繁,就需要考虑是否是进程或者线程数开的过多。
每个进程上下文切换的具体数量,可以通过查看内存映射文件获取,如下代码所示:[root@localhost ~]# cat /proc/2788/status ... voluntary_ctxt_switches: 93950 nonvoluntary_ctxt_switches: 171204
内存
Java 程序一般通过 JVM 对内存进行分配管理,主要是用 JVM 中的堆内存来存储Java 创建的对象。系统堆内存的读写速度非常快,所以基本不存在读写性能瓶颈。但是由于内存成本要比磁盘高,相比磁盘,内存的存储空间又非常有限。所以当内存空间被占满,对象无法回收时,就会导致内存溢出、内存泄露等问题。
可以通过 top 命令关注内存的指标
如上图所示,我们看一下内存的几个参数,从 top 命令可以看到几列数据,注意方块框起来的三个区域,解释如下:
- VIRT 这里是指虚拟内存,一般比较大,不用做过多关注;
- RES 我们平常关注的是这一列的数值,它代表了进程实际占用的内存,平常在做监控时,主要监控的也是这个数值;
- SHR 指的是共享内存,比如可以复用的一些 so 文件等
磁盘 I/O
磁盘相比内存来说,存储空间要大很多,但磁盘 I/O 读写的速度要比内存慢,虽然目前引入的 SSD 固态硬盘已经有所优化,但仍然无法与内存的读写速度相提并论。
最能体现 I/O 繁忙程度的,就是 top 命令和 vmstat 命令中的 wa%。如果你的应用写了大量的日志,I/O wait 就可能非常高
iostat 是便捷好用的查看磁盘 I/O 的工具,你可以通过 sysstat 包进行安装。
上图中的指标详细介绍如下所示。
- %util:我们非常关注这个数值,通常情况下,这个数字超过 80%,就证明 I/O 的负荷已经非常严重了。
- Device:表示是哪块硬盘,如果你有多块磁盘,则会显示多行。
- avgqu-sz:平均请求队列的长度,这和十字路口排队的汽车也非常类似。显然,这个值越小越好。
- awai:响应时间包含了队列时间和服务时间,它有一个经验值。通常情况下应该是小于 5ms 的,如果这个值超过了 10ms,则证明等待的时间过长了。
- svctm:表示操作 I/O 的平均服务时间。svctm 和 await 是强相关的,如果它们比较接近,则表示 I/O 几乎没有等待,设备的性能很好;但如果 await 比 svctm 的值高出很多,则证明 I/O 的队列等待时间太长,进而系统上运行的应用程序将变慢。
网络
网络对于系统性能来说,也起着至关重要的作用。如果你购买过云服务,一定经历过,选择网络带宽大小这一环节。带宽过低的话,对于传输数据比较大,或者是并发量比较大的系统,网络就很容易成为性能瓶颈。
衡量一般系统的性能的指标
性能调优,所谓性能,就是使用有限的资源在有限的时间内完成工作。最主要的衡量因素就是时间,所以很多衡量指标,都可以把时间作为横轴。
加载的快慢是性能优化是否合理的一个非常直观的判断因素,但性能指标不仅仅包括单次请求的速度,它还包含更多因素,比如如下指标:
-
响应时间
响应时间是衡量系统性能的重要指标之一,响应时间越短,性能越好,一般一个接口的响应时间是在毫秒级。在系统中,我们可以把响应时间自下而上细分为以下几种:- 数据库响应时间:数据库操作所消耗的时间,往往是整个请求链中最耗时的;
- 服务端响应时间:服务端包括 Nginx 分发的请求所消耗的时间以及服务端程序执行所消耗的时间;
- 网络响应时间:这是网络传输时,网络硬件需要对传输的请求进行解析等操作所消耗的时间;
- 客户端响应时间:对于普通的 Web、App 客户端来说,消耗时间是可以忽略不计的,但
- 如果你的客户端嵌入了大量的逻辑处理,消耗的时间就有可能变长,从而成为系统的瓶颈。
-
吞吐量
在测试中,会比较注重系统接口的 TPS(每秒事务处理量),因为 TPS 体现了接口的性能,TPS 越大,性能越好。在系统中,可以把吞吐量自下而上地分为两种:磁盘吞吐量和网络吞吐量。-
磁盘吞吐量,磁盘性能有两个关键衡量指标。
-
一种是 IOPS(Input/Output Per Second),即每秒的输入输出量(或读写次数),这种是指单位时间内系统能处理的 I/O 请求数量,I/O 请求通常为读或写数据操作请求,关注的是随机读写性能。适应于随机读写频繁的应用,如小文件存储(图片)、OLTP 数据库、邮件服务器。
-
另一种是数据吞吐量,这种是指单位时间内可以成功传输的数据量。对于大量顺序读写频繁的应用,传输大量连续数据,例如,电视台的视频编辑、视频点播 VOD(Video OnDemand),数据吞吐量则是关键衡量指标。
-
-
网络吞吐量,这个是指网络传输时没有帧丢失的情况下,设备能够接受的最大数据速率。网络吞吐量不仅仅跟带宽有关系,还跟 CPU 的处理能力、网卡、防火墙、外部接口以及 I/O 等紧密关联。而吞吐量的大小主要由网卡的处理能力、内部程序算法以及带宽大小决定。
-
-
计算机资源分配使用率
通常由 CPU 占用率、内存使用率、磁盘 I/O、网络 I/O 来表示资源使用率。这几个参数好比一个木桶,如果其中任何一块木板出现短板,任何一项分配不合理,对整个系统性能的影响都是毁灭性的。负载承受能力当系统压力上升时,可以观察,系统响应时间的上升曲线是否平缓。这项指标能直观地反馈系统所能承受的负载压力极限。例如,当你对系统进行压测时,系统的响应时间会随着系统并发数的增加而延长,直到系统无法处理这么多请求,抛出大量错误时,就到了极限。