负载测试与压力测试:差异、指标和 k6 示例

  1. 首页
  2. 博客
  3. 软件测试

软件测试

负载测试与压力测试:差异、指标和 k6 示例

证明您的系统可以处理您期望的流量。另一个则找出它在哪里断裂以及如何恢复。了解何时运行每个测试、信任哪些数字以及如何计划给出真实答案的测试。

阅读约 9 分钟
负载测试与压力测试:差异、指标和 k6 示例

要点速览

  • 负载测试 检查系统是否在预期流量下达到其目标。
  • 压力测试推动过去的能力寻找突破点并研究恢复。
  • 工具通常是相同的。不同之处在于目标和流量模式。
  • 用 p95 和 p99 等百分位数来判断结果,因为平均值可能隐藏严重的问题。

您的应用程序可以与十个用户完美配合。促销日,一万块会发生什么? 负载测试与压力测试描述了两种不同的方法来回答这个问题,将它们混合起来会导致测试无法证明您认为它们所做的事情。

本指南解释了每一项显示的内容、相关测试类型以及如何读取结果。我们在 Python 3.13 上使用模拟数字运行了下面的小百分位数计算。 k6 脚本是说明性示例,我们没有执行,并且用户计数和目标是发明的,因此请用您自己的脚本替换它们。仅针对您拥有或有权测试的系统运行这些测试,因为大流量可能看起来像是攻击并导致真正的中断。

简短的回答

负载测试问:我们能否处理我们期望的流量,并在执行过程中达到我们的目标? 压力测试会问:在失败之前我们能承受多少,我们如何失败,以及我们恢复的速度有多快?

想想一座桥。负载测试驱动预期数量的卡车通过并确认一切正常。压力测试不断增加权重以了解其极限,以及是否首先发出警告。幸运的是,破坏软件比破坏桥梁的成本要低。

负载测试与压力测试并排

方面 负载测试 压力测试
主要问题 我们是否达到预期负载目标? 我们在哪里以及如何打破?
流量等级 正常至预期峰值 超出预期峰值
图案 爬升,保持稳定 不断增加直至失败
失败 失败的测试 预期结果
关键指标 响应时间、吞吐量、错误 断点、故障模式、恢复时间
典型时序 在每个主要版本或事件之前 系统负载稳定后
结果 通过或未达到目标 容量限制和弱点列表

什么是负载测试?

负载测试模拟您期望的实际流量,并测量系统的行为方式。它需要一个测试目标,例如:“在 500 个并发用户下,95% 的结账请求在 800 毫秒内完成,错误率低于 1%。”

这是 k6 脚本的样子。 k6 测试是用 JavaScript 编写的,thresholds 将您的目标转换为通过或失败结果:

import http from "k6/http";
import { sleep } from "k6";

export const options = {
  stages: [
    { duration: "5m", target: 500 },   // ramp up
    { duration: "20m", target: 500 },  // hold at expected peak
    { duration: "2m", target: 0 },     // ramp down
  ],
  thresholds: {
    http_req_duration: ["p(95)<800"],
    http_req_failed: ["rate<0.01"],
  },
};

export default function () {
  http.get("https://staging.example.com/products");
  sleep(1);   // think time, as real users pause
}

sleep很重要。在没有思考时间的情况下,每个虚拟用户都会不断地攻击服务器,产生的流量比相同数量的真实用户多得多。

什么是压力测试?

压力测试故意超出预期水平。你并不是想通过。您想要以下问题的答案:

  • 在什么水平上性能开始下降?
  • 哪个组件首先失败:数据库、应用程序服务器还是第三方调用?
  • 它会优雅地失败并出现明显的错误,还是会崩溃并丢失数据?
  • 当流量下降时它会自行恢复吗?
export const options = {
  stages: [
    { duration: "5m", target: 500 },    // expected peak
    { duration: "5m", target: 1000 },   // beyond it
    { duration: "5m", target: 2000 },   // further
    { duration: "5m", target: 4000 },   // likely to break
    { duration: "10m", target: 0 },     // recovery: does it come back?
  ],
};

最后的恢复阶段经常被遗忘,但它可能是最有价值的阶段。一个能够很好地处理故障但如果不手动重启就无法恢复的系统仍然意味着需要有人在半夜修复故障。理想情况下,在极端负载下,系统应以受控方式停止工作,返回明确的 503 响应,而不是超时。

秒杀、浸泡等类型

负载和压力测试属于更广泛的性能测试系列:

类型 图案 它发现了什么
烟雾(表演) 一些用户,简要介绍一下 脚本和系统是否正常工作
负载 预期高峰,举行 是否达到目标
压力 过去产能的增加 限制和故障模式
斯派克 突然跳跃,然后下降 自动缩放和队列行为
浸泡(耐力) 正常负载数小时 内存泄漏、连接耗尽
容量 逐渐增加 目标范围内的最大负载

浸泡测试可以发现短期测试从未发现的问题。一个小的内存泄漏可能在二十分钟内看起来无害,但只会在两天后导致服务瘫痪。峰值测试适合闪购等流量不会逐渐增加的活动。

重要的指标

公制 含义 留意
响应时间(百分位数) 请求需要多长时间 p95和p99上升
吞吐量 每秒处理的请求数 随着用户增加而扁平化
错误率 失败请求的百分比 任何突然上升
CPU和内存 服务器资源使用 饱和或稳定增长
数据库 查询慢,连接池使用 池耗尽,锁等待
队列深度 等待处理的工作 永不枯竭的增长

为什么平均值会产生误导

平均值隐藏了真实人们抱怨的缓慢请求。为了证明这一点,我们生成了 100 个模拟响应时间,其中 95 个响应时间很快(80 到 200 毫秒),5 个非常慢(2.5 到 4 秒),然后计算了常见的统计数据:

import math, random, statistics

random.seed(3)
times = [random.randint(80, 200) for _ in range(95)] \
      + [random.randint(2500, 4000) for _ in range(5)]

def pct(values, p):
    values = sorted(values)
    return values[math.ceil(p / 100 * len(values)) - 1]

print("avg", round(statistics.mean(times)), "p50", pct(times, 50),
      "p95", pct(times, 95), "p99", pct(times, 99), "max", max(times))

# avg 302 p50 150 p95 199 p99 3740 max 3896

仔细看看这些数字。平均 302 毫秒说明没有人。典型的请求大约需要 150 毫秒,而慢速请求则需要 3 秒以上。即使 p95 在 199 毫秒时看起来也很健康,只有 p99 揭示了问题。像 p95 这样的数字意味着 95% 的请求都这么快或更快。

百分之一的请求并不是罕见的事件。如果一个页面发出 50 个请求,则缓慢的 p99 会影响很大一部分页面浏览量。在对你来说重要的百分位上设定目标,然后一起考虑几个。

提示: 在图表中寻找“膝盖”。随着负载的增加,响应时间会在一段时间内保持平稳,然后突然攀升,而吞吐量则停止增长。这一点就是你的实际能力,它通常在任何事情真正崩溃之前就出现了。

工具

工具 脚本编写 因
k6 JavaScript 开发者友好,门槛适合CI
杰米特 图形用户界面和 XML 成熟,支持多种协议
加特林 Scala、Java、Kotlin 等 基于代码,高效
蝗虫 蟒蛇 简单、基于 Python 的用户行为

其中任何一个都可以运行负载和压力测试。选择适合您团队技能的功能,而不是追逐功能列表。要在管道中使用它,请参阅什么是 CI/CD。

如何计划测试

  1. 设定目标。 使用真实分析来查找峰值流量并定义目标。
  2. 选择旅程。 以真实的比例模拟真实的用户行为,例如浏览、搜索和结帐。仅点击主页并不能证明什么。
  3. 使用现实环境。 小型临时服务器无法预测生产。请注意任何差异。
  4. 准备数据。使用真实的卷,因为在 100 行上快速的查询可能会在 1000 万行上爬行。
  5. 警告人们。告诉您的运营团队和任何第三方,不要意外地对支付提供商进行负载测试。在适当的情况下模拟外部服务。
  6. 从小处开始。 首先运行一个小冒烟测试以确认您的脚本有效。
  7. 监控一切。如果没有服务器指标,您将看到那个它放慢了速度,但没有为什么.
  8. 重复。每次修复后再次运行它,并保留基线进行比较。

读取结果

症状 可能的原因
响应慢,但 CPU 低 等待数据库、锁或外部服务
数据库CPU高 缺少索引或查询效率低下
突然连接错误 达到连接池或文件限制
内存稳步攀升 泄漏或无界缓存
负载生成器CPU高 测试工具是瓶颈,而不是您的系统

数据库是常见的瓶颈,因此如果查询是问题所在,请阅读我们的 SQL 查询优化指南。表的最后一行是一个重要的陷阱:始终检查生成负载的机器的运行状况。

常见错误

  • 混淆用户与请求。 明确目标是指并发用户数还是每秒请求数。
  • 忽略思考时间。这会增加流量并产生误导性结果。
  • 测试太晚。 发布前一周,没有时间修复架构问题。
  • 使用单个端点。 真实流量混合读取、写入和搜索。
  • 缓存隐藏了一切。相同的重复请求可能只会测试您的缓存,因此请改变数据。
  • 测试一次。 每个版本的性能都会发生变化,因此请定期运行测试。

最后的话

运行负载测试以确认您可以处理预期的流量,并进行压力测试以了解无法处理时会发生什么情况。从小事做起:选择最重要的用户旅程,设定一个明确的目标,并在启用监控的情况下针对登台进行简短的测试。第一次运行通常会教会你一些东西,你可以从那里开始构建。

常见问题

负载测试和压力测试之间的主要区别是什么?

负载测试根据设定的目标检查预期流量下的行为。压力测试超出了找到断点以及系统如何恢复的能力。

我应该先运行负载测试还是压力测试?

首先进行负载测试。一旦系统在预期流量下达到其目标,就可以运行压力测试来探索其极限。应力结果对于不稳定的系统来说意义不大。

压力测试和峰值测试有什么区别?

压力测试通常会逐步提高负载直至失败。尖峰测试突然跳到高水平,看看系统如何应对突然的变化。

p95 响应时间是多少?

这意味着 95% 的请求在该时间或之后完成。它比一般用户更好地描述了大多数用户的体验。

我可以在生产环境中运行负载测试吗?

你可以,但要小心,并获得许可、监控和快速停止的方法。大多数团队从类似生产的临时环境开始,以降低真实用户的风险。

继续学习

与Stack Quora合作

需要测试、WordPress 或 SEO 方面的帮助?

我们帮助团队交付更好的软件并发展他们的网站。告诉我们您正在做什么,我们将建议明确的下一步。

继续阅读

更多指南 测试、人工智能和现代开发

喜欢这篇文章吗?选择一个主题并直接跳至我们最新的教程。

人工智能

人工智能幻觉解释:语言模型为何会产生幻觉以及如何减少它

人工智能幻觉解释:它们是什么,为什么语言模型产生它们,常见类型,如何通过基础和提示来减少它们,以及我们进行的简单检查。

阅读文章 →
DevOps

环境变量解释:.env 文件、秘密和配置正确完成

使用我们运行的 Python 示例解释环境变量:读取和设置它们、为什么每个值都是字符串、.env 文件、进程继承、秘密和验证。

阅读文章 →
软件开发

API 速率限制解释:算法、标头和处理 429 错误

API 速率限制通过我们运行的模拟进行了解释:令牌桶、固定窗口、滑动窗口、429 响应、速率限制标头、带回退的重试和测试限制。

阅读文章 →
测试自动化

Python 中的模拟:unittest.mock、补丁以及导致测试谎言的错误

使用unittest.mock 在Python 中进行模拟,经过测试:模拟和return_value、side_effect、修补使用名称的位置、spec 和autospec、模拟日期以及常见错误。

阅读文章 →
软件测试

代码覆盖率解释:行覆盖率与分支覆盖率以及 100% 的真正含义

用真实的coverage.py运行来解释代码覆盖率:行、分支和条件覆盖率,阅读报告,为什么100%覆盖率仍然会错过错误,以及合理的目标。

阅读文章 →
数据库与 SQL

SQL 中的 DELETE、TRUNCATE 和 DROP:差异、回滚以及何时使用它们

DELETE、TRUNCATE 和 DROP 通过经过测试的 SQL 进行了解释:各自删除什么、回滚、触发器、身份重置、速度,以及 PostgreSQL、MySQL、SQL Server 和 SQLite 有何不同。

阅读文章 →
数据库与 SQL

SQL 视图解释:创建、使用和避免陷阱(附示例)

SQL 视图通过我们运行的查询进行解释:创建视图、实时结果、隐藏列、性能、可更新和物化视图、破坏的依赖关系和最佳实践。

阅读文章 →
软件开发

TypeScript 接口与类型:真正的差异,使用编译器进行测试

TypeScript 接口与类型,使用真实编译器进行检查:两者都可以做什么,只有一个可以做什么,声明合并,扩展与交集以及要遵循的简单规则。

阅读文章 →
软件开发

Python *args 和 **kwargs 解释:灵活的函数和清晰的示例

Python *args 和 **kwargs 用我们运行的代码进行了解释:打包和解包、参数顺序、仅关键字和仅位置参数、装饰器和常见错误。

阅读文章 →
软件开发

Python Lambda 函数:语法、实际用途以及何时不使用它们

Python lambda 函数用我们运行的代码进行了解释:语法、使用键排序、映射和过滤器、闭包、后期绑定陷阱,以及何时普通 def 是更好的选择。

阅读文章 →
人工智能

什么是人工智能代理?它们是如何工作的,只需运行一个简单的代理循环

什么是人工智能代理?关于代理循环、工具、内存和护栏的简单指南,以及我们运行的一个小型 Python 循环、真正的风险以及何时简单的聊天机器人是更好的选择。

阅读文章 →
DevOps

监控与可观察性:真正的区别,包括日志、指标和跟踪

监控与可观察性解释:已知和未知的故障、日志、指标和跟踪、检查事件演练、黄金信号、OpenTelemetry 和有用的警报。

阅读文章 →
DevOps

Docker Volumes 与 Bind Mounts:以正确的方式保留容器数据

Docker 卷与绑定挂载解释:为什么容器数据消失、命名卷、绑定挂载、tmpfs、Compose 示例、备份、权限以及何时使用它们。

阅读文章 →
测试自动化

TDD 与 BDD:主要区别、以两种方式构建的一个功能以及何时使用每种方式

TDD 与 BDD 的解释是通过一种双向构建的折扣功能:使用我们运行的代码进行红绿重构、Given-When-Then 场景、工具、权衡以及何时使用每种方案。

阅读文章 →
软件测试

用户验收测试 (UAT):流程、清单和真实示例

用户验收测试解释:谁运行它、它与 QA 有何不同、七步流程、UAT 测试用例模板、清单、签核标准和常见错误。

阅读文章 →
数据库与 SQL

SQL NULL 值解释:IS NULL、COALESCE、NULLIF 和要避免的陷阱

通过我们运行的查询解释了 SQL NULL 值:三值逻辑、IS NULL、COALESCE、NULLIF,聚合、连接和排序如何处理 NULL,以及它悄悄引起的错误。

阅读文章 →
数据库与 SQL

SQL CASE WHEN 语句:语法、实例和常见错误

SQL CASE WHEN 解释了我们运行的查询:简单和搜索表单、条件顺序、SUM 透视、COUNT 技巧、自定义排序、更新和要避免的 NULL 陷阱。

阅读文章 →
软件开发

什么是智威汤逊?通过工作示例解释 JSON Web 令牌

什么是智威汤逊? JSON Web Tokens 解释:这三个部分,在我们运行的代码中构建和篡改的令牌,JWT 与会话,存储,刷新令牌和安全错误。

阅读文章 →
软件开发

JavaScript 事件循环解释:调用堆栈、微任务和计时器以及示例

JavaScript 事件循环用我们运行的代码进行了解释:调用堆栈、任务和微任务队列、为什么 setTimeout 0 不是立即执行、阻塞、饥饿和面试测验。

阅读文章 →
软件开发

Python 生成器和 Yield 解释:通过真实示例节省内存

Python 生成器和yield 用我们运行的代码进行了解释:它们如何暂停和恢复、测量内存节省、管道、无限序列、yield from、发送和常见陷阱。

阅读文章 →
人工智能

什么是嵌入?向量和余弦相似度简单解释

什么是嵌入?关于向量、余弦相似度和语义搜索的简单语言指南,其中包含我们运行的一个小示例、常见用途、选择模型和已知限制。

阅读文章 →
DevOps

蓝绿部署与金丝雀部署:差异、权衡以及如何选择

蓝绿部署与金丝雀部署解释:每种部署的工作原理、成本、风险和回滚、我们测试的稳定用户分桶、数据库迁移、滚动更新以及如何选择。

阅读文章 →
测试自动化

不稳定的测试:造成这些问题的原因以及如何彻底解决它们

我们运行的演示解释了不稳定的测试:主要原因、如何确认和诊断、正确的时间修复、共享状态和日期以及隔离过程。

阅读文章 →
软件测试

负载测试与压力测试:差异、指标和 k6 示例

负载测试与压力测试的解释:各自证明什么、峰值和浸泡测试、重要的指标、为什么平均值会误导、k6 个示例以及如何计划测试。

阅读文章 →
软件测试

测试用例与测试场景:主要区别、示例以及何时使用它们

测试用例与测试场景的解释:清晰的定义、并排的表格、从场景到案例的密码重置功能、模板以及何时使用每个功能。

阅读文章 →
数据库与 SQL

SQL 查询优化:读取执行计划并修复慢速查询(带计时)

对一百万行进行 SQL 查询优化测试:读取执行计划、添加正确的索引、修复不可控制的过滤器、使用复合索引和键集分页。

阅读文章 →
数据库与 SQL

SQL UNION 与 UNION ALL:差异、示例和性能测试

SQL UNION 与 UNION ALL 通过我们运行的查询进行了解释:重复、都遵循的规则、真实的数据错误、测量的性能、INTERSECT 和 EXCEPT 以及何时使用它们。

阅读文章 →
软件开发

JavaScript Fetch API 教程:GET、POST、错误处理和超时

针对本地服务器测试的 JavaScript Fetch API 教程:GET 和 POST、为什么不抛出 404、可重用的帮助程序、查询字符串、超时、CORS 和常见错误。

阅读文章 →
软件开发

Python 文件处理:读取、写入、CSV、JSON 和 pathlib 以及示例

使用我们运行的代码解释了 Python 文件处理:打开模式、使用、读取大文件、CSV、JSON、pathlib、二进制文件、安全写入、错误处理和常见错误。

阅读文章 →
软件开发

Python 类和对象:带有实际示例的 OOP 教程

用我们运行的代码解释 Python 类和对象:__init__、方法、类与实例属性、继承、属性、dunder 方法、数据类和常见错误。

阅读文章 →
人工智能

什么是大语言模型?法学硕士如何运作,在没有炒作的情况下进行解释

什么是大语言模型?法学硕士用简单的语言解释:标记、嵌入、注意力、训练、温度和上下文窗口、一个微小的下一个单词演示和真正的限制。

阅读文章 →
DevOps

适合初学者的 Linux 命令:带有示例的基本终端指南

适合初学者的 Linux 命令,按任务分组:导航、文件、查看日志、grep、管道、权限、进程和档案,并附有实用的日志分析示例。

阅读文章 →
测试自动化

Selenium 与 Playwright:主要区别、代码比较以及选择

Selenium 与 Playwright 比较:架构、等待、浏览器和语言支持、调试、两种工具中的相同测试、迁移建议以及如何选择。

阅读文章 →
软件测试

等价划分:通过实例解释测试设计技术

等效分区解释:如何找到有效和无效分区,年龄、密码和优惠券的工作示例,我们运行的测试,以及为什么将其与边界配对。

阅读文章 →
软件测试

软件测试中的严重性与优先级:差异、矩阵和实例

软件测试中的严重性与优先级解释:清晰的定义、带有示例的四向矩阵、级别尺度、谁设置每个级别以及如何运行错误分类。

阅读文章 →
数据库与 SQL

SQL 子查询教程:类型、相关子查询和 EXISTS 以及示例

SQL 子查询教程,其中包含我们运行的查询:标量、IN、FROM 和 SELECT 子查询、相关子查询、EXISTS、NOT IN NULL 陷阱以及子查询与 JOIN 与 CTE。

阅读文章 →
数据库与 SQL

SQL 事务和 ACID 解释:COMMIT、ROLLBACK 和隔离级别

SQL 事务和 ACID 通过我们运行的银行转账进行了解释:COMMIT、ROLLBACK、保存点、锁、隔离级别、脏读、死锁和最佳实践。

阅读文章 →
软件开发

Git merge 与 rebase 的区别:示例及如何选择

用真实的 log 输出、对比表、rebase 黄金法则和安全的团队工作流,讲清 Git merge 与 rebase 的区别。

阅读文章 →
软件开发

Python 列表推导式:语法、示例与何时避免使用

Python 列表推导式详解:语法、过滤、if-else、嵌套循环、实测耗时,以及何时普通 for 循环更合适。

阅读文章 →
数据库与 SQL

SQL JOIN 详解:附示例(INNER、LEFT、RIGHT、FULL)

通过示例表和查询,一步步讲解 SQL JOIN:INNER、LEFT、RIGHT、FULL、CROSS 和 SELF,并介绍常见错误与性能技巧。

阅读文章 →