You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
13 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 12 | 九条性能测试的经验和教训:如何保证测试结果可靠且可重复?
你好,我是庄振运。
上一讲我们介绍了十几种常用的性能测试工具。我们知道,性能测试的一个关键是保证测试结果可靠、可重复,否则就没有意义。所以,我们今天来学习一下进行性能测试时,这方面的经验和教训。
根据以前做过的相关工作,我总结了九条这样的经验和教训。按照逻辑时间顺序,我将它们大体上分成三大类别,就是测试前的规划、测试中的变化和测试后的结果分析;每一类又有三条要点。
![](https://static001.geekbang.org/resource/image/ee/a5/eee41a2f022ed7c88ea2a31c5b4157a5.png)
## 测试规划
三大类别的第一类别是测试规划,我们先来说说测试规划时要注意的三条要点。
### 1.详细记录测试环境和测试过程
做每个性能测试时,测试的环境至关重要。这里的环境包括软件硬件、操作系统、测试的负载、使用的数据等等。
测试的环境不同,性能测试的结果可能会迥异。除了测试环境,其它几个因素比如测试的过程,包括步骤和配置的改变也有相似的重要性。所以,我们每次测试都要把测试环境和测试过程记录下来,为将来分析数据做参考。
这些测试环境信息包括什么呢?大体上是操作系统和程序的版本号,以及各种软件参数的设置等等。
记录测试环境的目的是为了以后的各种分析。比如我们如果发现两次测试结果不匹配,需要找到不匹配的原因,那么这些测试环境就是相当关键的信息。如果两次测试结果的不同是因为软件配置不同导致的,那么根据记录的测试环境信息,我们就很容易根因出来。
至于如何记录,我们可以去手工去记录,但最好是自动记录,因为手工记录既费时费力,又容易出错。如果我们知道常用的环境配置的路径,可以很方便的写个程序来自动记录。或者依靠能自动记录配置的软件也可以。
### 2.快速地复位测试环境
有时候性能测试需要重复进行多次,那么就需要在每一次测试后,能够有快速“复位”到初始测试环境的机制。这个复位机制越简单有效越好,最好能达到所谓“一键复位”的程度,从而最大限度地降低手工复位的工作量。
复位的具体内容和方式要根据性能测试的情况而定,这就要分析一下在每个测试过程中,哪些子系统的配置和参数被改动或者影响了。
虽然理论上说,我们只需要恢复那些会影响测试结果的配置和参数,但是由于系统的复杂性,有些乍看起来不会影响测试结果的参数,其实或多或少也会影响测试结果。所以一般来讲,我们会**把所有的配置和参数都恢复一下**。环境复位的具体方式包括重新拷贝文件、重置配置参数、清空各种缓存等。
举个具体例子,如果是测试一个存储系统的性能,比如硬盘的性能。如果测试过程中写入了大量数据到存储系统,那么测试完毕,恢复环境的时候,就需要删掉这些数据,以便使被测系统回到初始状态。
再举个类似的例子,如果是测试一个数据库的性能,那么测试过程可能发出了很多查询请求。因为数据库经常会缓存各种情况和结果,所以在恢复测试环境时,不要忘记把缓存也清空。
一个现代计算机系统中往往有很多种缓存比如数据库缓存、文件系统缓存、存储缓存比如SAN等。不同种类的缓存自然有不同的清空方式。对于数据库缓存在每个测试之前可以用命令行来刷新数据库缓存如果数据库不提供这样的命令则需要重新启动数据库。对于文件系统缓存一般需要重新启动服务器。对于SAN缓存可以考虑在测试期间减少甚至关闭缓存或者用大量的随机数据来“污染”缓存。
### 3.足够的负载请求和数据
性能测试需要流量负载以及相关的数据,我们需要特别注意保证它们的多样化和代表性。否则测试结果会严重失真。
当使用相同的测试数据进行重复测试时,如果负载请求不够大,那么各种缓存可能会严重影响结果。
如何识别缓存的影响呢?
各级缓存系统都有相应的统计指标和命令比如文件系统缓存和SAN缓存中的缓存命中率就可以通过统计信息中报告的延迟并且结合经验来识别。举例来说如果一个随机8KB或16KB数据的对硬盘的读写测量出的延迟不到1毫秒那就实在是“太快”了快得让人不敢相信可以肯定它是命中某种缓存了。
除了合理清空缓存外,更有效地方式是保证测试时间足够长、测试的负载请求足够多和数据足够多样化,从而最大限度地减少或者掩盖缓存等其他因素的影响。
## 测试进行
测试规划之后,我们就要关注测试中的变化了。
### 1.性能数据日志要适当输出
性能测试过程中也需要实时输出有关的性能数据和日志比如CPU使用率数据。这些数据对于测试完成后的分析至关重要。
输出的数据和日志最好保存起来,以方便后期处理/重新处理。比如执行过很多不同参数配置的测试之后,我们经常需要进行测试之间的比较,这时候就需要仔细检查以前输出的日志了。
输出数据的多少也需要注意,虽然我们希望尽量多地输出,但是也要意识到,太多的输出有时候会起反作用。
* 存储的开销:可以用压缩存储来解决。
* 数据处理时间的开销:注意压缩和解压缩也要花时间。
* 可能影响性能测试的结果:有时候因为某些原因,日志输出会影响被测系统的性能,比如往一个文件写入日志,可能会导致系统的暂停,从而影响测试的结果(这个我们下一讲会详细剖析)。
### 2.测试环境要稳定
性能测试的环境在测试进行过程中,以及重复测试时一定要保持稳定和一致,否则测试结果就不可靠或者不能重复。
比如一个测试进行中,背景流量的负载产生了剧烈变化,导致被测系统的延迟增加。假如这个背景流量变化不是预期产生在测试规划之内的,就会造成测试结果失真。
类似的,如果测试过程中有不可控的因素,造成每次重复测试结果都不同,那这样的测试就不可靠。无论测试结果好坏,都不能用来作出有用的结论。
一般的解决方案,是尽量在一个独立无干扰的环境中进行测试,加上每次测试都准确地恢复测试环境,就能最大限度地保证测试环境的稳定。
### 3.一次调一个参数的利弊
在性能调优测试中,就是通过实验来找出系统的最优配置。
对这种测试,我们经常听到的经验是,“一次只调一个参数,通过对比实验,就能知道这个参数的最佳值”。你觉得这个经验对吗?
我们先看这个经验的出发点。性能调优过程中有很多可调参数和配置,互相之间的影响不清楚,因此不宜对系统的各种参数进行随意的改动。应该以基本参考设置为基础,逐次根据实际测试结果进行优化,一次只对某个领域进行性能调优,并且每次只改动一个设置和参数,避免其他参数和相关因素的干扰。
这个经验有它的道理,但我觉得这样做既对也不对,你不能盲从,否则就会错过最优配置的机会。
我举个生活中的例子来说明。假设有一个装水的木桶,这个木桶由多块可调整高度的木条组成。假设这些木条的初始高度不一,我们的目的是找到一个木条高度组合,从而实现最大的装水量。这个问题看起来很简单,根据木桶定律,就是把每块木条都调到最高嘛。
![](https://static001.geekbang.org/resource/image/c9/e7/c91e6cbdd7b63b27a0d1f0bf9bd5ace7.png)
我们假设这个木桶是我们的被测系统,每块木条就是一个参数。再假设我们对木条之间的关系不清楚。如果一次只调整一个参数,然后实验测试装水量。因为木桶装水量取决于高度最低的木条,我们或许会得出结论——只有那块最低木条值得调,其他木条的高度都不重要。
这种结论的结果就是把最低木条调高,比如调到最高。那么这个所谓的“最优系统”,也就是整个木桶的装水量取决于新的最低木条,也就是原来木桶中高度次低的那个木条。
我们很容易看出,如果一次调整多个木条,那么我们就会很快地找出一个更优的系统,也就是一个装水更多的木桶。
## 结果分析
测试后的结果分析也是需要你关注的重点,三条经验如下:
### 1.根因分析要由易到难
如果在性能测试过程中需要查找性能瓶颈,查找的过程一定要由易到难逐步排查。因为参考我们学过的帕累托法则,从最明显的性能瓶颈来开始,往往可以事半功倍。
首先从最常见的几种资源和几个指标查起比如CPU使用率、存储IO繁忙度、内存大小、网络发送和接收速度等。
进一步的分析就可以针对不太明显的资源比如内存带宽缓存击中率线程加锁解锁等从而过渡到应用程序和系统的一些配置参数。这些配置参数包括应用服务器及中间件操作系统瓶颈数据库、WEB服务器的配置还有应用业务瓶颈比如SQL语句、数据库设计、业务逻辑、算法、数据等。
### 2.几种测试最好互相验证
各种性能测试工具和测试手段都有自己的局限性或缺陷,从而可能会造成测试结果出现偏差。所以,如果条件和时间允许,最好使用几种不同测试工具或手段,分别进行独立的进行测试,并将结果相互比较和验证。
如果几种测试比较后结果相似,那么皆大欢喜。
否则就需要进行深入比较分析,弄明白造成结果不同的原因。分析以后,如果能够清楚地了解根因并作出合理解释,那么很多时候就够了,可以止于此。最后形成结论时,只要稍加有针对性地说明就可以。
### 3.测试结果和生产环境比较
如果性能测试是在非生产环境中进行的,那么得出的测试结果或许会和生产环境大相径庭。如果我们测试的目的是尽量和生产环境一致,就需要仔细审查每个测试的环节,包括测试环境和测试流程。
假如非生产环境的测试结果和生产环境的测量不同,很多情况下是由于测试环境不同导致,尤其要注意的是网络环境的差异。
比如测试的环境是在局域网而真正的生产环境是无线网或者4G那么可以肯定这样的局域网测试没有什么意义。网络差异的影响经常会大大超过很多人的预期因为实际的生产环境还牵扯很多上层协议和背景流量比如HTTP协议。
假设4G用户在网络层或者链路层上相对局域网而言只是多了30毫秒的延迟。在Web服务器中等负载的情况下这几十毫秒的链路层延迟就可能导致应用层响应时间增加惊人的几十秒延迟。
对网络环境这点,最理想的解决方式当然是在生产环境进行测试。如果实现这点有困难,那么可以考虑使用一定的网络仿真,来模拟真实生产环境。
除了网络环境的影响真实客户的操作流程也可能造成测量结果的不同。比如一个真正的Web用户或者APP用户在访问我们的系统时往往会有思考或者阅读时间所以真实世界的用户不太会在1秒钟内发出背对背的好几个页面请求。对于这种情况的差异可以通过人为地添加停顿来模拟思考时间。
现在很多测试工具都支持这种思考时间的引入。比如使用JMeter可以使用高斯随机计时器Gaussian Random Timer来模拟现实世界中的用户以随机方式和页面进行交互。
## 总结
![](https://static001.geekbang.org/resource/image/eb/c0/eb79090925a67c93acc09ae2efed0cc0.png)
当年毛主席给彭德怀写过几句诗:
山高路远坑深,
大军纵横驰奔。
谁敢横刀立马?
唯我彭大将军。
打仗有很多坑,做性能工作也有很多坑。我们要把性能测试的工作做好,本身就不容易。山高路远都不在乎,但一定要注意其中的陷阱。
所以只有不断地学习别人总结的经验,吸取别人的教训,解决测试的各种挑战,才能得出可靠,可重复并且有意义的性能测试结果。
## 思考题
回想一下你做过的性能测试,有没有踩过今天介绍的几个坑?踩过之后,是怎么防止以后不再踩同样的坑的?对没有踩过的坑,你有没有方法避免以后中招呢?
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。