什么是性能分析
\“性能分析”一词有许多种定义,但在我看来最有用的一个是:
\\\一种由测量驱动的方法,用以了解一个应用程序在负载下的行为。
\
这个定义的好处是,它提醒您注意测量是整个过程的关键点。并通过简单的延伸,也提醒您统计和数据分析可能是性能工程师的重要工具。
\进一步讲,它使我们更相信应把性能分析看作是一项基础的实证研究活动,是它把输入和输出粘合在一起组成实验科学。
\这样,这些输出就可以被框定为一系列具有量化答案的问题,比如:
\\\如果有10倍的客户数,系统还有足够的内存来应付吗?
\在客户看来,应用程序的平均响应时间是多少?
\(这个响应时间)在其余部分的分布是什么样子的?
\与我们的竞争对手相比如何?
\
在这种形式中,使用这些最佳实践所表达出的性能更具有科学性,而不是艺术性;更是一种从根本上可以量化的行为,并且与业务活动有着直接的关系。
\然而,尽管有这些属性,性能指标却常常屈居于一个尴尬的状态,知名的最佳实践滞后于从业者的现实处境。
\有一些不同的模型可以佐证这一点,可能其中很有趣的一个就来自Carey Flichel的精湛之作《为什么开发者总是选昏招(Why Developers Keep Making Bad Technology Choices)》。
\在文章中,Carey特别提出了导致开发者做出错误选择的五大因素:
\- 乏味;\
- 简历加料;\
- 同侪压力;\
- 缺乏了解现有系统;\
- 被误读的,或者根本不存在的问题。\
在本篇文章中,我们提出一些在企业平台上最常见的性能分析反模式,并尝试使用Carey所列举的几种基本因素进行阐述。获得以下的结论所使用到的具体例子是从Java生态系统抽取来的,但类似的描述也适用于许多其他类型的企业系统。
\每一项基本因素都对应于一些常见的认知偏差。例如,乏味和简历加料都源于对现有技术的逃离——这些技术是开发人员在他的日常工作中整天使用的——以及他们对美好明天的雄心壮志。
\以下就列举这些反模式,这里使用了一种向“四人组(Gang of Four)”,当然也包括反模式的开创者Brown等前辈致敬的风格和格式。
\反模式目录
\只见光鲜亮丽
\描述
\最新最酷的技术往往是第一指向目标。
\典型意见
\\\万事开头难,我们需要颠覆性的开始。
\
真实情况
\- 这只是在黑暗中的灵光一闪;\
- 开发者并不真正了解新技术。\
根本原因
\- 乏味;\
- 简历加料。\
探讨
\这种反模式在年轻的团队中最常见。急于证明自己,或者尽力避免绑在他们眼中的“遗留”系统上,他们往往倡导新的、“热”的技术。也可能巧合的是,这类技术又常常在跳槽时能为他们带来更高的薪水。
\因此,面对任何性能问题时,合乎逻辑的潜意识结论就是先来看看新的技术。毕竟,它还没有为人熟知,所以一双有创见的眼睛总是有益的,不是吗?
\解决方法
\- 测量,并去找出真正的瓶颈;\
- 确保围绕新的组件有足够的日志记录。\
只见简单事物
\描述
\首先把系统中最简单的部分列为目标。
\典型意见
\\\让我们从我们了解的部分开始吧。
\
真实情况
\- 开发人员懂得怎么为这部分(可能只是这部分?)系统调优。\
根本原因
\- 缺乏对现有系统的理解。\
探讨
\作为“只见光鲜亮丽”反模式的对偶,这种反模式经常出现在旧的、更成熟的团队,这类团队常常是个维护者、保持灯亮着的角色。如果他们的应用程序最近被合并到或搭配了新技术,团队可能会感到恐惧或不愿意参与到新系统中去。
\在这种情况下,开发人员可能会感觉只分析那些熟悉的系统组件会更令人舒服,希望他们可以在不离开他们的舒适区的条件下达到预期目标。
\特别值得注意的是,这前两个反模式都出自于一种对于未知的反应。在“只见光鲜亮丽”中,这表现为一种开发者(或团队)希望学习更多并获得优势的愿望,基本上是一个进攻型策略。相反的,“只见简单事物”是一种防御型策略,只搞熟悉的部分,而避开有潜在威胁性的新技术。
\解决方法
\- 测量,以确定真正的瓶颈;\
- 从领域专家那里寻求帮助,判断问题是否出自一个不常为人所知的组件。\
我的桌面就是用户验收测试(UAT)环境
\描述
\用户验收测试环境与生产环境之间有显著差异。
\典型意见
\\\一个全尺寸的用户验收测试环境过于昂贵了。
\
真实情况
\- 由于不同环境之间的差异最终造成的服务中断损失,几乎总是贵过多买几台机器。\
根本原因
\- 被误读的,或者根本不存在的问题。\
探讨
\我的桌面就是用户验收测试环境来自于一种不同类型的认知偏差,我们以前从未见过。这种认知偏差固执的认为执行一些用户验收测试总比什么都不做要好。不幸的是,这种满心希冀从根本上误解了企业环境的复杂本质。为了使任何一种有意义的推断具有合理性,用户验收测试环境都必须与生产环境等同。
\在现代的适应性环境中,运行时子系统会善用可用资源。如果这些资源与目标部署环境截然不同,它们将根据不同的情况做出不同的决策,使得我们做出的漂亮的推断完全失去意义。
\解决方法
\- 跟踪服务中断的成本,以及与所流失客户相关的机会成本;\
- 投资搭建一个与生产环境一致的用户验收测试环境;\
- 在大多数情况下,上述第一项成本远远大于第二项。\
模拟生产数据难以编造
\描述
\在用户验收测试中使用的数据看起来一点也不像生产系统中的。
\典型意见
\\\保持生产系统与用户验收测试系统的同步太难了。
\
真实情况
\- 在用户验收测试中使用的数据必须近似于生产系统,这样才能得到准确结果。\
根本原因
\- 缺乏对现有系统的理解。\
探讨
\这种反模式也落入了“聊胜于无”的陷阱。这个想法就是,即使使用过期的和并不具代表性的数据测试都比不测试好。
\与前面的一样,这是一个极其危险的推理过程。在系统测试中,虽然使用一些东西(即使它并不像生产数据)执行一定规模的测试,总能揭示出一些缺陷和疏漏,但它却带来了安全的错觉。
\到系统投产时,实际使用模式与由用户验收测试数据确定的预期基准模型风马牛不相及,开发和运维团队就会被好好的上一课,他们已经被用户验收测试所带来的温暖光芒给照的洋洋得意,在一次大规模生产系统发布后,毫无准备的直面这种全然恐怖。
\解决方法
\- 咨询数据领域的专家,并投资建立一个把生产数据迁移回用户验收测试环境的流程;\
- 对大规模投产做好万全准备;\
- 只要有可能,建立专门的“最坏情况”小组或工具(比如捣乱猴)。\
性能秘诀(又名,按民间传说调优)
\描述
\盲目应用代码和参数变更。
\典型意见
\\\我在Stack Overflow网站上找到了这些很棒的秘诀。一切都变了。
\
真实情况
\- 开发者并未理解上下文,或者所谓性能秘诀的基础,真正的影响是未知的。\
根本原因
\- 缺乏对现有系统的理解;\
- 同侪压力。\
探讨
\性能秘诀是对已知问题的权变措施,从根本上说就是一个寻找问题出处的过程。它们会有一个有效期,但通常标识日期错的很离谱。在软件或平台未来的发布版本中,最终会有人证实这条秘诀根本不灵(即使在最好的情况下)。
\性能建议的一个特别糟糕的来源就是管理员手册。手册中写的常是缺乏上下文的一般性建议。这种建议以及所谓的“推荐配置”往往只是律师所坚持的,如果供应商被起诉,作为辩护的另一条理据。
\Java的性能在一个特定的上下文中才有意义,具有大量的影响因素。如果我们脱离那个上下文,由于执行环境的复杂性,几乎就不可能推理得到结论。
\解决方法
\- 只使用那些经过良好测试和充分理解的技术,它们才是直接关系到一个系统的最重要的方面的。\
替罪羊
\描述
\某些组件总被认定为问题所在。
\典型意见
\\\肯定是JMS,或者Hibernate,或者另一个什么库。
\
真实情况
\- 得出这个结论前的分析不足。\
根本原因
\- 同侪压力;\
- 被误读的,或者根本不存在的问题。\
探讨
\这种反模式通常是管理人员或业务人员所犯的。在许多情况下,他们对整个技术栈没有充分的了解,就通过经验主义或者未确认的认知偏差进一步得出结论。当然,技术人员对此反模式也远未达到免疫。
\解决方法
\- 顶住急于下结论的压力;\
- 进行正常的分析;\
- 把分析结果传达给所有利益相关者(以尽力展现导致问题的更真实的原因的图景)。\
拨弄开关
\描述
\团队变得痴迷于JVM的参数开关。
\典型意见
\\\我只要修改这些设置,我们就会得到更好的性能。
\
真实情况
\- 团队并未理解变更带来的影响。\
根本原因
\- 缺乏对现有系统的理解;\
- 被误读的,或者根本不存在的问题。\
探讨
\JVM有上百个参数开关,这为我们提供了一个高度可配置的运行时环境,但也诱惑我们想要利用所有的配置项。这通常是错误的,JVM的默认值和自我管理能力一般都足够了。有些参数开关之间有意想不到的组合关系,盲目调整甚至是很危险的。
\解决方法
\在应用任何参数开关的变更之前:
\- 在生产系统中进行测量;\
- 在用户验收测试时,每次只改变1个参数开关;\
- 在用户验收测试时,测试发生的变化;\
- 在用户验收测试时,重复测试;\
- 安排人手重新校验你的推断。\
“微”基准测试
\描述
\只在系统的一些非常细小的方面努力调优。
\典型意见
\\\看看我们是不是能加快方法分派时间……
\
真实情况
\微小变更对整个系统层面的影响是完全未知的。
\根本原因
\- 缺乏对现有系统的理解;\
- 被误读的,或者根本不存在的问题;\
- 简历加料;\
- 同侪压力。\
探讨
\性能调优是一项统计性的活动,它依赖于一个非常具体的执行环境。这意味着,大型系统通常比小型系统容易进行基准测试,因为在大型系统中,大数定律会按照工程师的期望发挥作用,帮助纠正在平台中扭曲个别事件的影响。
\相对应的,在复杂环境中,越是需要聚焦于系统的一个方面,就越需加倍努力解耦组成平台的那些独立的子系统(例如线程、垃圾回收、调度,JIT编译等),至少在Java和C#环境中如此。这是非常难做到的,对统计数字的处理容易受到外部因素影响,而且软件工程师们在成长的道路上通常学不到这种技能。这就容易造成一种结果,工程师们本以为正在对某个系统层面进行基准测试,所得到的数字却并不能准确的反映那个系统层面的行为。
\有一个不幸的倾向,就是这还会与人的认知偏差结合起来,使人们误以为找到了一些本不存在的模式。总之,这些效应把我们带往障碍之路,一个性能工程师被不良统计数字或者不当控制深深引诱,还热切地宣称要达成一个同侪无法企及的性能基准或者效果。
\解决方法
\- 不要做“微”基准测试,除非你有一个已知的关于它的现实用例。如若要做,就做得公开,和你的同侪一起完成;\
- 做好自己犯了很多错误的准备,反复自省。\
结语
\为什么在性能调优中会出现这些反模式?在调优的过程中,是什么激励了认知偏差,从而导致这种不正确的结论?
\回答这些问题的关键之处,是理解软件工程与其他工程学科有着本质不同。在广阔的机械工程系统领域,我们可以很好的理解小零件的物理属性,而复合效应只会带来少量的(而且常常是被充分了解的)意外表现。
\软件就不同了。我们所开发的系统远比人类在其他领域的成果更称得上精工巧琢。这既是因为我们使用非常简单的基础零件,而且还因为我们制造了工具,使我们可以驾驭为数浩繁的零件。不幸的是(或者迷人的是,看你怎么看了),随着软件已经变得越来越复杂,我们发现它有高度的突生特性。意思也就是说,随着复杂度增加,始料未及的现象已经显现。如我们在本文中所讨论的一样,这其中并不都是正面的。
\致谢
\特别感谢Martijn Verburg、Kirk Pepperdine、Trisha Gee和James Gough(以及更多人),他们向我解释(有些时候是命名)这些反模式。
\关于作者
\Ben Evans是jClarity的首席执行官,jClarity是一个Java/JVM性能分析的初创企业。在业余时间,他是伦敦Java社区的领导者之一,还在JCP执行委员会拥有一个席位。他做过的项目包括谷歌IPO的性能测试、几个金融交易系统,以及为一些90年代的著名电影编写的备受赞誉的网站等等。
查看英文原文:Modern Enterprise Performance Analysis Antipatterns
\感谢臧秀涛对本文的审校。
\给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ)或者腾讯微博(@InfoQ)关注我们,并与我们的编辑和其他读者朋友交流。