一、系统性能理解
如果说需求、开发、DB、运维、测试是单一一门学科,那么性能就是综合学科,它包含了需求分析、DB、开发、测试、运维的所有学科。其实一般来说在实际性能分析和调优中,测试担任的角色就是写压测脚本并执行脚本查看结果,然后配合开发和运维来对压测结果不通过的或者不满意的地方一起分析和优化,等开发和运维优化好之后再次压测看是否通过,反复如此知道压测通过。
让我们先来说说如何什么是系统性能。这个定义非常关键,如果我们不清楚什么是系统性能,那么我们将无法定位之。 总体来说,系统性能就是两个事:
Throughput :吞吐量。也就是每秒钟可以处理的请求数,任务数。
Latency:系统延迟。也就是系统在处理一个请求或一个任务时的延迟。
一般来说,一个系统的性能受到这两个条件的约束,缺一不可。比如,我的系统可以顶得住一百万的并发,但是系统的延迟是2分钟以上,那么,这个一百万的负载毫无意义。系统延迟很短,但是吞吐量很低,同样没有意义。所以,一个好的系统的性能测试必然受到这两个条件的同时作用。 有经验的朋友一定知道,这两个东西的一些关系:
Throughput越大,Latency会越差。因为请求量过大,系统太繁忙,所以响应速度自然会低。
Latency越好,能支持的Throughput就会越高。因为Latency短说明处理速度快,于是就可以处理更多的请求。
二、分析及调优流程
所以当我们执行完压测脚本后,如何对压测结果进行分析和调优呢?下面我将带领大家捋一捋性能分析和调优的顺序和想法。
一般分析和调优的方法分为两种:从上至下和从下至上,如下图所示:
三、性能分析
服务器问题
首先,当我们系统有问题的时候,我们不要急于去调查我们代码,这个毫无意义。我们首要需要看的是操作系统的报告。看看操作系统的CPU利用率,看看内存使用率,看看操作系统的IO,还有网络的IO,网络链接数等等。在压测过程中需要让运维配合查看服务器的这些指标。
1)先看CPU利用率,如果CPU利用率不高,但是系统的Throughput和Latency上不去了,这说明我们的程序并没有忙于计算,而是忙于别的一些事,比如IO。(
2)然后,我们可以看一下IO大不大,IO和CPU一般是反着来的,CPU利用率高则IO不大,IO大则CPU就小。关于IO,我们要看三个事,一个是磁盘文件IO,一个是驱动程序的IO(如:网卡),一个是内存换页率。这三个事都会影响系统性能。
3)然后,查看一下网络带宽使用情况,在Linux下,你可以使用iftop, iptraf, ntop, tcpdump这些命令来查看。或是用Wireshark来查看。
4)如果CPU不高,IO不高,内存使用不高,网络带宽使用不高。但是系统的性能上不去。这说明你的程序有问题,比如,你的程序被阻塞了。可能是因为等那个锁,可能是因为等某个资源,或者是在切换上下文。
通过了解操作系统的性能,我们才知道性能的问题,比如:带宽不够,内存不够,TCP缓冲区不够,等等,很多时候,不需要调整程序的,只需要调整一下硬件或操作系统的配置就可以了。
中间件问题
其次如果硬件指标都没有问题,需要查看中间件相关指标,例如:线程池、连接池、GC等,如果是这些指标问题,需要深入的分析。
数据库问题
如果中间件相关指标没问题,需要查看数据库相关指标,例如:慢查SQL、命中率、锁、参数设置。总结下来就是:
1)慢查询
2)数据库连接池设置太小,导致数据库连接出现排队
3)数据库出现死锁
程序问题
如果以上指标都正常,应用程序的算法、缓冲、缓存、同步或异步可能有问题,需要具体深入的分析。
压测机问题
Jmeter单机负载能力有限,如果需要模拟的用户请求书超过负载,也会导致TPS压不下去。这时候我们需要考虑分布式压测了。
总结下来就是如下图所示:
四、性能调优
1.CPU
CPU资源利用率很高的话,需要看CPU消耗User、Sys、Wait哪种状态。
如果CPU User非常高,需要查看消耗在哪个进程,可以用top(linux)命令看出,接着用top –H –p 看哪个线程消耗资源高。如果是Java应用,就可以用jstack看出此线程正在执行的堆栈,看资源消耗在哪个方法上,查看源代码就知道问题所在;如果是c++应用,可以用gprof性能工具进行分析。
如果CPU Sys非常高,可以用strace(linux)看系统调用的资源消耗及时间。
如果CPU Wait非常高,考虑磁盘读写了,可以通过减少日志输出、异步或换速度快的硬盘。
2.Memory
操作系统为了最大化利用内存,一般都设置大量的cache,因此,内存利用率高达99%并不是问题,内存的问题主要看某个进程占用的内存是否非常大以及是否有大量的swap(虚拟内存交换)。
3.磁盘I/O
磁盘I/O一个最显著的指标是繁忙率,可以通过减少日志输出、异步或换速度快的硬盘来降低繁忙率。
4.网络I/O
网络I/O主要考虑传输内容大小,不能超过硬件网络传输的最大值70%,可以通过压缩减少内容大小、在本地设置缓存以及分多次传输等操作提高网络I/O性能。
5.JVM
JVM主要分析GC/FULL GC是否频繁,以及垃圾回收的时间,可以用jstat命令来查看,对于每个代大小以及GC频繁,通过jmap将内存转储,再借助工具HeapAnalyzer来分析哪地方占用的内存较高以及是否有内存泄漏可能。简单点可以使用APM工具,例如阿里云ARMS。
6.线程池
如果线程不够用,可以通过参数调整,增加线程;对于线程池中的线程设置比较大的情况,还是不够用可能的原因是:某个线程被阻塞来不及释放,可能在等锁、方法耗时较长、数据库等待时间很长等原因导致,需要进一步分析才能定位。
7.JDBC连接池
连接池不够用的情况下,可以通过参数进行调整增加;但是对于数据库本身处理很慢的情况下,调整没有多大的效果,需要查看数据库方面以及因代码导致连接未释放的原因。
8.SQL
SQL效率低下也是导致性能差的一个非常重要的原因,可以通过查看执行计划看SQL慢在哪里,这个需要优化SQL语句。一般来说开发负责排查。
9.代码优化
最高效的程序就是不执行任何代码的程序,所以,代码越少性能就越高。关于代码级优化的技术大学里的教科书有很多示例了。如:减少循环的层数,减少递归,在循环中少声明变量,少做分配和释放内存的操作,尽量把循环体内的表达式抽到循环外,条件表达的中的多个条件判断的次序,尽量在程序启动时把一些东西准备好,注意函数调用的开销(栈上开销),注意面向对象语言中临时对象的开销,小心使用异常(不要用异常来检查一些可接受可忽略并经常发生的错误),…… 等等,等等,这连东西需要我们非常了解编程语言和常用的库。
名词解释
1.负载均衡(Server Load Balancer,下文简称 SLB)的引入,可以降低单台云服务器 ECS(下文简称 ECS)出现异常时对业务的冲击,提升业务的可用性。同时,结合弹性伸缩服务,通过动态调整后端服务器,可以快速对业务进行弹性调整(扩容或缩容),以快速应对业务的发展。以网络层面进行负载均衡,通过将客户端请求分发到多个服务器来均衡负载。SLB可以根据服务器的性能指标、连接数、会话信息等进行智能的流量分发,从而提高系统的弹性和稳定性。SLB还支持多种负载均衡算法,如轮询、加权轮询、最小连接数等。
2.Nginx(Ng)是一款高性能的开源反向代理服务器,它也可以用作负载均衡器。Nginx的负载均衡功能可以在应用层面进行,即对请求进行解析,并根据自定义的规则将请求转发给后端服务器。Nginx的负载均衡支持多种算法,包括轮询、加权轮询、IP哈希等,可以根据实际需求灵活配置。
3.线程池就是事先将多个线程对象放到一个容器中,当使用的时候就不用new线程而是直接去池中拿线程即可,节省了开辟子线程的时间,提高的代码执行效率。
4.连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。
5.GC,全称 Garbage Collection,即垃圾回收,是一种自动内存管理的机制。
当程序向操作系统申请的内存不再需要时,垃圾回收主动将其回收并供其他代码进行内存申请时候复用,或者将其归还给操作系统,这种针对内存级别资源的自动回收过程,即为垃圾回收。而负责垃圾回收的程序组件,即为垃圾回收器。
6.CDN的全称是Content Delivery Network,即内容分发网络。其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输得更快、更稳定。通过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN系统能够实时地根据网络流量和各节点的连接、负载状况以及到用户的距离和响应时间等综合信息将用户的请求重新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet网络拥挤的状况,提高用户访问网站的响应速度。