性能、负载、压力测试——从性能测试角度理解系统开发
引言
最近,由于旧机器下线,我对过去部署的一些服务做了迁移,顺带对新部署的服务做了一个简单的性能测试。在实施过程中,我发现自己对很多性能指标的理解很不清晰,对于并发数、压力、吞吐量、延迟等概念,通常是以望文生义的方式使用。对于系统应该关注什么样的性能指标,认识也不完整。为此,我阅读了wiki百科以及一些博客,希望能对这方面的概念有一个较完整的认识。
跑个题,最近重新提到了工程能力。这个词包含的范围很广,从技术路线选择、系统可维护性,到代码可读性、系统性能优化等,凡是能想到的有利于实现功能、提高效率的实践,似乎都可以被归为这个能力。然而,可以确定的是,工程能力的很大一部分体现,都与性能指标及其优化有关。如果在实现一个服务或系统时,对性能指标没有足够的关注,对性能相关的问题没有胸有成竹的回答,则很难说是有足够的工程能力。
本文的主要内容是对性能测试做了简要总结。通过阅读维基百科及一些博客,了解性能测试的内容、指标、方法、目标。对这些文章做了一些摘要、总结、翻译。
本文分为以下几部分:第二部分,是性能测试的分类,主要内容来自维基百科;第三部分,是如何实施性能测试,主要注意哪些问题,以及优化的方向,内容来自维基百科以及Grig Gheorghiu的两篇博客;第四部分,是如何区分性能测试、负载测试、压力测试,内容主要来自Grig Gheorghiu的两篇博客。
性能测试分类
在维基百科上性能测试的定义是
In software engineering, performance testing is in general, a testing practice performed to determine how a system performs in terms of responsiveness and stability under a particular workload. It can also serve to investigate, measure, validate or verify other quality attributes of the system, such as scalability, reliability and resource usage.
第一层含义是判断在一定工作负载下,系统响应能力和稳定性。进一步延伸,包括测量、验证系统的其他质量属性,如可扩展性、可靠性、资源消耗。
性能测试可以包括但不限于以下类型:
- 负载测试(load testing)
负载测试的目的,是为了解在预期的工作负载下,系统的行为。- 压力测试(stress testing)
压力测试是为了解系统提供服务能力的上限,了解系统在超出预期的压力下是否足够稳定。使我们了解如果当前负载增加至超出设计范围,系统是否能承受。- 渗透测试(soak testing)
渗透测试也叫忍耐力(endurance)测试,了解系统能否较长时间持续支撑的某个负载量,是否在持续的负载下会导致性能下降。- 尖峰测试(spike testing)
尖峰测试是突然增加或降低负载,看系统在负载量突然变化时的行为。- 断点测试(breakpoint testing)
断点测试与压力测试很相似,持续增加系统的负载,观察系统是否在预期的负载压力下出现性能下降或失败。- 配置测试(configuration testing)
观察修改系统配置,会对系统行为造成怎样的影响,例如负载均衡的配置。- 隔离测试(isolate testing)
隔离测试并非一个独立的测试项目,而是通过不断地重复某种测试,将出现问题的部分隔离出来。- 互联网测试(internet testing)
互联网测试通常是对全球性的服务来说的。测试负载的生成者,来自实际的目标用户所在地。
上面的分类摘自维基百科。可以看出,不同类型测试有重叠甚至重复的部分,但每种测试又有其各自的目的。负载测试(load testing)和渗透测试(soak testing)可以同时进行。压力测试(stress testing)、尖峰测试(spike testing)和断点测试(breakpoint testing)似乎也可以同时进行,只要针对不同的目的做好对应的记录。如果出现问题,所有测试的目的最终都是将问题区域隔离出来(isolate testing)以进行优化。
如何实施性能测试
性能测试的目的和性能目标
首先需要明确的一个问题是“why are we performance-testing?”。从目的来看,性能测试可以用来:
- 验证系统达到预期的性能要求;
- 对比两个系统,看哪个系统性能更好;
- 找出系统的瓶颈以便优化
不同的系统有不同的性能目标,但是通常都包含以下几个:
- 并发量。对于有用户登录状态的系统,同时服务的用户数通常是一个考量指标,因此并发量是一个重要性能目标。
- 吞吐量。对于无用户登录状态,吞吐量或者transaction rate是其关注的指标。
- 响应时间。从服务器端看,是从系统接收到请求,到系统发出反馈的时间。或者从客户端机器看,从客户端发出请求,到客户端收到响应的时间,压测脚本通常只能测到这个时间。服务器端的时间,需要通过日志等方式记录。
性能规格
性能测试之前应该有一个明确的性能规格说明,并且落地成文档。理想情况下,这个性能规格应该在需求阶段就制定下来,并且应该先于任何系统设计。
性能规格通常应该至少回答以下问题:
- 性能测试的范围是什么?哪些子系统,哪些接口,哪些组件要被测试?要测试它们的哪些性能目标?
- 对于有用户接口的系统,用户并发量是多少?峰值和均值预期是多少?
- 目标系统是什么样子?包括系统环境、网络配置、硬件配置?
- 每个会话中各环节的负载比例是如何的?(登录20%,搜索40%,登出10%,选择30%)
- 系统中各会话的负载比例是如何的?(A回话20%,B会话40%,...)
- 后台批处理的时间要求什么?
前提条件
性能测试需要满足的条件包括:
- 系统稳定,或者待测的接口稳定。因为性能测试的目的不是发现bug,而是发现瓶颈。
- 目标明确。响应时间、并发、吞吐量?
尽可能满足的条件是:
- 与系统使用真实环境保持一致
性能测试应该尽可能早地开展,因为越早发现瓶颈,修复的代价越小。
测试工具
分为两部分:
- 一部分是用于模拟请求的performance scripting,包括HP LoadRunner, NeoLoad, Apache JMeter, Rational Performance Tester, Silk Performer和Gatling;
- 一部分是用于监控系统状态的monitor,包括系统本身的日志,以及监控CPU、内存、磁盘、网络的使用情况。
实施与优化
当目标、环境和工具已经就绪后,可以开始启动客户端模拟。对于一般的性能测试,目的是发现性能瓶颈,通常的做法是逐渐增加负载,记录并观察各项指标的变化。
当发现系统的性能指标达不到预期时,以web服务为例,通常的优化思路是:
- 首先需要优化的是应用程序和数据库。代码逻辑是否足够高效?数据库的查询是否高效?数据库是否针对常用查询语句做了优化?可以用的工具有jUnitPerf和pyUnitPerf。
如果上述方案还无法满足,可以考虑以下思路:
- 增加Cache是否能提升效率;
- 对常用页面静态化;
- 考虑使用负载均衡,对系统进行横向扩展;
- 将服务划分为只读/读写;
- 提升机器的硬件性能,SSD、CPU、内存等;
- 提升网络带宽。
性能、负载、压力测试的区别
这一步分的内容完全来自于Grig Gheorghiu的博客,可以在原文看到更详细的解释。
性能测试(performance testing)、负载测试(load testing)、压力测试(stress testing)三个概念经常被替换着使用,因为他们在实施方式上有相同的地方,但他们也确实有一些区别。在本文第一部分性能测试分类中,已经大致介绍了概念上的区别,主要体现在目的不同。
- 性能测试。目的是发现系统瓶颈,并为系统设定一个性能baseline,以便为后续的优化设定一个参照。通常是白盒测试。
- 负载测试。目的是确认系统是否满足某负载要求,以及发现在高负载下是否会出现低负载无法出现的bug。通常是黑盒测试。
- 压力测试。目的是看系统在异常环境,如压力过大、网络断开、磁盘故障、资源不足等情况下的表现,检查系统的稳定性、对异常的恢复能力。
其实只要测试的目标明确,具体使用哪个名字并无太大关系。