首页 > 代码库 > 后端系统性能优化(第一季 2 找出坏代码)
后端系统性能优化(第一季 2 找出坏代码)
昨天开了个头
博文链接:后端系统性能优化(第一季:改掉那些坏代码)
今天,来说说 什么样的代码才是坏代码,怎么来找出这些坏代码。
不少猿在吐槽烂代码。但是我们今天说的不是烂代码,坏代码只需要改动很小的一部分,把它的坏的地方改掉,他依然是好代码 。而烂代码,只有重新写过了,才会让你觉得浑身轻松,压力瞬间释放,而且在写之前你还得花90%的时间去看懂它。所以我说改掉坏代码,因为只有坏代码才能改,而烂代码是用来看。我很庆幸我在的这个团队的代码驾驭能力都还不错,很少有烂代码。但为什么还会有坏代码?坏代码不是与生俱来的,他在刚上线的时候也许也是好代码,慢慢的变坏了,没关系,我们把它找出来。
找出坏代码,我们有两个阶段
第一个阶段是我们应用无法对自身的性能进行监控,只能通过DB提供性能较差以及执行频率过高的sql。DBA给你找出那些高峰期太过消耗资源的sql,这些sql可以大致的让你找出来是哪个功能再具体到哪个方法。博主负责的项目使用了mybatis框架(用hibernate的,我就只能 呵呵 了),如果有sql,我们能很快的定位到具体的方法,然后再进一步的查看代码,了解业务,并修改影响性能的代码。但是这种通过sql定位的方法并不全面,只能找出单条具有性能问题sql所在的方法, sql本身的性能是以条为单位,而我们的方法往往包含多个sql。如:2个每个只需要250ms的sql,在同一个方法里面,那就要消耗500ms,这种情况,sql性能上没有较大的问题,通过DB是很难找出来的。一些有性能问题的单条sql(查询比较多)以及执行频率较高的sql,DBA会比较容易提供。而且这种sql性能的问题,优化的手段非常有限,如增加一些条件或者hint的方式强制走索引,DBA绑定执行计划降低sql的消耗,比较复杂的查询的sql拆分成单表查询,增加字段进行关联属性的冗余,数据库中的字典值直接放入到内存中,尽量减少大表之间的关联查询......详细的优化手段的使用场景,我在下一节中再详细的介绍。
第二阶段是,建立应用内部的自我监控,我们使用spring的aop实现了一个简单高效的监控功能,这个监控功能可以切入到各个层,如service,manager,dao等等,切入之后,每一层中的方法所消耗的时间都会放入到内存中,方法调用结束后,如果超出预先设定的执行时间如 500ms 预警线,就会将各个调用执行的方法的时间打印在一个日志中,这非常有用,而且实现的简单优雅,基本能满足应用的自我性能监控所需要的数据。但它也是有缺点的,如果预警线设置的过低,对IO的消耗还是有点小严重。一般情况下,性能优化的预警线我们设置的时间为500ms。这样,执行时间超过500ms的方法都会收集在日志中。
使用应用内部的自我监控,还有一个非常明显的好处,在系统平均响应时间突然变慢的时候,我们打开这个日志可以很快定位出到底是什么问题。几个月前在我们的应用中,使用了一个叫 hazelcast的缓存组件,jvm毫无征兆的OOM,并由我们的运维系统自动重启,我们打开这个日志,定位到是hazelcast操作超时造成,很快,我们就有了下一步的动作。
有了性能监控的日志文件之后,我们就有了基础数据,通过脚本每天分析出来性能最差的top10和超过500ms执行次数的top10。得到类似如下的数据
请求次数、平均响应时间、请求方法名
238 1346.82 BServiceImpl.createA
184 6750.17 AServiceImpl.queryA
159 2620.29 CServiceImpl.getC
这个简单有效,能有如此多的好处的小东西,到底是怎么实现的?
我在之前的博文中有介绍 spring AOP的方式监控方法的执行时间
它记录下来的文件的格式大概是:
2014-05-04 23:12:00 [550,100%,0] A.createA() ----------------------------[10,50%,5] B.queryB() ---------------------------------[5,50%,10] C.queryC() ----------------------------[20,100%,30] C.queryD();
前面的东西都很好理解,方括号的东西的意思是 第一个数字 方法执行的总时间,第二个百分比数字,方法执行占上一层方法调用总耗时百分比,第三个数字的意思是时间流,从0开始,执行到哪个方法的时候耗时多少。
这种记录的方式让我们很快的就能找到对应的坏代码的地方。
总之,只有对自身的应用有所了解,才能准确的找到那些坏的代码。找到了他们,性能优化就成功了一半。
还没有实现自己应用内部监控的,赶紧写一个,让坏代码无处可藏。