对于一些外包公司或为第三做系统的公司他们更关注业务的实现,而对性能几乎很少关注,直到业务企业的高层有对访问功能巨慢的切身体会之后,才会引起建设方对性能的重视。对于程序员也好可能很少关注自己曾经开发上线的系统目前的状况,一味地追求新技术,疏于对已经做过系统的分析,其实错过了最重要的提升自己的机会,最重要的还会对产品或公司产生不好的影响,所以线上系统进行分析对公司来说是一件重要的事情。在对系统线上的性能分析技能对于程序员来说是非常必要掌握的技能,因为通过问题分析,程序员不仅可以提升自己的能力,更重要的一方面可以改善系统的使用,提升系统用户使用满意度:
提升用户体验通过对线上系统的性能分析,我们可以提前预知系统可能存在卡顿,及时改进,确保用户不会流失。
维持运营效率在高并发的情况下,性能问题可能会导致系统崩溃或运行不稳定。
提前预防故障通过对系统进行性能分析和优化,可以预防潜在的故障和问题,确保系统的稳定运行。
性能测试与功能测试性能测试和功能测试是软件测试工作中两个不同的方面,它们共同保证软件产品的质量。
接下来我们分析一下性能分析的一些方法论,并且如何对现象进行抽丝剥茧最终找到性能瓶颈。
我们例举公司的办公自动化系统,这类系统特点是使用人员多(大型企业多在上万人)、并发高(通常在上班的时候员工集中访问OA信息)、部署复杂(通常会涉及企业内部的多个服务器、不同网络环境、混杂的数据库等应用)、业务复杂(通常会涉及业务的方方面面,有各种流程,各种审批和不同的例外条件)针对这样一个系统如果一旦出现问题,首先就会受到公司高层的压力,其他不用说,我总结了一些方法,接下来我们看一下如何进行分析:
此时我们只看见了问题表现的现象,并不知道问题发生的原因,此时最重要是收集数据。
那么我们应该按照什么思路来收集数据呢?我觉得应该按照请求访问链路来收集数据,比如:用户在手机订机票的时候特别慢,我认为应该按照,用户当时所处网络环境,经过了多少路由转发(包括外网和内网),应用服务器,数据库服务器,第三方数据接口,这样一个思路来收集数据,也就是我们收集的数据要覆盖整个链路,越全面越好,避免对重要信息的遗漏。
应对于这一点,市面上也有较多的应用(较多时付费的)可以监控网络层面、服务器层面、SQL执行层面、第三方接口层面进行监控,得知输入、输出、执行时间都可以进行监控。
为什么我要将方法放在第一位,我认为对的方法可以更加快速的找到问题的本质原因,也可以让分析少走弯路,所以方法和方向非常重要,只要方法和方向正确了,也许快点也许慢一点最终都能达到目标。但是如果方向错了那么速度越快反而距离目标越远。
那些是应该坚持的分析方法呢?
第一、我认为基本的有两点应该坚持,首先、纵向分析,即用户请求链路分析,通过从客户端到最终结果返回至客户端的请求日志能够标识某一个特定用户,然后按照链路逐一分析找出性能瓶颈。其次,横向分析,即通过客户端层、网络层、应用层和数据库层等方面分别分析,发现那些层面是影响性能的最大瓶颈。
第二、在对收集出来的数据中,特别需要注意一些异常值,比如某一个/多个表有上百万数据。
第三、注重前后对比,也就是故障发生前和故障发生后,请求量、数据库数据量、网络转发延时、应用处理时长 都有哪些特点,发生了哪些改变。这样一方面可以帮助我们快速找到问题,另一方面也可以让我们知道如果故障恢复应该恢复到那种状态。
以总结的方法,我认为可以应付大部分场景,接下来我们详细分析一下如何分层研究每一层的性能:
请求来自客户端,客户端是最终感知请求性能的地方,对客户端分析我们可以从哪些方面呢?我总结了以下几点:
第一、兼容性,如果是web页面,我们适当考虑是不是浏览器的兼容性,导致网页加载和解析较慢,毕竟不同的浏览器对不同浏览器内核的封装和调用方式不同,不可避免会出现因为封装方式不同导致性能下降的问题,这类问题建议用户固定某一个浏览器,或者在自己的APP内将合适的流量器封装进去。
第二、客户端操作系统,某些情况由于操作系统的问题导致系统性能问题,甚至导致系统出现BUG,这类问题就建议升级系统。
第三、客户端软件冲突,一般冲突了会抛出异常,只需要将冲突软件卸载掉就好了。
第四、客户端缓存了较老的数据,我们现在的浏览器为了提升用户使用体验度,一般会缓存用户数据,但是由于服务端修改这时客户端并未及时请求缓存那么就会导致显示不正确,此时要么让客户手动清空缓存,要么我们设置缓存失效时间短一些
第五,由于客户未预知的操作,这类问题较为复杂,如果是公司内部系统我们还可能找到操作人了解操作步骤,但是如果是对外开放的系统就很难知道用户操作步骤了,此时日志非常重要,我们要清晰记录用户每一步操作,然后逐步分析才有可能还原用户操作环境。
所有的请求和数据都需要通过网络进行转发,才能到达服务器,最终获取或写入正确的数据,所以网络过程的分析也是性能分析重要环节:
一般情况,我们将服务部署在内网服务器中,然后通过代理的方式通过DMZ区的IP将服务公布出去,也有可能是通过专线将服务提供给特定用户,此时我们的分析可以按照如下思路:
第一、用户所处的网络环境
对用户所处网络环境需要进行了解,比如用户使用的联通网络,但是我们服务暴露是电信网络,那么这之中就会有服务商与服务商之间的数据交互,如果这里面有性能问题,我们可以考虑将服务分别提供联通、移动和电信的服务。
如果用户所处地方较为偏远,那么就有可能信号不好导致加载很慢,我们需要考虑网络流量压缩。
如果用户是不是4G/5G,案例来所4G或5G对于网页浏览应该不会产生差异,这一点需要进行对比分析。
第二、内外网对比
我们的服务部署在内网,用户通过外网进行请求,那么请求就会通过外网一层层路由到内网,然后再有内网转发到对应的应用服务器,此时需要考虑,路由层是否过多,如果过多考虑CDN网络减少路由层,内网路由是否有性能瓶颈,我们在内网环境中使用网络回路命令(如:ping命令)可以查询到网络回复的情况。其次,可以考虑使用网络抓包软禁对网络丢包情况进行分析,如果丢包情况较多,考虑优化网络转发层次。
第三、网络转发层级
考虑使用网络追踪命令(tracert 或者 pathping)可以追踪路由,并打印节点信息
tracert www.baidu.com
通过最多 30 个跃点跟踪
到 www.a.shifen.com [14.119.104.189] 的路由:
1 2 ms 1 ms 3 ms 192.168.1.1
2 4 ms 4 ms 5 ms 10.1.1.1
3 4 ms 4 ms 4 ms 116.22.52.1
4 11 ms 14 ms 14 ms 113.98.85.121
5 3 ms 8 ms 3 ms 14.147.4.209
6 * * * 请求超时。
7 26 ms 5 ms * 121.14.14.162
8 24 ms 18 ms 10 ms 14.29.117.150
9 * * * 请求超时。
10 * * * 请求超时。
11 12 ms 4 ms 6 ms 14.119.104.189
跟踪完成。
通过上面的追踪,我们可知道路由相关情况。
第四、网络传输数据量
其实这一点也非常重要,就比如:水管尺寸和流动速度是固定的,如果输送的水量少,那么就很快,如果输送的水量多那肯定就慢了下来。这个道理同样也适用于网络数据传输,如果传输的数据量较大,并且遇到了并发请求,大数据翻了N倍要通过这条固定贷款的网线传输出来,势必用户的响应时间会被拖慢下来,用户也会感觉到系统慢。
所有的请求最终都需要在应用服务器进行相关的逻辑计算,才能将正确的结果反馈给客户端,所以服务器中代码运行效率非常重要。代码执行效率低下,一般要么是代码写的比较冗余执行效率不高,要么是机器性能较差导致执行低下,接下来提供几条分析服务器性能的角度:
第一、机器性能分析
这一点比较直接,直接可以通过机器的任务管理器里面就可以查看,一般运维会有Zabbix这一类的工具可以在一个端监控所有纳入监控的服务器性能,监控其CPU、内存、硬盘等使用情况,如果占比超过70%就要考虑升级或者扩容。PS:这里面不同的硬件设备也存在效率上的差异,这里就不多讲了。
第二、运行环境dump镜像分析
如果我们是JDK环境,通过jvisualvm我们可以将运行环境做一个运行缓慢时候的dump文件,然后分析dump文件,这就可以分析道那个类那些函数运行情况,对于有开发经验的小伙伴其实比较有利的
第三、进程和线程分析
同理我们可以通过jvisualvm 分析程序运行起来的进程和线程,看是否存在进程线程是否存在死锁等问题,这一点对于多线程运行非常重要,因为死锁会导致程序响应超时,积累过多的死锁会导致新的请求根本无法响应。
第四、代码分析
结合前面三点,最终落地于优化代码,这一点比较难,需要专门的开发知识才能进行,比如:优化线程池使用乐观锁避免死锁、降低代码冗余优化代码执行效率、采用异步“削峰”操作,降低代码IO频度等等,主打对用户及时响应。
以上是对服务器相关分析的一些总结
大多数业务程序过来要么需要向数据库中写入数据,要么需要从数据库中获取数据,其实都避免不了与数据库的操作,所以作为性能分析数据库的性能是避免不了的。接下来我总结几点对数据库的性能分析:
第一、慢查询
大多程序比较慢都出现在慢查询上面,慢查询主要有以下几种情况
(a)过多的关联
多张表关联查,如果使用union,那么其中的计算量会呈现数据的指数级增长,如果有这样的情况,建议将关联查询拆分一下,借鉴《阿里巴巴Java开发手册》里面规定,关联查询不超过3张表。
(b)采用错误的关联“大表关联小表”,正确的是“小表关联大表”
大表驱动小表其实是不合理的,这样会频繁增加过多的计算,采用小表驱动大表可以减少数据库数据计算数量
(c)未正确建立索引
索引其实是建立数据的一个AB二叉树,这样在查询中可以缩短查询路径提升查询性能,比如在一些高频查询中的where中,并没有加索引,导致全表扫描势必会降低效率。建议where条件中的条件字段添加索引。PS:建立了索引虽然查询的效率会提升,但是每一次写入/修改都需要对索引的二叉树进行修改,势必会降低写入/修改的性能,需要结合实际业务进行合理的设计。
d)未正确使用索引
在mysql中有一些建立了索引,但是未正确使用查询语句的时候,导致查询没有走索引,这会降低程序响应效率,以下几种情况会导致索引失效:
有or必全有索引;
复合索引未用左列字段;
like以%开头;
需要类型转换;
where中索引列有运算;
where中索引列使用了函数;
如果mysql觉得全表扫描更快时(数据少);
第二、特殊的大表(数据量大)
某些情况数据表里面有些表会存在上百万或上千万的数据,但是数据库并未分区分表,这会导致查询慢,此时我们需要考虑一下,为什么这些表会有这么多数据?需要关联业务,如果未关联到业务,也就是异常产生的数据,要么是程序代码有问题,要么是程序受到了额外的恶意攻击。
其次,这些数据有必要存这么久么?对于没有必要存太久的数据,根据业务重要性可以进行转储或直接删除的操作(直接删除的比如3个月之前的日志)
最后,考虑进行分库分表操作,这会设计到代码的变动,效果最好,但是代价也最大
第三、数据库不合理的设计
设计这一词,我认为没有绝对的正确和绝对的错误,都是就事论事,不同情况下采用不同的设计,这最符合该业务特点,对应也会提升性能。比如:我们在进行关系型数据库设计的时候,一直强调需要遵循“三范式”进行设计,请想一下也许遵循了三范式会导致很多小表,并且会产生过多的关联查询,这样合适么?要不要权衡一下,适当满足“三范式”,在必要的情况下也可以突破“三范式”的约束,为了开发效率和运行效率。也许在一些互联网企业中使用到关系型数据库就没那么严格的遵循三范式的要求了。
有些接口回去访问第三方接口,对于第三方接口我们能进行优化的概率较小,但是我们也可以采用一些方法加快性能。但是,首先,我们得使用日志记录第三方接口的执行效率,确定第三方接口执行效率较低。在明确了这一个节点之后,我们可以采用比如缓存最近一次接口数据,定期同步接口数据到本地等方法,当调用第三方接口超时的时候,要么我们从缓存读取数据要么从本地读取数据。
在线系统性能排故,是一个要求综合能力较高的工作,对操作系统、网络、应用服务和数据库都要有所了解才能进行。另一方面在线系统性能排故,会顶着巨大的压力,即来自领导层面也来自外部客户的,在面对这样的压力下还能潜心分析性能其实要求有一颗能够抗压的内心。当然性能分析最重要的是思路要对,步子要稳,对异常的内容要敏锐,多问几个Why,也许让我们的思路有更高的提升。