“望闻问切”是中医诊断疾病的必经步骤,“望”是指观气色,“闻”是指听声息,“问”是指询问症状,“切”是指摸脉象,合称“四诊”,经过这四个步骤,大夫基本上就能确认病症所在,然后加以药物调理,或能还以病人健康身躯。
一个应用系统如果出现性能问题,不管是偶发性问题还是持久性问题,都是系统“生病”的表现,需要工程师去诊断,然后对症下药。我们可以把Java的性能诊断也分为此四个过程(把我们自己想象成医生吧,只是我们的英文名字不叫Doctor,而是叫做Trouble Shooter):
(1)望
观察性能问题的症状。有人投诉我们开发出的系统性能慢,如蜗牛爬行,执行一个操作,在等待它返回的过程中,用户已经完成了倒水、喝茶、抽烟等一系列消遣活动,但系统还是没返回结果!其实这是个好现象,至少我们能看到症状,从而可以对症下药。性能问题从表象上来看可以分为两类:
不可(或很难)重现的偶发性问题
比如线程阻塞,在某种特殊条件下,多个线程访问共享资源时会被阻塞,但不会形成死锁,这种情况很难去重现,当用户打电话投诉时,我们自己赶到现场症状已经消失了,然后1个月内再也没有出现过,当我们都认为“磨合”期已过,系统已经正常运行的时候,又接到了类似的投诉,崩溃呀!对于这种情况,“望”已经不起作用了,不要为了看到症状而花费大量的时间和精力,可以采用后续提到的“闻问切”方式。
可重现的性能问题
客户打电话给我们,反映系统性能缓慢,不需要我们赶到现场,自己观察一下生产机就可以发现部分交易缓慢,CPU过高,可用内存较低等问题,在这种情况下我们至少要测试三个有性能问题的交易(或者三个与业务相关而技术无关的功能,或者与技术有关而业务无关的功能),为什么是三个呢?因为“永远不要带两块手表”,这会致使无法验证和校对。
比如三个不同的输入功能,都是用户输入信息,然后保存到数据库中,但是三个交易的性能都非常缓慢,通过初步的“望”我们就可以基本确认是与数据库或数据驱动相关的问题;若是只有一个交易缓慢,其他两个正常,那就可以大致定位到一个面:该交易的逻辑层出现问题。
(2)闻
中医上的“闻”是大夫听(或嗅)患者不自觉发出的声音和气味,在性能优化上的“闻”则是关注项目被动产生的信息,其中包括:项目组的技术能力(主要取决于技术经理的技术能力)、文化氛围、群体的习惯和习性,以及他们专注和擅长的领域等,各位读者可能要疑惑了:中医上“闻”的对象是病人,而为什么这里“闻”的对象却是开发团队呢?
我们这样来思考该问题,如果是一个人(个体)生病了,找大夫如此处理是没有任何问题的,但是如果是人类(群体)生病了,那如何追寻这个根源呢?假设人是上帝创造的,如果有一群外星生物说“人类都有自私的缺陷”,那是不是应该去观察一下上帝?了解这个缺陷是源于他的习惯性动作还是技能缺乏,或者是“文化传承”。对于一个Java应用来说,我们就是“上帝”,我们创造了他,给了他生命(能够运行),给了他尊严(用户需要它),给了他灵魂(解决了业务问题),那一旦他生病,是不是应该审视一下我们这些“上帝”呢?或者我们得自我反省一下呢?
如果项目组的技术能力很强,有资深的数据库专家,有顶尖的架构师,也有首席程序员,那性能问题产生的根源就应该定位在无意识的代码缺陷上。
如果项目组的文化氛围很糟糕,组员不交流,没有固定的代码规范,缺乏整体的架构等,那性能问题的根源就可能存在于某个配置上,或者相互的接口调用上。
如果项目组已经习惯了某一个框架,而且也习惯了框架的种种约束,那性能的根源就可能是有人越过了框架的协约。
需要注意的是,“闻”并不是主动地去了解,而是由技术(人或应用)自行挥发出的“味道”,需要我们要敏锐地抓住,这可能会对性能分析有非常大的帮助。
(3)问
“问”就是与技术人员(缔造者)和业务人员(使用者)一起探讨该问题,了解性能问题的历史状况,了解“慢”产生的前因后果,比如对于业务人员我们可以咨询:
性能是不是一直这样慢,从何时起慢到不能忍受?
哪一个操作或哪一类操作最慢,大概的等待时间是多长?
用户的操作习惯是什么,是喜欢快捷键还是喜欢用鼠标点击?
在什么时间段最慢,业务高峰期是否有滞顿现象,业务低谷是否也缓慢?
其他访问渠道,如移动设备是否也有效率问题?
业务品种和数量有没有激增,操作人员是否大规模增加?
是否在业务上发生过重大事项或重要变更,当时的性能如何?
用户的操作习惯有没有改变,或者用户是否自定义了某些功能?
而对于技术人员,我们就要从技术角度来询问性能问题了,而且由于技术人员对系统了如指掌,可能会“无意识”地回避问题,我们应该有技巧地处理这类问题,例如可以这样来询问技术人员:
系统日志是否记录了缓慢信息,是否可以回放缓慢交易?
缓慢时系统的CPU、内存、I/O如何?
高峰期和低谷时业务并发数量、并发交易种类、连接池的数量、数据的连接数量如何?
最早接到用户投诉是什么时候,是如何处理的,优化后如何?
数据量的增长幅度如何,是否有历史数据处理策略?
系统是否有不稳定的情况,是否出现过宕机,是否产生过javacore文件?最后一次变更是何时,变更的内容是哪些,变更后是否出现过性能问题?操作系统、网络、存储、应用软件等环境是否发生过改变?
通过与技术人员和业务人员交流,我们可以对性能问题有一个整体认识,避免“管中窥豹,只见一斑”的偏见,更加有助于我们分析和定位问题。
(4)切
“切”是“四诊”的最后一个环节,也是最重要的环节,这个环节结束我们就要给出定论:问题出在什么地方,该如何处理等。Java的性能诊断也是类似的,“切”就要我们接触真实的系统数据,需要去看设计,看代码,看日志,看系统环境,然后是思考分析,最后给出结论。在这一环节中,需要注意两点:一是所有的非一手资料(如报告、非系统信息)都不是100%可信的,二是测试环境毕竟是测试环境,它只是证明假设的辅助工具,并不能证明方法或策略的正确性。
曾经遇到过这样一个案例,有一个24小时运行的高并发系统,从获得的资料上看,在出现偶发性的性能故障前系统没有做过任何变更,网络也没变更过,业务也没有过大的变动,业务人员的形容是“一夜之间系统就变慢了”,而且该问题在测试机上不能模拟重现。接到任务后,马上进行“望闻问”,都没有太大的收获。进入到“切”环节时,对大量的日志进行跟踪分析调试,最终锁定到了加密机上:加密机属于多个系统的共享资源,当排队加密数据时就有可能出现性能问题,最终的解决方案是增加一台加密机,于是系统性能恢复正常。
性能优化是一个漫长的工作,特别是对于偶发性的性能问题,不要期望找到“名医”立刻就能见效,这是不现实的,深入思考,寻根探源,最终必然能找到根源所在。中医上有一句话“病来如山倒,病去如抽丝”,系统诊断也应该这样一个过程,切忌急躁。
注意 性能诊断遵循“望闻问切”,不可过度急躁。