前言
前面 16 篇覆盖了功能测试的三个维度:Web UI 自动化验证页面功能,接口自动化验证后端逻辑,App 自动化验证移动端交互。但有一类问题这些都测不了:系统在高并发下还能不能正常工作?
- 100 个用户同时登录,服务器会不会崩溃?
- 商品列表接口响应时间从 200ms 涨到 5s,用户体验还行吗?
- 大促活动开始瞬间涌入 1000 个请求,系统扛得住吗?
这些问题需要性能测试来回答。
本篇用 JMeter 对 MallLite 做接口性能测试。JMeter 是性能测试领域最常用的工具,开源免费,社区成熟。
本篇九个部分:
- 什么是性能测试
- 性能测试指标
- JMeter 环境搭建
- JMeter 核心概念
- 第一个测试计划:MallLite 登录接口
- 测试计划进阶:多接口场景
- 参数化与关联
- 测试场景设计
- 性能报告分析
一、什么是性能测试
1.1 性能测试的定义
功能测试验证的是"系统做对了没有",性能测试验证的是"系统做得快不快、扛不扛得住"。
功能测试:1 个用户登录 → 返回"登录成功" → 结果正确 ✓
性能测试:100 个用户同时登录 → 都返回"登录成功" → 平均 200ms → 指标达标 ✓
性能测试:1000 个用户同时登录 → 80% 返回成功,20% 超时 → 指标不达标 ✗
1.2 性能测试的类型
| 类型 | 目的 | 做法 | 例 |
|---|---|---|---|
| 负载测试(Load) | 测试系统在预期负载下的表现 | 逐步增加用户数,观察指标变化 | 100 个用户同时浏览商品 |
| 压力测试(Stress) | 找到系统的极限 | 继续增加用户数直到系统崩溃 | 加到 500、1000 用户看什么时候扛不住 |
| 稳定性测试(Endurance) | 测试长时间运行是否稳定 | 用固定负载持续运行几小时 | 100 个用户持续运行 4 小时 |
| 并发测试(Spike) | 测试瞬间高并发 | 短时间内突然涌入大量请求 | 大促开始瞬间 1000 个请求同时到达 |
知识点:什么时候需要做性能测试?
必须做:
- 新系统上线前
- 大促活动前(双 11、618)
- 架构变更后(换了数据库、加了缓存、迁移到云)
建议做:
- 接口响应变慢了,定位瓶颈
- 数据量增长后(从 1 万条商品变成 100 万条)
- 新增了核心功能(如支付模块)
不需要做:
- 内部管理系统(用户少,性能不是问题)
- 纯前端页面(性能瓶颈在后端)
1.3 性能测试流程
第 1 步:明确目标
系统需要支撑多少并发用户?
接口响应时间要求是多少?
可接受的错误率是多少?
第 2 步:选择场景
选哪些接口测?(核心接口优先)
每个接口的请求比例是多少?
例:浏览商品 60%,搜索 20%,登录 10%,下单 10%
第 3 步:准备环境
性能测试环境要跟生产环境配置一致(或按比例缩放)
准备测试数据(数据库中有足够的数据量)
第 4 步:编写脚本
用 JMeter 编写测试计划
配置线程组、HTTP 请求、断言、监听器
第 5 步:执行测试
逐步加压,记录数据
先小规模验证脚本正确,再大规模正式测试
第 6 步:分析结果
分析响应时间、吞吐量、错误率
找出性能瓶颈(数据库慢?网络延迟?代码问题?)
第 7 步:优化验证
针对瓶颈做优化
重新测试,对比优化效果
二、性能测试指标
2.1 核心指标
| 指标 | 英文 | 含义 | 例 |
|---|---|---|---|
| 响应时间 | Response Time | 从发送请求到收到响应的时间 | 200ms |
| 吞吐量 | Throughput (TPS/QPS) | 每秒处理的请求数 | TPS = 500(每秒处理 500 个请求) |
| 并发用户数 | Concurrent Users | 同时操作系统的用户数 | 100 个用户同时在线 |
| 错误率 | Error Rate | 失败请求占总请求的比例 | 0.1%(每 1000 个请求有 1 个失败) |
2.2 响应时间详解
用户感知:
< 200ms → 非常快(用户感觉不到延迟)
200-500ms → 快(可以接受)
500ms-1s → 还行(用户能感觉到,但不烦躁)
1-3s → 慢(用户开始不耐烦)
> 3s → 很慢(用户可能离开)
> 10s → 超时(用户关闭页面)
性能测试中的关键值:
平均响应时间(Average) → 所有请求的平均值
中位数(Median/P50) → 50% 的请求在这个时间内完成
90 分位(P90) → 90% 的请求在这个时间内完成
95 分位(P95) → 95% 的请求在这个时间内完成
99 分位(P99) → 99% 的请求在这个时间内完成
最大响应时间(Max) → 最慢的那个请求
知识点:为什么不能只看平均值?
假设有 10 个请求,响应时间分别是:
100, 120, 150, 180, 200, 220, 250, 300, 500, 5000
平均值 = 702ms ← 看起来还行
P95 = 500ms ← 95% 的请求在 500ms 内
最大值 = 5000ms ← 有 1 个请求用了 5 秒!
如果只看平均值,会漏掉那 1 个 5 秒的慢请求。
所以性能测试通常看 P95 或 P99,而不是平均值。
2.3 吞吐量详解
TPS(Transactions Per Second):每秒事务数
一个"事务"可以包含一个或多个请求
例:一次"下单"事务 = 加购物车请求 + 创建订单请求
QPS(Queries Per Second):每秒查询数
通常指每秒的请求数
TPS 和 QPS 的区别:一个 TPS 可能包含多个 QPS
关系:
如果每个事务只包含 1 个请求:TPS = QPS
如果每个事务包含 3 个请求:TPS = QPS / 3
2.4 错误率详解
可接受的错误率:
核心接口(登录、下单、支付):0%(不允许出错)
一般接口(商品列表、搜索): < 0.1%
非核心接口(推荐、统计): < 1%
常见的错误原因:
连接超时 → 服务器来不及处理,队列满了
响应超时 → 接口太慢,超过客户端等待时间
500 错误 → 服务器内部异常(代码 bug 或资源不足)
连接被拒绝 → 服务器主动拒绝新连接(达到最大连接数)
2.5 一份性能测试报告需要包含什么
1. 测试环境描述
服务器配置、数据库配置、网络环境
2. 测试场景描述
测了哪些接口、并发数、持续时间
3. 测试结果数据
┌─────────────┬──────────┬──────────┬──────────┬──────────┐
│ 接口 │ 平均RT │ P95 RT │ TPS │ 错误率 │
├─────────────┼──────────┼──────────┼──────────┼──────────┤
│ 登录 │ 180ms │ 350ms │ 200 │ 0% │
│ 商品列表 │ 120ms │ 280ms │ 500 │ 0% │
│ 创建订单 │ 450ms │ 800ms │ 100 │ 0.1% │
└─────────────┴──────────┴──────────┴──────────┴──────────┘
4. 结论和建议
哪些达标、哪些不达标、瓶颈在哪、怎么优化
三、JMeter 环境搭建
3.1 为什么用 JMeter
| 维度 | JMeter | LoadRunner | Locust |
|---|---|---|---|
| 费用 | 免费开源 | 商业(很贵) | 免费开源 |
| 学习难度 | 中等 | 较高 | 较低(Python) |
| 协议支持 | HTTP、JDBC、FTP、SMTP 等 | 全面 | 主要是 HTTP |
| GUI | 有图形界面 | 有图形界面 | Web 界面 |
| 分布式 | 支持 | 支持 | 支持 |
| 社区 | 非常成熟 | 一般 | 活跃 |
| 适用场景 | 接口性能测试为主 | 企业级全面性能测试 | 接口性能测试 |
JMeter 是性能测试领域使用最广泛的开源工具,适合入门学习。
3.2 安装步骤
第 1 步:确认 Java 已安装
JMeter 是 Java 开发的,需要 JDK。
powershell
java -version
# 如果第 14 篇已经装过 JDK,这里应该有输出
第 2 步:下载 JMeter
浏览器打开 https://jmeter.apache.org/download_jmeter.cgi
下载 apache-jmeter-5.x.zip(Binary 版本)
解压到一个目录,如 D:\tools\apache-jmeter-5.x
第 3 启动 JMeter
powershell
cd D:\tools\apache-jmeter-5.x\bin
jmeter.bat
JMeter 会打开图形界面(GUI 模式),看起来像一个树状结构的配置工具。
知识点:JMeter 的两种模式
GUI 模式(图形界面):
用来设计和调试测试计划
不适合运行大规模测试(GUI 本身消耗资源)
Non-GUI 模式(命令行):
用来运行正式的性能测试
命令:jmeter -n -t test_plan.jmx -l result.jtl -e -o report/
消耗资源少,结果更准确
正确的工作流程:
1. 用 GUI 模式设计测试计划
2. 保存为 .jmx 文件
3. 用 Non-GUI 模式运行测试
4. 用 GUI 模式或 HTML 报告查看结果
3.3 设置中文界面
JMeter 默认是英文界面。改为中文:
菜单栏 → Options → Choose Language → Chinese (Simplified)
设置后界面会变成中文。
四、JMeter 核心概念
4.1 测试计划的结构
JMeter 的测试计划是一棵树:
测试计划(Test Plan) ← 顶层容器
├── 线程组(Thread Group) ← 模拟用户
│ ├── 配置元件 ← 全局配置(如 HTTP 请求默认值)
│ ├── 前置处理器 ← 请求发送前的操作(如参数化)
│ ├── 取样器(Sampler) ← 实际发送的请求(如 HTTP 请求)
│ ├── 后置处理器 ← 请求发送后的操作(如提取响应数据)
│ ├── 断言(Assertion) ← 验证响应是否正确
│ └── 监听器(Listener) ← 查看结果和图表
└── 线程组 2... ← 可以有多个线程组
4.2 各组件的作用
线程组(Thread Group)
线程组 = 一组虚拟用户
配置项:
线程数(Number of Threads)= 100 → 模拟 100 个用户
启动时间(Ramp-Up Period)= 10s → 10 秒内启动完 100 个用户
循环次数(Loop Count)= 5 → 每个用户执行 5 次
含义:
100 个用户,10 秒内陆续启动,每个用户执行 5 次请求
总请求数 = 100 × 5 = 500
┌─── 第 1 秒:10 个用户开始 ───┐
├─── 第 2 秒:10 个用户开始 ───┤
├─── ... ───┤
└─── 第 10 秒:最后 10 个用户 ───┘
→ 100 个用户全部就绪
知识点:Ramp-Up 的重要性
如果 Ramp-Up = 0:
100 个用户同时启动 → 瞬间涌入 100 个请求 → 不真实
如果 Ramp-Up = 100:
100 个用户,100 秒内启动 → 每秒启动 1 个 → 太温和
一般设为"线程数 / 10":
100 个用户,Ramp-Up = 10 → 每秒启动 10 个 → 比较真实
取样器(Sampler)
取样器 = 一个具体的请求
HTTP 请求取样器:
协议:http
服务器:localhost
端口:8000
方法:POST
路径:/api/user/login
请求体:{"username": "admin", "password": "admin123"}
断言(Assertion)
断言 = 验证响应是否符合预期
响应断言:
检查响应中是否包含 "登录成功"
如果不包含,该请求标记为失败
状态码断言:
检查 HTTP 状态码是否为 200
监听器(Listener)
监听器 = 查看测试结果的工具
查看结果树 → 每个请求的详细信息(调试用)
聚合报告 → 统计数据(平均值、P95、TPS、错误率)
响应时间图 → 响应时间的趋势图
TPS 图 → 吞吐量的趋势图
配置元件
HTTP 请求默认值:
设置所有 HTTP 请求的公共参数(服务器地址、端口)
避免每个请求都重复填写
定时器(Timer)
定时器 = 请求之间的等待时间(模拟用户"思考时间")
固定定时器:每次请求前等待 1000ms
高斯随机定时器:等待 500-1500ms 之间的随机时间
知识点:为什么要加定时器?
真实用户不会连续不停地发请求。
用户看完商品列表 → 思考几秒 → 点击详情 → 看几秒 → 加购物车
定时器模拟这个"思考时间",让测试更接近真实场景。
五、第一个测试计划:MallLite 登录接口
5.1 目标
对 MallLite 的登录接口做简单的并发测试:50 个用户同时登录,观察响应时间和错误率。
5.2 创建步骤
第 1 步:新建测试计划
打开 JMeter → 菜单 → 文件 → 新建
左侧面板出现"测试计划"
第 2 步:添加 HTTP 请求默认值
右键测试计划 → 添加 → 配置元件 → HTTP 请求默认值
填写:
协议:http
服务器名称或 IP:localhost
端口号:8000
知识点:HTTP 请求默认值的作用
所有 HTTP 请求都会继承这些默认值,不用每个请求都写一遍 localhost:8000。如果要换测试环境,只改这一处。
第 3 步:添加线程组
右键测试计划 → 添加 → 线程(用户)→ 线程组
填写:
线程数(用户数):50
启动时间(秒):5
循环次数:10
含义:50 个虚拟用户,5 秒内启动,每个用户执行 10 次登录。总请求数 = 50 × 10 = 500。
第 4 步:添加 HTTP 请求
右键线程组 → 添加 → 取样器 → HTTP 请求
填写:
名称:登录接口
方法:POST
路径:/api/user/login
消息体数据(Body Data):
{
"username": "admin",
"password": "admin123"
}
HTTP 请求头(需要先添加 HTTP 信息头管理器):
Content-Type: application/json
第 5 步:添加 HTTP 信息头管理器
右键线程组 → 添加 → 配置元件 → HTTP 信息头管理器
添加:
名称:Content-Type
值:application/json
知识点:为什么需要设置 Content-Type?
MallLite 的接口是 RESTful JSON 格式。如果不设置 Content-Type: application/json,服务器可能无法解析请求体。
第 6 步:添加响应断言
右键 HTTP 请求 → 添加 → 断言 → 响应断言
填写:
测试字段:响应文本
模式匹配规则:包括
测试模式:登录成功
如果响应中不包含"登录成功",该请求标记为失败。
第 7 步:添加监听器
右键线程组 → 添加 → 监听器 → 查看结果树(调试用)
右键线程组 → 添加 → 监听器 → 聚合报告(正式数据)
右键线程组 → 添加 → 监听器 → 用表格查看结果
5.3 调试运行
第 1 步:先确认 MallLite 正在运行
cd mall-lite && python run.py
第 2 步:在 JMeter 中点击绿色三角形 ▶ 按钮(启动)
注意:会弹出提示"是否保存",选择保存到一个 .jmx 文件
第 3 步:查看"查看结果树"
绿色 ✅ → 请求成功
红色 ❌ → 请求失败(查看响应数据排查原因)
5.4 正式运行
调试通过后,用命令行运行(GUI 模式消耗资源,影响结果准确性):
powershell
cd D:\tools\apache-jmeter-5.x\bin
# 命令行运行
jmeter -n -t test_plans/login_test.jmx -l reports/login_result.jtl -e -o reports/login_report
# 参数说明:
# -n Non-GUI 模式
# -t test.jmx 测试计划文件
# -l result.jtl 结果数据文件
# -e 生成 HTML 报告
# -o report/ HTML 报告输出目录
运行完成后,打开 reports/login_report/index.html 查看报告。
5.5 读取聚合报告
聚合报告中的关键列:
标签 → 接口名称(登录接口)
样本 → 总请求数(500)
平均值 → 平均响应时间(ms)
中位数 → P50 响应时间
90% 百分位 → P90 响应时间
95% 百分位 → P95 响应时间
99% 百分位 → P99 响应时间
最小值 → 最快的响应时间
最大值 → 最慢的响应时间
异常% → 错误率
吞吐量 → TPS(每秒处理请求数)
六、测试计划进阶:多接口场景
6.1 为什么测多接口
真实用户不会只调一个接口。一个典型的用户行为是:
登录 → 浏览商品列表 → 查看商品详情 → 加购物车 → 创建订单
性能测试需要模拟这个完整流程,因为接口之间会共享资源(连接池、Session、数据库连接),单独测一个接口看不出问题。
6.2 创建多接口测试计划
在同一个线程组下添加多个 HTTP 请求,按用户操作顺序排列:
线程组(50 用户,Ramp-Up 10s,循环 10 次)
├── HTTP 请求默认值(localhost:8000)
├── HTTP 信息头管理器(Content-Type: application/json)
│
├── HTTP 请求 1:登录
│ POST /api/user/login
│ Body: {"username": "admin", "password": "admin123"}
│ └── 后置处理器:JSON 提取器(提取 token)
│
├── 定时器(固定定时器 1000ms,模拟"思考")
│
├── HTTP 请求 2:获取商品列表
│ GET /api/product/list?page=1&pageSize=20
│ HTTP 请求头:Authorization: ${token}
│
├── 定时器(固定定时器 2000ms)
│
├── HTTP 请求 3:获取商品详情
│ GET /api/product/detail/1
│ HTTP 请求头:Authorization: ${token}
│
├── 定时器(固定定时器 1000ms)
│
├── HTTP 请求 4:添加购物车
│ POST /api/cart/add
│ Body: {"productId": 1, "quantity": 1}
│ HTTP 请求头:Authorization: ${token}
│
└── 聚合报告
知识点:后置处理器和关联
登录接口返回的 token 需要传给后续请求。用 JSON 提取器从登录响应中提取 token,存到变量 ${token} 中,后续请求通过 ${token} 引用。
这跟接口自动化中"从登录响应提取 token 给后续接口用"是同一个思路。
七、参数化与关联
7.1 参数化
如果 50 个用户都用同一个账号登录,服务器只维护一个 Session,不能真实反映并发压力。参数化让每个虚拟用户用不同的账号。
方式 1:CSV 数据文件设置
第 1 步:创建 CSV 文件 test_data/users.csv
admin,admin123
user1,password1
user2,password2
user3,password3
...
第 2 步:在 JMeter 中添加 CSV 数据文件设置
右键线程组 → 添加 → 配置元件 → CSV 数据文件设置
文件名:test_data/users.csv
变量名:username,password
分隔符:,
遇到文件结束符是否再次循环:True
第 3 步:在 HTTP 请求中引用变量
Body: {"username": "${username}", "password": "${password}"}
知识点:CSV 参数化的工作方式
线程 1 → 读第 1 行 → username=admin, password=admin123
线程 2 → 读第 2 行 → username=user1, password=password1
线程 3 → 读第 3 行 → username=user2, password=password2
...
线程 50 → 读第 50 行 → ...
线程 51 → 再次从第 1 行开始(如果设置了循环)
方式 2:函数助手
JMeter 菜单 → 工具 → 函数助手对话框
常用函数:
__Random(1,1000) → 生成 1-1000 的随机数
__RandomString(8) → 生成 8 位随机字符串
__time(yyyyMMdd) → 当前日期
__UUID → 生成唯一 ID
7.2 关联
关联是指从上一个请求的响应中提取数据,传给下一个请求。
最常见的场景:登录 → 提取 token → 后续请求带 token
第 1 步:在"登录"请求下添加"JSON 提取器"
右键 HTTP 请求(登录)→ 添加 → 后置处理器 → JSON 提取器
变量名:token
JSON 路径表达式:$.data.token
匹配数字:1
第 2 步:在后续请求中引用
HTTP 请求头管理器中添加:
Authorization: Bearer ${token}
知识点:JSON 提取器的 JSONPath 表达式
MallLite 登录响应:
{
"code": 200,
"message": "登录成功",
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9...",
"username": "admin"
}
}
JSONPath 表达式:
$.data.token → 提取 token 值
$.data.username → 提取 username
$.code → 提取 code
$.data.items[0].id → 提取列表中第一个商品的 id
这跟接口自动化中 Extractor 的思路完全一样:
接口自动化:Extractor.get(resp.json(), "data.token")
JMeter: JSON 提取器 → $.data.token → ${token}
7.3 定时器
固定定时器(Constant Timer):
每次请求前等待固定时间
例:1000ms(模拟用户每秒操作一次)
高斯随机定时器(Gaussian Random Timer):
等待时间在一定范围内随机
偏差 100ms,固定延迟 300ms
→ 实际等待 200-400ms 之间的随机值
统一随机定时器(Uniform Random Timer):
最小 500ms,最大 1500ms
→ 实际等待 500-1500ms 之间的随机值
知识点:什么时候不加定时器?
压力测试(Stress Test)中通常不加定时器。
因为压力测试的目的是找到系统极限,
要求尽可能高的请求频率。
负载测试(Load Test)才需要模拟真实用户的"思考时间"。
八、测试场景设计
8.1 场景 1:基准测试
目的:了解系统在低负载下的基本性能
配置:
线程数:10
Ramp-Up:5s
循环次数:100
总请求数:1000
预期结果:
响应时间 < 200ms
错误率 = 0%
TPS 作为基准参考
8.2 场景 2:负载测试
目的:测试系统在预期负载下的表现
配置(逐步加压):
阶段 1:10 用户,持续 1 分钟
阶段 2:30 用户,持续 1 分钟
阶段 3:50 用户,持续 1 分钟
阶段 4:80 用户,持续 1 分钟
阶段 5:100 用户,持续 1 分钟
观察:
随着用户增加,响应时间怎么变化?
TPS 是不是线性增长?
哪个用户数级别开始出现错误?
知识点:用"阶梯线程组"实现逐步加压
JMeter 中添加"阶梯线程组"插件(Stepping Thread Group):
This group will start 100 threads → 最终 100 个用户
First, wait for 0 seconds → 不等待,直接开始
Then start 10 threads → 第一批 10 个用户
Next, add 10 threads every 30 seconds → 每 30 秒增加 10 个
Using ramp-up of 5 seconds → 每批 5 秒内启动
Then hold load for 60 seconds → 全部启动后持续 1 分钟
Finally, stop 10 threads every 10 seconds → 每 10 秒减少 10 个
8.3 场景 3:压力测试
目的:找到系统的极限(什么时候崩溃)
配置:
线程数:逐步增加到 200、500、1000
每个级别持续 5 分钟
观察:
什么时候响应时间超过 3 秒?
什么时候错误率超过 5%?
什么时候服务器 CPU 达到 100%?
服务器崩溃前的 TPS 是多少?
知识点:压力测试的"拐点"
TPS 曲线通常呈现:
前期:用户增加 → TPS 线性增长
中期:用户继续增加 → TPS 增长变缓(到达瓶颈)
后期:用户再增加 → TPS 下降 + 错误率上升(过载)
拐点就是系统容量的上限
8.4 场景 4:稳定性测试
目的:长时间运行是否稳定(检测内存泄漏等)
配置:
线程数:50(预期负载)
持续时间:4 小时(或 8 小时、24 小时)
观察:
响应时间是否随时间增长?(可能内存泄漏)
TPS 是否稳定?
是否有间歇性错误?
服务器内存使用是否持续增长?
知识点:为什么稳定性测试要跑这么久?
某些问题只在长时间运行后才暴露:
内存泄漏:内存使用量持续增长,直到 OOM
连接池耗尽:数据库连接没有正确释放
文件句柄泄漏:打开了文件没关闭
缓存过期:缓存失效后查询变慢
跑 10 分钟看不出这些问题,至少要跑几小时。
8.5 场景 5:混合场景
目的:模拟真实用户的混合操作
配置(一个线程组中多个请求,按比例分布):
浏览商品列表(60%)→ HTTP 请求:GET /api/product/list
搜索商品(20%) → HTTP 请求:GET /api/product/list?keyword=iPhone
登录(10%) → HTTP 请求:POST /api/user/login
下单(10%) → HTTP 请求:POST /api/order/create
用"吞吐量控制器"控制比例:
右键线程组 → 添加 → 逻辑控制器 → 吞吐量控制器
百分比模式:60(浏览商品)
百分比模式:20(搜索)
百分比模式:10(登录)
百分比模式:10(下单)
8.6 场景设计原则
原则 1:先基准,后加压
先用小负载确认脚本正确,再逐步加大负载
原则 2:一次只变一个变量
同时改线程数和循环次数,不知道是哪个因素导致的性能变化
原则 3:模拟真实场景
加定时器(模拟用户思考时间)
用参数化(不同用户用不同数据)
用混合场景(不同操作按比例分布)
原则 4:关注关键指标
不是所有接口都要测,优先测核心接口
不是 TPS 越高越好,要满足响应时间要求
原则 5:环境一致性
性能测试环境的配置要跟生产环境一致(或按比例缩放)
测试数据量要跟生产环境接近
九、性能报告分析
9.1 聚合报告解读
┌─────────────┬───────┬────────┬────────┬────────┬────────┬───────┬───────┐
│ 标签 │ 样本 │ 平均值 │ P95 │ P99 │ 最大值 │ 异常% │ 吞吐量 │
├─────────────┼───────┼────────┼────────┼────────┼────────┼───────┼───────┤
│ 登录接口 │ 500 │ 180ms │ 350ms │ 500ms │ 1200ms │ 0% │ 200/s │
│ 商品列表 │ 500 │ 120ms │ 280ms │ 420ms │ 900ms │ 0% │ 350/s │
│ 商品详情 │ 500 │ 80ms │ 180ms │ 300ms │ 600ms │ 0% │ 400/s │
│ 添加购物车 │ 500 │ 150ms │ 320ms │ 480ms │ 1100ms │ 0% │ 250/s │
│ 创建订单 │ 500 │ 450ms │ 800ms │ 1200ms │ 3000ms │ 0.2% │ 100/s │
├─────────────┼───────┼────────┼────────┼────────┼────────┼───────┼───────┤
│ 总计 │ 2500 │ 196ms │ 420ms │ 680ms │ 3000ms │ 0.04% │ │
└─────────────┴───────┴────────┴────────┴────────┴────────┴───────┴───────┘
分析:
✓ 登录、商品列表、商品详情响应时间在 200ms 以内(优秀)
⚠ 创建订单 P95 = 800ms(需要关注)
⚠ 创建订单最大值 = 3000ms(有慢请求)
⚠ 创建订单有 0.2% 错误率(需要排查)
✓ 整体错误率 0.04%(可接受)
9.2 性能瓶颈判断
| 现象 | 可能的瓶颈 | 优化方向 |
|---|---|---|
| 响应时间长,CPU 低 | 数据库慢查询、外部接口调用慢 | 加索引、加缓存、异步调用 |
| 响应时间长,CPU 高 | 代码逻辑复杂、计算密集 | 优化算法、减少计算 |
| TPS 不增长,连接数到上限 | 连接池太小 | 增大连接池 |
| 内存持续增长 | 内存泄漏 | 排查代码中的内存泄漏 |
| 错误率高,日志报连接超时 | 服务器过载 | 增加服务器、加负载均衡 |
知识点:怎么看服务器的资源使用情况?
Linux 服务器:
top → CPU 和内存使用
vmstat 1 → CPU、内存、IO 详细信息
iostat -x 1 → 磁盘 IO
netstat -an | grep ESTABLISHED | wc -l → 当前连接数
Windows 服务器:
任务管理器 → 性能 tab
资源监视器
数据库:
慢查询日志
SHOW PROCESSLIST → 当前正在执行的查询
EXPLAIN SELECT... → 查看查询执行计划
9.3 常见优化手段
| 优化方向 | 具体措施 |
|---|---|
| 数据库优化 | 加索引、优化 SQL、读写分离、分库分表 |
| 缓存 | 热点数据用 Redis 缓存(如商品列表) |
| 代码优化 | 减少循环中的数据库查询、批量操作 |
| 连接池 | 增大数据库连接池、HTTP 连接池 |
| 异步处理 | 耗时操作用消息队列异步执行 |
| 负载均衡 | 增加服务器节点,分摊请求 |
| CDN | 静态资源(图片、JS、CSS)用 CDN 加速 |
9.4 性能测试结果的呈现
写一份性能测试报告,包含:
1. 测试概述
测试目的、测试时间、测试环境
2. 测试场景
并发用户数、持续时间、接口列表、参数化策略
3. 测试结果
聚合报告数据(表格)
响应时间趋势图
TPS 趋势图
错误率统计
4. 结论
哪些达标、哪些不达标
瓶颈在哪
优化建议
5. 附录
服务器资源监控截图
数据库慢查询日志
JMeter 测试计划文件
十、今日成果
- 讲解了性能测试的定义、类型和完整流程
- 掌握了核心指标:响应时间(P90/P95/P99)、TPS、错误率
- 搭建了 JMeter 环境,了解了 GUI 和 Non-GUI 两种模式
- 理解了 JMeter 核心概念:线程组、取样器、断言、监听器、定时器
- 编写了第一个测试计划:MallLite 登录接口并发测试
- 设计了多接口场景:登录 → 商品列表 → 商品详情 → 加购 → 下单
- 掌握了参数化(CSV)和关联(JSON 提取器)的用法
- 设计了 5 种测试场景:基准、负载、压力、稳定性、混合
- 学会了性能报告的分析方法和瓶颈判断
十一、下篇预告
18 - 项目收尾与企业落地
最后一篇,把前面 17 篇的内容串起来:完整项目结构回顾、CI/CD 集成、测试策略设计、企业级最佳实践、简历和面试指导。
性能测试篇完成。下一篇:项目收尾与企业落地(18/18)。