跳到主要内容

性能测试

性能测试是验证软件系统在特定负载下的响应速度、稳定性和可扩展性的测试类型。它帮助发现系统瓶颈,确保系统在生产环境中能够满足性能要求。

什么是性能测试?

性能测试通过模拟各种负载条件来评估系统的性能表现。不同于功能测试关注「系统能否正确工作」,性能测试关注「系统工作得有多好」。

性能测试的目的

目的说明
发现瓶颈识别系统中的性能限制因素
验证容量确认系统能否满足预期的用户负载
评估稳定性验证系统在长时间运行下的稳定性
优化指导为性能优化提供数据支持
容量规划为基础设施扩容提供依据

性能测试类型

负载测试(Load Testing)

负载测试在预期负载下验证系统性能,是最常见的性能测试类型。

目的:验证系统能否满足预期的用户负载和性能要求。

测试方法

  1. 确定预期的用户数和请求量
  2. 逐步增加负载到目标值
  3. 保持负载一段时间
  4. 监控系统各项指标
  5. 分析性能数据
// K6 负载测试示例
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
stages: [
{ duration: '2m', target: 100 }, // 2分钟内增加到100用户
{ duration: '5m', target: 100 }, // 保持100用户5分钟
{ duration: '2m', target: 0 }, // 2分钟内降到0
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95%请求响应时间小于500ms
http_req_failed: ['rate<0.01'], // 错误率小于1%
},
};

export default function () {
const res = http.get('https://api.example.com/users');

check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});

sleep(1); // 模拟用户思考时间
}

压力测试(Stress Testing)

压力测试在超过正常负载的情况下测试系统,目的是找到系统的极限和崩溃点。

目的:确定系统的性能极限,观察系统在超载时的行为。

测试方法

  1. 从正常负载开始
  2. 持续增加负载直到系统崩溃或性能严重下降
  3. 记录系统崩溃点和崩溃前的性能数据
  4. 观察系统恢复能力
// K6 压力测试示例
export const options = {
stages: [
{ duration: '2m', target: 100 }, // 正常负载
{ duration: '5m', target: 100 }, // 维持正常负载
{ duration: '2m', target: 200 }, // 超过正常负载
{ duration: '5m', target: 200 }, // 维持高负载
{ duration: '2m', target: 300 }, // 继续增加
{ duration: '5m', target: 300 }, // 观察系统极限
{ duration: '2m', target: 400 }, // 继续施压
{ duration: '5m', target: 400 }, // 找到崩溃点
{ duration: '5m', target: 0 }, // 恢复阶段
],
};

尖峰测试(Spike Testing)

尖峰测试模拟突然的大幅负载增加,验证系统处理突发流量的能力。

目的:验证系统对突发流量的响应和恢复能力。

场景示例

  • 电商促销活动开始瞬间
  • 限时抢购活动
  • 热门文章突然爆火
// K6 尖峰测试示例
export const options = {
stages: [
{ duration: '2m', target: 100 }, // 正常负载
{ duration: '10s', target: 1000 }, // 突然增加到10倍
{ duration: '1m', target: 1000 }, // 维持高负载
{ duration: '10s', target: 100 }, // 快速下降
{ duration: '2m', target: 100 }, // 恢复正常
],
};

耐久性测试(Endurance Testing)

耐久性测试在长时间内维持稳定的负载,验证系统的稳定性和资源泄漏问题。

目的:发现内存泄漏、数据库连接泄漏、文件句柄泄漏等随时间累积的问题。

测试时长:通常持续数小时到数天。

// K6 耐久性测试示例
export const options = {
stages: [
{ duration: '5m', target: 100 }, // 启动阶段
{ duration: '24h', target: 100 }, // 持续24小时
{ duration: '5m', target: 0 }, // 结束阶段
],
};

容量测试(Volume Testing)

容量测试使用大量数据来验证系统的数据处理能力。

目的:验证系统处理大量数据时的性能表现。

关注点

  • 数据库数据量增长对查询性能的影响
  • 文件存储容量对系统性能的影响
  • 日志文件大小对性能的影响

可扩展性测试(Scalability Testing)

可扩展性测试验证系统在增加资源(水平扩展或垂直扩展)后的性能提升。

目的:评估系统的扩展能力和效率。

测试方法

  1. 在基准配置下测试性能
  2. 增加资源(CPU、内存、服务器实例)
  3. 再次测试性能
  4. 分析性能提升与资源增加的比例

性能指标

响应时间指标

指标说明目标值
平均响应时间所有请求的平均响应时间通常 < 1秒
P5050%请求的响应时间反映典型用户体验
P9090%请求的响应时间大多数用户体验
P9595%请求的响应时间SLA 常用指标
P9999%请求的响应时间关注尾部延迟
最大响应时间最慢请求的响应时间识别异常情况
// K6 响应时间阈值配置
export const options = {
thresholds: {
http_req_duration: [
'avg<200', // 平均响应时间小于200ms
'p(50)<150', // 50%请求小于150ms
'p(90)<300', // 90%请求小于300ms
'p(95)<500', // 95%请求小于500ms
'p(99)<1000', // 99%请求小于1000ms
],
},
};

吞吐量指标

指标说明单位
RPS每秒请求数requests/second
TPS每秒事务数transactions/second
QPS每秒查询数queries/second
带宽数据传输速率MB/s

资源利用率指标

指标说明警戒值
CPU 使用率CPU 占用百分比> 80% 需关注
内存使用率内存占用百分比> 85% 需关注
磁盘 I/O磁盘读写速率接近上限需关注
网络 I/O网络带宽使用率> 70% 需关注
连接数数据库/API 连接数接近上限需关注

错误率指标

指标说明
HTTP 错误率4xx/5xx 响应占比
超时率超时请求占比
失败率业务失败请求占比

性能测试工具

工具对比

工具类型特点适用场景
JMeterGUI/CLI功能全面、插件丰富、支持多协议传统企业应用
K6CLIJavaScript 脚本、云原生、CI 友好现代云应用
LocustCLIPython 脚本、分布式、易扩展Python 团队
GatlingCLIScala DSL、高性能、详细报告高并发场景
VegetaCLI简单命令行、HTTP 负载测试快速基准测试
ArtilleryCLIJavaScript、支持 WebSocket实时应用

JMeter 实战

安装配置

# 下载并解压
wget https://downloads.apache.org//jmeter/binaries/apache-jmeter-5.6.3.tgz
tar -xzf apache-jmeter-5.6.3.tgz

# 启动 GUI
./apache-jmeter-5.6.3/bin/jmeter.sh

# 命令行运行
./apache-jmeter-5.6.3/bin/jmeter.sh -n -t test.jmx -l results.jtl

测试计划配置

<!-- test.jmx 简化示例 -->
<ThreadGroup>
<stringProp name="ThreadGroup.num_threads">100</stringProp>
<stringProp name="ThreadGroup.ramp_time">60</stringProp>
<boolProp name="ThreadGroup.scheduler">true</boolProp>
<stringProp name="ThreadGroup.duration">300</stringProp>
</ThreadGroup>

<HTTPSamplerProxy>
<stringProp name="HTTPSampler.domain">api.example.com</stringProp>
<stringProp name="HTTPSampler.path">/users</stringProp>
<stringProp name="HTTPSampler.method">GET</stringProp>
</HTTPSamplerProxy>

性能测试脚本

// JMeter Groovy 脚本示例
import org.apache.jmeter.protocol.http.sampler.HTTPSampler

// 创建 HTTP 请求
def sampler = new HTTPSampler()
sampler.setDomain("api.example.com")
sampler.setPath("/api/users")
sampler.setMethod("GET")
sampler.addArgument("page", "1")

// 执行请求
def result = sampler.sample()
log.info("Response time: " + result.getTime() + "ms")
log.info("Response code: " + result.getResponseCode())

K6 实战

安装

# macOS
brew install k6

# Windows
choco install k6

# Linux
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C4914C31F145
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt update
sudo apt install k6

基础脚本

// basic-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';

export const options = {
vus: 10, // 虚拟用户数
duration: '30s', // 测试持续时间
};

export default function () {
// GET 请求
const res = http.get('https://test-api.k6.io/public/crocodiles/');

// 断言检查
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 200ms': (r) => r.timings.duration < 200,
});

sleep(1); // 思考时间
}

复杂场景测试

// complex-scenario.js
import http from 'k6/http';
import { check, group, sleep } from 'k6';
import { Rate, Trend, Counter } from 'k6/metrics';

// 自定义指标
const myRate = new Rate('my_rate');
const myTrend = new Trend('my_trend');
const myCounter = new Counter('my_counter');

// 测试配置
export const options = {
scenarios: {
// 场景1:普通用户浏览
browsing: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '1m', target: 50 },
{ duration: '3m', target: 50 },
{ duration: '1m', target: 0 },
],
gracefulRampDown: '30s',
},
// 场景2:API 调用
api_calls: {
executor: 'constant-arrival-rate',
rate: 100, // 每秒100个请求
timeUnit: '1s',
duration: '5m',
preAllocatedVUs: 50,
maxVUs: 200,
},
},
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
},
};

// 测试数据
const BASE_URL = 'https://api.example.com';

export default function () {
group('用户流程', function () {
// 1. 登录
const loginRes = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
username: 'testuser',
password: 'password123',
}), {
headers: { 'Content-Type': 'application/json' },
});

check(loginRes, {
'login successful': (r) => r.status === 200,
});

const token = loginRes.json('token');
const authHeaders = {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
};

sleep(1);

// 2. 获取用户信息
const userRes = http.get(`${BASE_URL}/user/profile`, authHeaders);
check(userRes, {
'profile fetched': (r) => r.status === 200,
});

myTrend.add(userRes.timings.duration);

sleep(2);

// 3. 获取订单列表
const ordersRes = http.get(`${BASE_URL}/orders`, authHeaders);
check(ordersRes, {
'orders fetched': (r) => r.status === 200,
'has orders': (r) => r.json('orders').length > 0,
});

myCounter.add(1);
myRate.add(ordersRes.status === 200);
});
}

数据驱动测试

// data-driven.js
import http from 'k6/http';
import { check } from 'k6';
import { SharedArray } from 'k6/data';

// 从文件加载测试数据
const users = new SharedArray('users', function () {
return open('./users.json').split('\n').map(JSON.parse);
});

export const options = {
iterations: users.length, // 每个用户数据执行一次
};

export default function () {
const user = users[__ITER]; // 当前迭代的数据

const res = http.post('https://api.example.com/login', JSON.stringify({
username: user.username,
password: user.password,
}), {
headers: { 'Content-Type': 'application/json' },
});

check(res, {
'login successful': (r) => r.status === 200,
});
}

Locust 实战

安装

pip install locust

测试脚本

# locustfile.py
from locust import HttpUser, task, between, events
from locust.runners import MasterRunner, WorkerRunner
import json

class WebsiteUser(HttpUser):
"""模拟网站用户行为"""

wait_time = between(1, 5) # 请求间隔1-5秒

def on_start(self):
"""用户开始时执行(登录)"""
response = self.client.post("/api/auth/login", json={
"username": "testuser",
"password": "password123"
})
if response.status_code == 200:
self.token = response.json().get("token")
else:
self.token = None

@task(3) # 权重为3
def view_products(self):
"""浏览商品列表"""
self.client.get("/api/products", name="/products")

@task(2) # 权重为2
def view_product_detail(self):
"""查看商品详情"""
product_id = 1 # 可以随机生成
self.client.get(f"/api/products/{product_id}", name="/products/[id]")

@task(1) # 权重为1
def add_to_cart(self):
"""添加到购物车"""
if self.token:
self.client.post(
"/api/cart/items",
json={"product_id": 1, "quantity": 1},
headers={"Authorization": f"Bearer {self.token}"},
name="/cart/items"
)

# 自定义事件处理
@events.test_start.add_listener
def on_test_start(environment, **kwargs):
"""测试开始时执行"""
if isinstance(environment.runner, MasterRunner):
print("测试开始...")

@events.test_stop.add_listener
def on_test_stop(environment, **kwargs):
"""测试结束时执行"""
if isinstance(environment.runner, MasterRunner):
print("测试结束...")

运行测试

# Web UI 模式
locust -f locustfile.py

# 无头模式
locust -f locustfile.py --headless -u 100 -r 10 -t 5m \
--host https://api.example.com \
--html report.html

# 分布式模式
locust -f locustfile.py --master # 主节点
locust -f locustfile.py --worker --master-host=master-ip # 工作节点

性能测试流程

1. 需求分析

明确性能测试的目标和范围:

  • 预期用户数和并发量
  • 响应时间要求
  • 吞吐量要求
  • 可接受的错误率
  • 测试场景和业务流程

2. 测试计划

制定详细的测试计划:

  • 测试类型选择
  • 测试环境配置
  • 测试数据准备
  • 测试工具选择
  • 测试时间安排

3. 测试设计

设计测试场景和用例:

  • 用户行为建模
  • 测试数据设计
  • 性能指标定义
  • 阈值设定

4. 环境准备

搭建独立的性能测试环境:

# 环境配置示例
performance_test_env:
servers:
- name: app-server
cpu: 8 cores
memory: 16GB
count: 2
- name: database
cpu: 4 cores
memory: 32GB
storage: 500GB SSD
- name: cache
type: redis
memory: 8GB

network:
bandwidth: 1Gbps
latency: < 1ms

monitoring:
- prometheus
- grafana
- ELK stack

5. 脚本开发

编写和调试测试脚本:

  • 录制或编写测试脚本
  • 参数化测试数据
  • 添加断言和检查点
  • 配置思考时间

6. 测试执行

执行测试并监控:

  • 基准测试(确定基线)
  • 负载测试
  • 压力测试
  • 收集性能数据

7. 结果分析

分析测试结果:

// K6 报告分析示例
// 输出:
// http_req_duration..............: avg=120.5ms min=45.2ms med=98.3ms max=2.5s p(90)=180.2ms p(95)=250.6ms
// http_req_failed.................: 0.5% ✓ 50 ✗ 9950
// http_reqs........................: 10000 333.333333/s
// iteration_duration..............: avg=1.2s min=0.5s med=1.1s max=5.2s
// iterations.......................: 10000 333.333333/s
// vus.............................: 100 min=100 max=100

// 分析要点:
// 1. 平均响应时间是否符合预期
// 2. P95/P99 是否在可接受范围
// 3. 错误率是否低于阈值
// 4. 吞吐量是否满足需求

8. 性能优化

根据分析结果进行优化:

常见瓶颈和优化方向

瓶颈类型症状优化方向
数据库慢查询、连接池耗尽SQL优化、索引、连接池配置
内存GC频繁、OOM内存泄漏修复、缓存策略
CPU高CPU使用率算法优化、并发处理
网络高延迟、带宽不足CDN、压缩、连接复用
磁盘高IO等待SSD、缓存、异步写入

性能测试最佳实践

1. 测试环境隔离

# 确保测试环境与生产环境隔离
# 使用独立的数据、独立的网络
PERF_ENV=performance pytest tests/performance/

2. 基准测试先行

// 在做任何优化之前,先建立基准
export const options = {
vus: 1,
duration: '1m',
};

// 记录基准性能
// 后续优化可以对比基准

3. 模拟真实用户行为

// ❌ 不好:没有思考时间
export default function () {
http.get('https://api.example.com/data');
http.get('https://api.example.com/users');
}

// ✅ 好:包含思考时间和真实流程
export default function () {
// 登录
http.post('https://api.example.com/login', ...);
sleep(randomIntBetween(1, 3)); // 思考时间

// 浏览
http.get('https://api.example.com/data');
sleep(randomIntBetween(2, 5)); // 更长的思考时间

// 操作
http.post('https://api.example.com/action', ...);
}

4. 使用真实数据量

# ❌ 不好:测试数据库只有100条数据
# 实际生产可能有百万条数据,性能表现完全不同

# ✅ 好:使用接近生产的数据量
# 使用数据生成工具创建测试数据

5. 监控系统资源

# Prometheus 监控配置
scrape_configs:
- job_name: 'node_exporter'
static_configs:
- targets: ['localhost:9100']

- job_name: 'app_metrics'
static_configs:
- targets: ['app-server:8080']

6. 持续性能测试

# .github/workflows/performance.yml
name: Performance Tests

on:
schedule:
- cron: '0 2 * * *' # 每天凌晨2点
workflow_dispatch:

jobs:
k6:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Run K6 test
uses: grafana/k6-[email protected]
with:
filename: tests/performance/load-test.js

- name: Upload results
uses: actions/upload-artifact@v4
with:
name: k6-report
path: summary.json

常见问题

1. 测试结果不稳定

原因:网络波动、测试机资源不足、测试数据不一致

解决方案

  • 多次测试取平均值
  • 使用专用测试机
  • 确保测试数据一致

2. 测试环境与生产环境差异大

原因:硬件配置、数据量、网络环境差异

解决方案

  • 尽量使测试环境接近生产
  • 使用容器化保证环境一致
  • 记录环境差异并在分析时考虑

3. 无法模拟真实用户行为

原因:脚本过于简单、缺乏真实数据

解决方案

  • 分析生产日志了解用户行为
  • 使用生产数据脱敏后作为测试数据
  • 增加随机性和变化

总结

性能测试是确保系统质量的重要环节。关键要点:

  • 明确目标:知道要测试什么,为什么测试
  • 选择合适的测试类型:负载、压力、尖峰等
  • 使用合适的工具:JMeter、K6、Locust 等
  • 关注关键指标:响应时间、吞吐量、错误率
  • 持续改进:建立基准,持续监控,不断优化

参考资源