性能测试(Performance Testing)

性能测试(Performance Testing)

一句话总结:测试系统在不同负载下的响应速度、吞吐量和稳定性。

🌟 快速理解(小白入门)

用生活化类比

就像测试高速公路的承载能力

  • 功能测试 = 检查高速公路能否通车(能不能用)
  • 性能测试 = 测试高速公路能承载多少车辆(好不好用)
    • 1辆车:速度 120km/h ✅
    • 100辆车:速度 100km/h ✅
    • 1000辆车:速度 60km/h ⚠️
    • 10000辆车:堵车 ❌

就像测试餐厅的服务能力

1
2
3
4
1个客人:5分钟上菜 ✅
10个客人:10分钟上菜 ✅
100个客人:30分钟上菜 ⚠️
1000个客人:厨房崩溃 ❌

真实场景

电商大促性能测试

1
2
3
4
5
6
7
8
9
10
双11零点:
- 预计 100万 并发用户
- 每秒 10万 次下单请求
- 数据库 1000万 条记录

性能测试目标:
✅ 响应时间 < 3秒
✅ 成功率 > 99.9%
✅ CPU 使用率 < 80%
✅ 内存使用率 < 70%

📌 核心概念

定义

性能测试(Performance Testing):测试系统在不同负载下的响应时间、吞吐量、资源使用率和稳定性。

通俗解释

  • 功能测试:测试”能不能用”(按钮能否点击)
  • 性能测试:测试”好不好用”(按钮点击后多久响应)

关键指标

指标 说明 目标值示例
响应时间 从请求到响应的时间 < 3秒
吞吐量 每秒处理的请求数 > 1000 TPS
并发用户数 同时在线的用户数 10万
错误率 失败请求的比例 < 0.1%
CPU 使用率 CPU 占用百分比 < 80%
内存使用率 内存占用百分比 < 70%

🎯 为什么需要性能测试?

真实案例

案例 1:淘宝双11性能优化

时间:2009年 vs 2023年

数据对比

年份 交易额 并发用户 响应时间 成功率
2009 0.5亿 1万 10秒 95%
2023 5403亿 100万+ 1秒 99.99%

优化手段

  • 性能测试发现瓶颈
  • 数据库分库分表
  • 缓存优化(Redis)
  • CDN 加速
  • 限流降级

教训:充分的性能测试是大促成功的关键。


案例 2:GitHub 宕机事件

时间:2018年10月21日

问题:数据库故障导致24小时宕机。

原因

  • 性能测试不充分
  • 数据库主从切换失败
  • 数据不一致

损失:数百万开发者无法工作,GitHub 信誉受损

教训:性能测试必须包含故障场景测试。


行业数据

研究机构 数据 说明
Google 页面加载时间每增加 1秒,转化率下降 20% 性能影响收入
Amazon 页面加载时间每增加 100ms,销售额下降 1% 性能就是金钱
Akamai 53% 的移动用户会放弃加载超过 3秒的页面 性能影响用户体验

✅ 性能测试的优势

优势 1:发现性能瓶颈 🔍

通俗解释

就像体检发现身体的薄弱环节。

专业说明

性能测试能发现:

  • 数据库查询慢
  • 接口响应慢
  • 内存泄漏
  • CPU 占用高

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 使用 Locust 进行性能测试
from locust import HttpUser, task, between

class EcommerceUser(HttpUser):
wait_time = between(1, 3)

@task(3) # 权重 3
def browse_products(self):
"""浏览商品"""
self.client.get("/products")

@task(2) # 权重 2
def search_products(self):
"""搜索商品"""
self.client.get("/products/search?q=iPhone")

@task(1) # 权重 1
def add_to_cart(self):
"""加入购物车"""
self.client.post("/cart/add", json={
"product_id": 123,
"quantity": 1
})

# 运行:locust -f test_performance.py --users 1000 --spawn-rate 10
# 结果:
# - 浏览商品:平均响应时间 200ms ✅
# - 搜索商品:平均响应时间 500ms ✅
# - 加入购物车:平均响应时间 2000ms ❌ 瓶颈!

优化方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 优化前:每次都查询数据库
def add_to_cart(user_id, product_id, quantity):
product = db.query(f"SELECT * FROM products WHERE id = {product_id}")
cart = db.query(f"SELECT * FROM carts WHERE user_id = {user_id}")
# ...

# 优化后:使用 Redis 缓存
def add_to_cart(user_id, product_id, quantity):
# 从缓存获取商品信息
product = redis.get(f"product:{product_id}")
if not product:
product = db.query(f"SELECT * FROM products WHERE id = {product_id}")
redis.set(f"product:{product_id}", product, ex=3600)

# 购物车直接写入 Redis
redis.sadd(f"cart:{user_id}", product_id)

# 异步同步到数据库
celery.send_task("sync_cart_to_db", args=[user_id, product_id])

# 优化后响应时间:200ms ✅

优势 2:验证系统容量 📊

通俗解释

就像测试电梯能承载多少人。

专业说明

性能测试能验证:

  • 系统能承载多少并发用户
  • 系统能处理多少请求
  • 系统在什么负载下会崩溃

代码示例

1
2
3
4
5
6
7
8
9
10
11
# 使用 JMeter 进行容量测试
# 测试计划:
# 1. 线程组:1000 个用户
# 2. 递增时间:10 秒(每秒增加 100 个用户)
# 3. 持续时间:60 秒

# 结果分析:
# - 0-500 用户:响应时间 < 1秒 ✅
# - 500-800 用户:响应时间 1-3秒 ⚠️
# - 800-1000 用户:响应时间 > 5秒 ❌
# - 结论:系统容量上限为 800 并发用户

优势 3:优化用户体验 ⚡

通俗解释

就像优化餐厅的上菜速度。

专业说明

性能测试能优化:

  • 页面加载速度
  • 接口响应速度
  • 用户等待时间

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 前端性能测试
from playwright.sync_api import sync_playwright

def test_page_load_time():
with sync_playwright() as p:
browser = p.chromium.launch()
page = browser.new_page()

# 测试页面加载时间
start_time = time.time()
page.goto("https://example.com")
page.wait_for_load_state("networkidle")
end_time = time.time()

load_time = end_time - start_time

# 验证加载时间 < 3秒
assert load_time < 3, f"页面加载时间过长:{load_time}秒"

# 测试首屏渲染时间
fcp = page.evaluate("""
() => {
const paint = performance.getEntriesByType('paint');
const fcp = paint.find(entry => entry.name === 'first-contentful-paint');
return fcp ? fcp.startTime : 0;
}
""")

# 验证首屏渲染时间 < 1秒
assert fcp < 1000, f"首屏渲染时间过长:{fcp}ms"

browser.close()

🔄 性能测试的类型

1. 负载测试(Load Testing)

定义:测试系统在预期负载下的性能。

示例

1
2
# 测试系统在 1000 个并发用户下的性能
locust -f test_load.py --users 1000 --spawn-rate 10 --run-time 10m

🛠️ 性能测试工具推荐

开源工具

工具 语言 特点 推荐度
Locust Python 易用、可扩展 ⭐⭐⭐⭐⭐
JMeter Java 功能强大、GUI ⭐⭐⭐⭐
Gatling Scala 高性能、报告美观 ⭐⭐⭐⭐
K6 JavaScript 现代、易用 ⭐⭐⭐⭐⭐

Locust 示例(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from locust import HttpUser, task, between
import random

class EcommerceUser(HttpUser):
wait_time = between(1, 3)

def on_start(self):
"""用户登录"""
response = self.client.post("/login", json={
"email": "test@example.com",
"password": "password123"
})
self.token = response.json()["token"]

@task(5) # 权重 5
def browse_products(self):
"""浏览商品"""
self.client.get("/products", headers={
"Authorization": f"Bearer {self.token}"
})

@task(3) # 权重 3
def search_products(self):
"""搜索商品"""
keywords = ["iPhone", "iPad", "MacBook", "AirPods"]
keyword = random.choice(keywords)
self.client.get(f"/products/search?q={keyword}", headers={
"Authorization": f"Bearer {self.token}"
})

@task(2) # 权重 2
def view_product(self):
"""查看商品详情"""
product_id = random.randint(1, 1000)
self.client.get(f"/products/{product_id}", headers={
"Authorization": f"Bearer {self.token}"
})

@task(1) # 权重 1
def add_to_cart(self):
"""加入购物车"""
product_id = random.randint(1, 1000)
self.client.post("/cart/add", json={
"product_id": product_id,
"quantity": 1
}, headers={
"Authorization": f"Bearer {self.token}"
})

# 运行:
# locust -f locustfile.py --users 1000 --spawn-rate 10 --run-time 10m --host https://api.example.com

K6 示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import http from 'k6/http';
import { check, sleep } from 'k6';

export let options = {
stages: [
{ duration: '2m', target: 100 }, // 2分钟内增加到 100 个用户
{ duration: '5m', target: 100 }, // 保持 100 个用户 5 分钟
{ duration: '2m', target: 200 }, // 2分钟内增加到 200 个用户
{ duration: '5m', target: 200 }, // 保持 200 个用户 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 () {
// 登录
let loginRes = http.post('https://api.example.com/login', JSON.stringify({
email: 'test@example.com',
password: 'password123',
}), {
headers: { 'Content-Type': 'application/json' },
});

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

let token = loginRes.json('token');

// 浏览商品
let productsRes = http.get('https://api.example.com/products', {
headers: { 'Authorization': `Bearer ${token}` },
});

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

sleep(1);
}

📊 最佳实践

1. 制定性能目标 🎯

性能目标示例

指标 目标值 说明
响应时间 P95 < 500ms 95% 的请求响应时间 < 500ms
吞吐量 > 1000 TPS 每秒处理 > 1000 个请求
并发用户 10000 支持 10000 个并发用户
错误率 < 0.1% 错误率 < 0.1%
CPU 使用率 < 80% CPU 使用率 < 80%
内存使用率 < 70% 内存使用率 < 70%

2. 模拟真实场景 🎭

用户行为模拟

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from locust import HttpUser, task, between, SequentialTaskSet

class UserBehavior(SequentialTaskSet):
"""模拟真实用户行为"""

@task
def login(self):
"""1. 登录"""
self.client.post("/login", json={
"email": "test@example.com",
"password": "password123"
})

@task
def browse_homepage(self):
"""2. 浏览首页"""
self.client.get("/")

@task
def search_products(self):
"""3. 搜索商品"""
self.client.get("/products/search?q=iPhone")

@task
def view_product(self):
"""4. 查看商品详情"""
self.client.get("/products/123")

@task
def add_to_cart(self):
"""5. 加入购物车"""
self.client.post("/cart/add", json={
"product_id": 123,
"quantity": 1
})

@task
def checkout(self):
"""6. 结算"""
self.client.post("/orders", json={
"address": "北京市朝阳区xxx",
"phone": "13800138000"
})

@task
def logout(self):
"""7. 登出"""
self.client.post("/logout")

class EcommerceUser(HttpUser):
tasks = [UserBehavior]
wait_time = between(1, 3)

3. 分阶段测试 📈

测试阶段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 阶段 1:冒烟测试(5分钟)
# 目标:验证系统基本功能
locust -f locustfile.py --users 10 --spawn-rate 1 --run-time 5m

# 阶段 2:负载测试(30分钟)
# 目标:验证系统在预期负载下的性能
locust -f locustfile.py --users 1000 --spawn-rate 10 --run-time 30m

# 阶段 3:压力测试(30分钟)
# 目标:找到系统的性能上限
locust -f locustfile.py --users 5000 --spawn-rate 50 --run-time 30m

# 阶段 4:耐力测试(24小时)
# 目标:验证系统长时间运行的稳定性
locust -f locustfile.py --users 1000 --spawn-rate 10 --run-time 24h

4. 监控关键指标 📊

监控指标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 使用 Prometheus + Grafana 监控
from prometheus_client import Counter, Histogram, Gauge, generate_latest
from flask import Flask, Response

app = Flask(__name__)

# 定义指标
request_count = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint', 'status'])
response_time = Histogram('http_response_time_seconds', 'HTTP response time', ['method', 'endpoint'])
active_users = Gauge('active_users', 'Number of active users')
db_connections = Gauge('db_connections', 'Number of database connections')
cache_hit_rate = Gauge('cache_hit_rate', 'Cache hit rate')

@app.route('/metrics')
def metrics():
"""Prometheus 指标端点"""
return Response(generate_latest(), mimetype='text/plain')

@app.route('/products')
def get_products():
# 记录请求
request_count.labels(method='GET', endpoint='/products', status=200).inc()

# 记录响应时间
with response_time.labels(method='GET', endpoint='/products').time():
products = db.query("SELECT * FROM products")

# 更新活跃用户数
active_users.set(get_active_user_count())

# 更新数据库连接数
db_connections.set(get_db_connection_count())

# 更新缓存命中率
cache_hit_rate.set(get_cache_hit_rate())

return jsonify(products)

5. 持续优化 🔄

优化流程

1
2
3
4
5
6
7
8
9
graph TD
A[性能测试] --> B{是否达标?}
B -->|否| C[分析瓶颈]
C --> D[优化代码]
D --> E[优化数据库]
E --> F[优化缓存]
F --> G[优化架构]
G --> A
B -->|是| H[发布上线]

📊 总结对比

性能测试类型对比

类型 目标 负载 持续时间 关注点
负载测试 验证预期性能 预期负载 中等 响应时间、吞吐量
压力测试 找到性能上限 超出预期 中等 崩溃点、恢复能力
耐力测试 验证长期稳定性 预期负载 长时间 内存泄漏、性能衰减
峰值测试 验证突发负载 突然增加 短时间 快速响应、自动扩展
容量测试 验证数据容量 大量数据 中等 数据库性能、存储

🎓 学习资源

书籍推荐

  • 《性能之巅》- Brendan Gregg
  • 《高性能网站建设指南》- Steve Souders
  • 《Web 性能权威指南》- Ilya Grigorik

官方文档


🔗 相关主题

  • [[负载测试]] - 性能测试的一种
  • [[压力测试]] - 性能测试的一种
  • [[系统测试]] - 包含性能测试
  • [[自动化测试]] - 性能测试的实现方式

💡 快速参考卡片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
性能测试速查表
================

定义:测试系统在不同负载下的性能

核心指标:
- 响应时间(P95 < 500ms)
- 吞吐量(> 1000 TPS)
- 并发用户(10000)
- 错误率(< 0.1%)
- CPU 使用率(< 80%)
- 内存使用率(< 70%)

测试类型:
- 负载测试(预期负载)
- 压力测试(超出预期)
- 耐力测试(长时间)
- 峰值测试(突发负载)
- 容量测试(大量数据)

推荐工具:
- Locust(Python、易用)
- K6(JavaScript、现代)
- JMeter(Java、功能强大)
- Gatling(Scala、高性能)

最佳实践:
1. 制定性能目标
2. 模拟真实场景
3. 分阶段测试
4. 监控关键指标
5. 持续优化

性能优化方向:
- 代码优化(算法、数据结构)
- 数据库优化(索引、查询)
- 缓存优化(Redis、CDN)
- 架构优化(分布式、微服务)

常见瓶颈:
- 数据库查询慢 → 添加索引、优化查询
- 接口响应慢 → 使用缓存、异步处理
- 内存泄漏 → 检查代码、释放资源
- CPU 占用高 → 优化算法、分布式处理

2. 压力测试(Stress Testing)

定义:测试系统在超出预期负载下的性能。

示例

1
2
# 测试系统在 10000 个并发用户下的性能(超出预期)
locust -f test_stress.py --users 10000 --spawn-rate 100 --run-time 10m

3. 耐力测试(Endurance Testing)

定义:测试系统在长时间运行下的性能。

示例

1
2
# 测试系统在 1000 个并发用户下运行 24 小时
locust -f test_endurance.py --users 1000 --spawn-rate 10 --run-time 24h

4. 峰值测试(Spike Testing)

定义:测试系统在突然增加负载下的性能。

示例

1
2
# 测试系统在突然增加 5000 个用户时的性能
locust -f test_spike.py --users 5000 --spawn-rate 500 --run-time 5m

5. 容量测试(Volume Testing)

定义:测试系统在大量数据下的性能。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
# 测试系统在 1000万 条数据下的性能
def test_large_dataset():
# 插入 1000万 条数据
for i in range(10000000):
db.insert("INSERT INTO products ...")

# 测试查询性能
start_time = time.time()
result = db.query("SELECT * FROM products WHERE price > 1000")
end_time = time.time()

# 验证查询时间 < 1秒
assert end_time - start_time < 1

⚠️ 性能测试的挑战

挑战 1:环境搭建复杂 🔧

问题

  • 需要接近生产环境的硬件
  • 需要真实的数据量
  • 需要模拟真实的网络环境

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# docker-compose.yml
version: '3'
services:
app:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/test_db
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis

db:
image: postgres:14
environment:
POSTGRES_DB: test_db
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql

redis:
image: redis:7

locust:
image: locustio/locust
ports:
- "8089:8089"
volumes:
- ./locustfile.py:/mnt/locust/locustfile.py
command: -f /mnt/locust/locustfile.py --master

挑战 2:测试数据准备 📊

问题

  • 需要大量真实数据
  • 数据需要符合生产环境分布

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 使用 Faker 生成测试数据
from faker import Faker
import random

fake = Faker('zh_CN')

def generate_test_data(count=1000000):
"""生成 100万 条测试数据"""
users = []
for i in range(count):
users.append({
"id": i,
"name": fake.name(),
"email": fake.email(),
"phone": fake.phone_number(),
"address": fake.address(),
"created_at": fake.date_time_this_year()
})

# 批量插入数据库
db.bulk_insert("users", users, batch_size=10000)

# 生成测试数据
generate_test_data(1000000)

挑战 3:结果分析困难 📈

问题

  • 数据量大
  • 指标多
  • 难以定位瓶颈

解决方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 使用 Grafana + Prometheus 监控
# 1. 安装 Prometheus
docker run -d -p 9090:9090 prom/prometheus

# 2. 安装 Grafana
docker run -d -p 3000:3000 grafana/grafana

# 3. 配置监控指标
from prometheus_client import Counter, Histogram, Gauge
import time

# 请求计数器
request_count = Counter('http_requests_total', 'Total HTTP requests', ['method', 'endpoint'])

# 响应时间直方图
response_time = Histogram('http_response_time_seconds', 'HTTP response time', ['method', 'endpoint'])

# CPU 使用率
cpu_usage = Gauge('cpu_usage_percent', 'CPU usage percentage')

# 使用
@app.route('/products')
def get_products():
request_count.labels(method='GET', endpoint='/products').inc()

with response_time.labels(method='GET', endpoint='/products').time():
products = db.query("SELECT * FROM products")
return jsonify(products)

# 4. 在 Grafana 中创建仪表板
# - 请求数趋势图
# - 响应时间分布图
# - CPU/内存使用率图
# - 错误率图