一句话总结:测试系统在不同负载下的响应速度、吞吐量和稳定性。
🌟 快速理解(小白入门)
用生活化类比
就像测试高速公路的承载能力:
- 功能测试 = 检查高速公路能否通车(能不能用)
- 性能测试 = 测试高速公路能承载多少车辆(好不好用)
- 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
| from locust import HttpUser, task, between
class EcommerceUser(HttpUser): wait_time = between(1, 3) @task(3) def browse_products(self): """浏览商品""" self.client.get("/products") @task(2) def search_products(self): """搜索商品""" self.client.get("/products/search?q=iPhone") @task(1) def add_to_cart(self): """加入购物车""" self.client.post("/cart/add", json={ "product_id": 123, "quantity": 1 })
|
优化方案:
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}")
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.sadd(f"cart:{user_id}", product_id) celery.send_task("sync_cart_to_db", args=[user_id, product_id])
|
优势 2:验证系统容量 📊
通俗解释:
就像测试电梯能承载多少人。
专业说明:
性能测试能验证:
- 系统能承载多少并发用户
- 系统能处理多少请求
- 系统在什么负载下会崩溃
代码示例:
优势 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 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; } """) assert fcp < 1000, f"首屏渲染时间过长:{fcp}ms" browser.close()
|
🔄 性能测试的类型
1. 负载测试(Load Testing)
定义:测试系统在预期负载下的性能。
示例:
1 2
| 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) def browse_products(self): """浏览商品""" self.client.get("/products", headers={ "Authorization": f"Bearer {self.token}" })
@task(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) 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) 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}" })
|
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 }, { duration: '5m', target: 100 }, { duration: '2m', target: 200 }, { duration: '5m', target: 200 }, { duration: '2m', target: 0 }, ], thresholds: { http_req_duration: ['p(95)<500'], http_req_failed: ['rate<0.01'], }, };
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
|
locust -f locustfile.py --users 10 --spawn-rate 1 --run-time 5m
locust -f locustfile.py --users 1000 --spawn-rate 10 --run-time 30m
locust -f locustfile.py --users 5000 --spawn-rate 50 --run-time 30m
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
| 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
| locust -f test_stress.py --users 10000 --spawn-rate 100 --run-time 10m
|
3. 耐力测试(Endurance Testing)
定义:测试系统在长时间运行下的性能。
示例:
1 2
| locust -f test_endurance.py --users 1000 --spawn-rate 10 --run-time 24h
|
4. 峰值测试(Spike Testing)
定义:测试系统在突然增加负载下的性能。
示例:
1 2
| 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
| def test_large_dataset(): 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() 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
| 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
| 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
|
docker run -d -p 9090:9090 prom/prometheus
docker run -d -p 3000:3000 grafana/grafana
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_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)
|