属于我们的Performance & Scalability系列
阅读完整指南80% 的性能问题是由最终用户发现的,而不是通过测试发现的。 负载测试通过在用户遇到问题之前根据应用程序模拟现实世界的流量模式来翻转这一比例。处理黑色星期五流量的网站和崩溃的网站之间的区别几乎总是在于是否有人先运行负载测试。
本指南涵盖了 Web 应用程序、电子商务平台和 ERP 系统的负载测试方法、工具选择、测试设计和结果解释。
要点
- 负载测试应该模拟真实的用户行为,而不仅仅是锤击单个端点
- 在优化之前建立性能基线——你无法改进你没有测量过的东西
- 在类似生产的环境中运行负载测试;分期结果可能无法反映生产行为
- 在 CI/CD 中自动进行负载测试,以在部署前捕获性能回归
负载测试的类型
| 测试类型 | 目的 | 持续时间 | 负载模式 |
|---|---|---|---|
| 烟雾测试 | 验证最小负载下的基本功能 | 1-2 分钟 | 1-5 个用户 |
| 负载测试 | 验证预期流量下的性能 | 10-30 分钟 | 交通正常 |
| 压力测试 | 找到突破点 | 15-30 分钟 | 逐渐增加 |
| 尖峰测试 | 测试突发流量激增 | 5-10 分钟 | 突然跳跃 |
| 浸泡测试 | 检测内存泄漏和退化 | 2-8小时 | 持续正常负载 |
使用 k6 进行负载测试
基本负载测试
// k6/load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 50 }, // Ramp up to 50 users
{ duration: '5m', target: 50 }, // Stay at 50 users
{ duration: '2m', target: 100 }, // Ramp up to 100 users
{ duration: '5m', target: 100 }, // Stay at 100 users
{ duration: '2m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
http_reqs: ['rate>100'],
},
};
export default function () {
// Simulate realistic user behavior
const homeResponse = http.get('https://example.com/');
check(homeResponse, {
'homepage status is 200': (r) => r.status === 200,
'homepage loads in under 1s': (r) => r.timings.duration < 1000,
});
sleep(Math.random() * 3 + 1); // 1-4 seconds think time
const productsResponse = http.get('https://example.com/api/v1/products');
check(productsResponse, {
'products API is 200': (r) => r.status === 200,
'products API under 500ms': (r) => r.timings.duration < 500,
});
sleep(Math.random() * 2 + 1);
}
电子商务用户旅程测试
// k6/ecommerce-journey.js
import http from 'k6/http';
import { check, group, sleep } from 'k6';
export const options = {
scenarios: {
browsing: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '5m', target: 200 },
{ duration: '10m', target: 200 },
{ duration: '5m', target: 0 },
],
exec: 'browsingScenario',
},
purchasing: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '5m', target: 20 },
{ duration: '10m', target: 20 },
{ duration: '5m', target: 0 },
],
exec: 'purchaseScenario',
},
},
thresholds: {
'http_req_duration{scenario:browsing}': ['p(95)<800'],
'http_req_duration{scenario:purchasing}': ['p(95)<2000'],
http_req_failed: ['rate<0.01'],
},
};
export function browsingScenario() {
group('Browse Products', () => {
http.get('https://store.example.com/');
sleep(2);
http.get('https://store.example.com/products');
sleep(3);
http.get('https://store.example.com/products/sample-product');
sleep(2);
});
}
export function purchaseScenario() {
group('Purchase Flow', () => {
// Browse
http.get('https://store.example.com/products/sample-product');
sleep(1);
// Add to cart
http.post('https://store.example.com/api/cart', JSON.stringify({
productId: 'prod_123',
quantity: 1,
}), { headers: { 'Content-Type': 'application/json' } });
sleep(2);
// Checkout
http.get('https://store.example.com/cart');
sleep(3);
// Place order (simulated)
const orderResponse = http.post('https://store.example.com/api/checkout/validate', JSON.stringify({
email: `test-${__VU}@example.com`,
}), { headers: { 'Content-Type': 'application/json' } });
check(orderResponse, {
'checkout validates': (r) => r.status === 200 || r.status === 201,
});
sleep(1);
});
}
压力测试(找到突破点)
// k6/stress-test.js
import http from 'k6/http';
import { check } from 'k6';
export const options = {
stages: [
{ duration: '2m', target: 100 },
{ duration: '5m', target: 100 },
{ duration: '2m', target: 200 },
{ duration: '5m', target: 200 },
{ duration: '2m', target: 500 },
{ duration: '5m', target: 500 },
{ duration: '2m', target: 1000 },
{ duration: '5m', target: 1000 },
{ duration: '5m', target: 0 },
],
};
export default function () {
const res = http.get('https://example.com/api/v1/products');
check(res, {
'status is 200': (r) => r.status === 200,
});
}
解释结果
关键指标
| 公制 | 健康 | 警告 | 关键 |
|---|---|---|---|
| P95 响应时间 | <500 毫秒 | 500ms-2s | >2秒 |
| P99 响应时间 | <1秒 | 1-5秒 | >5秒 |
| 错误率 | <0.1% | 0.1-1% | >1% |
| 吞吐量 | 达到目标 | 目标的 80% | <目标的 80% |
常见瓶颈模式
CPU 限制瓶颈:响应时间随负载线性增加。 P95 和 P99 慢慢分开。
- 修复:优化热代码路径、增加CPU容量或水平扩展
数据库瓶颈:响应时间在特定负载阈值下呈指数增长。连接池耗尽。
- 修复:查询优化、连接池、只读副本(请参阅数据库扩展指南)
内存瓶颈:随着时间的推移逐渐退化。 GC 暂停会导致延迟峰值。
- 修复:增加内存、修复内存泄漏、优化对象分配
网络瓶颈:所有端点的响应时间均匀增加。带宽饱和。
- 修复:静态资产的 CDN、压缩、减少有效负载大小
性能基线
建立基线
在优化之前,记录您当前的性能:
# Run baseline test
k6 run --out json=baseline-results.json k6/load-test.js
# Compare after optimization
k6 run --out json=optimized-results.json k6/load-test.js
绩效预算
定义每个端点可接受的性能:
| 端点 | P95 目标 | 吞吐量目标 |
|---|---|---|
| 主页 | 500 毫秒 | 200 请求/秒 |
| 产品列表 | 800 毫秒 | 150 请求/秒 |
| 产品详情 | 600 毫秒 | 200 请求/秒 |
| 添加到购物车 | 300 毫秒 | 100 请求/秒 |
| 结帐 | 2000 毫秒 | 50 请求/秒 |
| 搜索 | 500 毫秒 | 100 请求/秒 |
| 管理仪表板 | 1500 毫秒 | 20 请求/秒 |
CI/CD 集成
自动性能回归测试
# .github/workflows/performance.yml
name: Performance Test
on:
push:
branches: [main]
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run k6 load test
uses: grafana/[email protected]
with:
filename: k6/load-test.js
flags: --out json=results.json
env:
K6_TARGET_URL: ${{ secrets.STAGING_URL }}
- name: Check thresholds
run: |
if grep -q '"thresholds":{".*":"fail"' results.json; then
echo "Performance thresholds exceeded!"
exit 1
fi
负载测试清单
测试前
- 环境与生产环境匹配(实例类型、数据库大小)
- 测试数据具有代表性(实际产品数量、用户数量)
- 监控处于活动状态(在测试期间跟踪服务器指标)
- 通知利益相关者(负载测试可以触发警报)
- CDN 和缓存的配置与生产中相同
测试期间
- Monitor server CPU, memory, disk I/O
- 监控数据库连接和查询延迟
- 注意错误率增加
- 检查资源耗尽(文件描述符、连接)
- 注意性能下降的负载级别
测试后
- 记录基线结果
- 识别瓶颈及其负载阈值
- 创建票证以提高性能
- 与绩效预算进行比较
- 安排优化后的后续测试
常见问题
我们应该加载测试生产还是登台?
如果可能的话,两者都可以。用于定期测试和 CI/CD 集成的阶段。定期验证(在低流量时段)的生产,因为分段通常在数据库大小、缓存温度、CDN 配置和网络拓扑方面存在差异。如果您只能测试一种环境,请测试暂存环境,但尽可能使其与生产环境类似。
我们应该多久运行一次负载测试?
对每个部署进行冒烟测试(在 CI/CD 中自动化)。每周或在主要版本发布之前进行满负载测试。每季度或在已知的高流量事件(销售、发布)之前进行压力测试。每季度进行一次 Soak 测试,以检测内存泄漏和长期退化。
我们如何对 ERP 系统进行负载测试?
ERP 负载测试需要模拟并发用户执行不同的任务:生成发票、创建采购订单、运行报告、导入数据。重点关注最繁重的操作(报告生成、数据导入)和最并发的操作(高峰时段的订单输入)。 ECOSIRE 提供 Odoo 性能测试 作为我们支持服务的一部分。
请求之间的实际思考时间是多少?
对于电子商务浏览:2-5 秒。表格填写:10-30 秒。结账时间:15-60 秒。对于管理/ERP 使用:5-15 秒。始终在负载测试中添加随机思考时间——恒定的间隔会创建不切实际的同步负载模式。
接下来会发生什么
负载测试揭示了指导您优化工作的瓶颈。跟进针对数据库瓶颈的数据库扩展、针对静态资产交付的CDN优化以及针对弹性容量的自动缩放。
联系 ECOSIRE 以进行性能测试和优化,或探索我们的 DevOps 指南 以了解完整的基础架构策略。
由 ECOSIRE 发布——帮助企业构建在压力下运行的应用程序。
作者
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
相关文章
2026 年云托管成本是多少?实际价格细分(AWS、Hetzner、DigitalOcean、Odoo.sh)
真正的 2026 年云托管成本来自支付账单的团队:业余爱好每月 5-25 美元,中小企业每月 50-400 美元,隐藏出口和备份费用,预留实例数学。
2026 年 Odoo 托管要求:按用户数量调整服务器规模(使用实际配置)
按用户数量划分的 Odoo 托管要求:5 至 250 个以上用户的 vCPU、RAM、存储和工作线程设置,以及实际部署中的 PostgreSQL 调整值。
Shopify 速度优化:真正改变核心网络生命力的技术清单 (2026)
经过现场测试的 2026 年 Shopify 速度清单 — 哪些因素实际上改进了真实商店中的 LCP、INP 和 CLS,哪些因素浪费了时间,以及如何审核应用程序和主题。
更多来自Performance & Scalability
Shopify 速度优化:真正改变核心网络生命力的技术清单 (2026)
经过现场测试的 2026 年 Shopify 速度清单 — 哪些因素实际上改进了真实商店中的 LCP、INP 和 CLS,哪些因素浪费了时间,以及如何审核应用程序和主题。
2026 年技术 SEO 审核清单:我们在每个客户网站上运行的 47 项检查
2026 年,我们在每个客户网站上运行了 47 点技术 SEO 审核清单——可爬行性、索引、规范、hreflang、核心网络生命和日志。
Odoo 19 HR:技能矩阵、职业规划、绩效周期
Odoo 19 HR 升级:本地技能矩阵、职业道路规划、绩效评估周期、9 框网格、继任计划、HRIS 集成。
Odoo 19 性能基准:PostgreSQL 17 调整数字
真实的 Odoo 19 性能基准:Web 客户端速度、ORM 吞吐量、PG17 调整设置、连接池、工作线程数、扩展阈值。
OpenClaw 大规模成本优化和代币效率
OpenClaw 令牌成本优化:提示缓存、模型路由、响应缓存、批处理 API 和生产代理的每租户成本护栏。
Power BI 增量刷新超过 1000 万行的表
适用于 10M 以上行表的 Power BI 增量刷新手册:分区设计、RangeStart/RangeEnd、刷新策略、查询折叠和 DirectQuery 混合。