正文
在去年的一次面试中,我被问及性能优化方面的问题。对方问,“你在性能优化方面有哪些经验?”。我感到问题笼统,有些无从下手,于是简单地回答道:“找到程序性能的瓶颈位置,进行针对性的优化,比如为数据库查询效率低的地方适当添加索引等……”。对方的表情告诉我,这个答案不令他满意。
那时的我并不觉得自己说错,且面试最终通过,不过对方的一瞬间的不快表情还是给我留下了深刻印象。时至今日,在经过一些学习和工作后,我不得不承认自己当时的回答是肤浅的。今天写下这篇文章,结合最近的学习和工作,记录下自己对这个问题的的一些新的认识。本文不涉及具体的性能优化技术,只有一些思考问题的思路,以及面试方面的反思。
本文的标题是“从系统角度考虑性能优化”,系统指的是一些模块和它们之间的关系的结合。模块的形式多种多样,可以是类、子系统、或服务等。
系统的价值在于它可以提供功能,比如一家手机公司的售后服务管理系统可以提供查询手机保修信息的功能、创建手机维修订单的功能。
性能,则是指系统执行功能的好坏程度。查询1台手机的保修信息需要花多久?它是查询功能的性能问题。
定义系统范围
在给出了性能的定义后,我们又面临着一个新的问题:谁的性能需要优化?
已知性能是功能的属性,功能来自于系统,那么要回答上面的问题,则首先要找到性能对应的功能,以及功能涉及的系统范围。
以上文提到的售后服务管理软件为例,
功能:查询手机的保修信息。
性能:查询效率。在定义了这样的系统范围之后,对于性能优化问题,我们便可以从系统的结构出发,进而思考一些问题,比如:
- 从表示层到业务逻辑层是否存在较大网络延迟?
- 业务逻辑层是否存在效率低下的代码?
- 数据库服务器负载如何?
- 数据库查询的执行计划是否正常?
- 需要用哪些工具/手段调查以上问题?
- 哪些相关人能负责调查系统的各个部分?
- ……
但是,这样的系统通常是给企业内部用户使用的,如果面临查询保修信息性能问题的人是一个外部用户,性能所涉及的系统的可能已经有变化,系统可能是如下模样,
图2 售后管理系统、中间件和外部应用组成的新系统
如图2所示,虚线框代表一个单一系统的边界,边界间的连线代表了系统间的接口。3个单独的系统联合起来组成了了一个新的复合系统。
外部用户使用外部应用查询保修信息,外部应用通过接口连接中间件系统,中间件再通过接口连接原本的售后管理系统的业务逻辑层,以获取保修信息。
此时,系统的结构已与图1中的结构不同,如果还按照图1下的思考方式排查性能问题,就有可能无法找到正确的问题所在。
比如,如果查询效率问题来自于中间件的吞吐量不足,那么无论怎样在售后管理系统进行分析、优化,恐怕也很难解决问题。
有的问题可能已经过时,比如,
- 从表示层到业务逻辑层是否存在较大网络延迟?(在新的系统结构下,用户并没有通过售后管理系统的表示层进行查询,因此该处网络延迟不会对性能有影响)
有些新的问题会产生,比如,
- 中间件的吞吐能力如何,有什么限制?
- 系统间是同步处理还是异步处理?
- 系统间的网络延迟如何?
- 接口的性能如何?
- 系统间的容错机制是什么?
- ……
所以,当我们希望做性能优化时,必须清晰地定义出相关的系统范围,对范围内的功能和结构做分析,才能可靠地完成工作。
这是我在面试中犯的第二个错误:没有考虑性能优化的问题背景,想当然地理解成单个程序的性能优化问题。在沟通中,不仅要理解双方对问题的具体概念的定义,也应充分理解问题的上下文。不然很容易陷入东拉西扯找不到重点的情况。在性能优化的问题中,系统范围和系统结构属于上下文。
重定义功能
既然性能是系统执行功能的好坏程度,那性能的好坏与功能本身的定义是有着密切联系的。比如,如果功能是在查询后同步返回一份保修信息报告,那么在查询发起一小时后返回这份报告通常是让人难以忍受的。但如果功能的定义变成:申请生成一份保修信息报告,并允许用户在24小时后查询。在这种新的功能定义下,程序花费1小时来准备这份报告似乎变得毫无问题。听起来这是一种文字游戏,但现实中的确有类似的做法,比如人行征信的报告
图3 人行个人征信报告申请界面
上面的例子可能过于夸张,但是在现实中,通过对系统功能进行一定调整以改善性能的做法往往是可行的。比如《Designing Data-Intensive Applications》中详细描述的派生数据系统,通过将一个不可变的变更日志作为源,异步地将数据更新到其它系统,以提供良好的可靠性和可伸缩性,并改善应用的进化能力。如果我们从一个派生数据系统中进行查询操作,并且将功能定义为“查询五分钟前的信息状态”而非查询实时的信息状态,得到的结果有可能是稍稍过时的,但是在查询的响应时间方面会有很大的提升空间。
通常来说,人们需要对业务和技术有着深入的理解,才能提出好的功能重定义方案。
与这段内容相关的是我在面试中可能犯下的第三个错误:没有延伸问题,把回答拘泥在提问的字面意思之内。就事论事当然是一种美德,但也不应忘记沟通中常见的XY问题:一个人想解决问题X,但是他不直接就问题X提问,而是询问如何解决问题Y,因为他相信问题Y能帮助他解决问题X。对于面试来说,面试官提问“性能优化”不代表他只想听到性能优化的具体技巧方面的回答,面试官可能只是希望找一个切入点来开展谈话。在真实的开发工作中,业务人员提出的性能问题,其背后可能存在另外的问题,也许可以通过功能的调整来改进。
关注价值
价值是有成本的利益,它通过系统与外界的交互(位于系统边界的接口)而体现。
在图2中,左侧的售后服务管理系统的查询功能的价值是通过其与中间件的接口来体现的,而由3个系统组成的复合系统的价值是通过系统提供给用户的查询功能而体现的,如下图,
图4 系统价值通过边界的接口体现
在进行性能优化的同时,也不要忘记把成本计算在内。
- 如果使用raid 0提高了磁盘读写性能,那么额外多出的硬盘就是成本。
- 如果使用一种空间换时间的算法减少程序的运行时间,那增加的内存占用就是成本。
- 如果程序员要花5天的时间改进一项功能的性能,那5人天就是成本。
- ……
如果程序员为一个一次性使用的程序花费了5天时间来进行性能优化,最终节约了用户3天的时间,那么这种性能优化工作有价值吗?答案是没有的,因为它的收益低于成本。
系统的价值取决于观察者的主观判断,而用户的观察和开发人员的观察可能是不同的。开发人员可能更关注技术上的进步,从这个角度来看,程序节约了3天的运行时间是一种成功。但从用户的角度看,性能优化的工作导致自己不得不等待5天,相比不优化使其损失了2天的时间,反而带来了麻烦。总结
这篇文章的标题包含【被面试官吊打】,原因正是开篇写到的面试经历。我想,被吊打并不可耻,只要不断反思、改善自己,那么被吊打的经历也会成为自己成长的助益。
本文的很多概念来自于《系统架构》,这是一本介绍系统思维、系统分析和设计的书,不是软件架构书。
本文中的图片1, 2, 4是使用draw.io绘制的,图3来自网络。