性能测试 performance-testing

性能测试是一种评估系统在不同负载条件下行为的方法,包括响应时间、吞吐量、资源利用率和可扩展性。它有助于识别性能瓶颈、验证性能要求,并确保系统能够处理预期的负载。

测试 0 次安装 0 次浏览 更新于 3/4/2026

性能测试

概述 性能测试衡量系统在各种负载条件下的行为,包括响应时间、吞吐量、资源利用率和可扩展性。它有助于识别瓶颈、验证性能要求,并确保系统能够处理预期的负载。

何时使用

  • 验证响应时间要求
  • 测量API吞吐量和延迟
  • 测试数据库查询性能
  • 识别性能瓶颈
  • 比较算法效率
  • 优化前后的基准测试
  • 验证缓存效果
  • 测试并发用户容量

性能测试类型

  • 负载测试:正常预期负载
  • 压力测试:超出正常容量(见压力测试技能)
  • 峰值测试:突然的负载增加
  • 耐久性测试:持续负载超过时间
  • 可扩展性测试:不同规模下的性能
  • 基准测试:组件级性能

指令

1. k6用于API负载测试

// load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';

// 自定义指标
const errorRate = new Rate('errors');
const orderDuration = new Trend('order_duration');

// 测试配置
export const options = {
  stages: [
    { duration: '2m', target: 10 },   // 逐渐增加到10个用户
    { duration: '5m', target: 10 },   // 保持10个用户
    { duration: '2m', target: 50 },   // 逐渐增加到50个用户
    { duration: '5m', target: 50 },   // 保持50个用户
    { duration: '2m', target: 0 },    // 逐渐减少到0
  ],
  thresholds: {
    http_req_duration: ['p(95)<500'],  // 95%的请求在500ms以下
    http_req_failed: ['rate<0.01'],    // 错误率低于1%
    errors: ['rate<0.1'],              // 自定义错误率低于10%
  },
};

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

export function setup() {
  // 登录一次并获取授权令牌
  const loginRes = http.post(`${BASE_URL}/auth/login`, {
    email: 'test@example.com',
    password: 'password123',
  });

  return { token: loginRes.json('token') };
}

export default function (data) {
  // 测试1:获取产品(读操作繁重)
  const productsRes = http.get(`${BASE_URL}/products`, {
    headers: { Authorization: `Bearer ${data.token}` },
  });

  check(productsRes, {
    'products status is 200': (r) => r.status === 200,
    'products response time < 200ms': (r) => r.timings.duration < 200,
    'has products array': (r) => Array.isArray(r.json('products')),
  }) || errorRate.add(1);

  sleep(1);

  // 测试2:创建订单(写操作繁重)
  const orderPayload = JSON.stringify({
    userId: 'user-123',
    items: [
      { productId: 'prod-1', quantity: 2 },
      { productId: 'prod-2', quantity: 1 },
    ],
  });

  const orderRes = http.post(`${BASE_URL}/orders`, orderPayload, {
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${data.token}`,
    },
  });

  const orderSuccess = check(orderRes, {
    'order status is 201': (r) => r.status === 201,
    'order has id': (r) => r.json('id') !== undefined,
  });

  if (!orderSuccess) {
    errorRate.add(1);
  }

  orderDuration.add(orderRes.timings.duration);

  sleep(2);

  // 测试3:获取订单详情
  if (orderSuccess) {
    const orderId = orderRes.json('id');
    const orderDetailRes = http.get(`${BASE_URL}/orders/${orderId}`, {
      headers: { Authorization: `Bearer ${data.token}` },
    });

    check(orderDetailRes, {
      'order detail status is 200': (r) => r.status === 200,
    }) || errorRate.add(1);
  
  sleep(1);
}

export function teardown(data) {
  // 如有需要则清理
}
# 运行k6测试
k6 run load-test.js

# 运行不同场景
k6 run --vus 100 --duration 30s load-test.js

# 输出到InfluxDB以进行可视化
k6 run --out influxdb=http://localhost:8086/k6 load-test.js

2. Apache JMeter

<!-- test-plan.jmx(简化表示) -->
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2">
  <hashTree>
    <TestPlan testname="API Performance Test">
      <ThreadGroup testname="Users">
        <elementProp name="ThreadGroup.main_controller">
          <stringProp name="ThreadGroup.num_threads">50</stringProp>
          <stringProp name="ThreadGroup.ramp_time">60</stringProp>
          <stringProp name="ThreadGroup.duration">300</stringProp>
        </elementProp>

        <!-- HTTP Request: Get Products -->
        <HTTPSamplerProxy testname="GET /products">
          <stringProp name="HTTPSampler.domain">api.example.com</stringProp>
          <stringProp name="HTTPSampler.path">/products</stringProp>
          <stringProp name="HTTPSampler.method">GET</stringProp>
        </HTTPSamplerProxy>

        <!-- Assertions -->
        <ResponseAssertion testname="Response Code 200">
          <stringProp name="Assertion.test_field">Assertion.response_code</stringProp>
          <stringProp name="Assertion.test_type">8</stringProp>
          <stringProp name="Assertion.test_string">200</stringProp>
        </ResponseAssertion>

        <DurationAssertion testname="Response Time < 500ms">
          <stringProp name="DurationAssertion.duration">500</stringProp>
        </DurationAssertion>

        <!-- Timers -->
        <ConstantTimer testname="Think Time">
          <stringProp name="ConstantTimer.delay">2000</stringProp>
        </ConstantTimer>
      </ThreadGroup>
    </TestPlan>
  </hashTree>
</jmeterTestPlan>
# 运行JMeter测试
jmeter -n -t test-plan.jmx -l results.jtl -e -o report/

# 从结果生成报告
jmeter -g results.jtl -o report/

3. pytest-benchmark用于Python

# test_performance.py
import pytest
from app.services import DataProcessor, SearchEngine
from app.models import User, Product

class TestDataProcessorPerformance:
    @pytest.fixture
    def large_dataset(self):
        """为测试创建大型数据集。"""
        return [{'id': i, 'value': i * 2} for i in range(10000)]

    def test_process_data_performance(self, benchmark, large_dataset):
        """基准测试数据处理。"""
        processor = DataProcessor()

        result = benchmark(processor.process, large_dataset)

        assert len(result) == len(large_dataset)
        # 基准测试将报告执行时间

    def test_filter_data_performance(self, benchmark, large_dataset):
        """测试不同条件下的过滤性能。"""
        processor = DataProcessor()

        def filter_operation():
            return processor.filter(large_dataset, lambda x: x['value'] > 5000)

        result = benchmark(filter_operation)
        assert all(item['value'] > 5000 for item in result)

    @pytest.mark.parametrize('dataset_size', [100, 1000, 10000])
    def test_scalability(self, benchmark, dataset_size):
        """在不同规模下测试性能。"""
        processor = DataProcessor()
        data = [{'id': i, 'value': i} for i in range(dataset_size)]

        benchmark(processor.process, data)

class TestSearchPerformance:
    @pytest.fixture
    def search_engine(self):
        """用索引数据设置搜索引擎。"""
        engine = SearchEngine()
        # 索引10,000个产品
        for i in range(10000):
            engine.index(Product(id=i, name=f"Product {i}"))
        return engine

    def test_search_performance(self, benchmark, search_engine):
        """基准测试搜索查询。"""
        result = benchmark(search_engine.search, "Product 5000")
        assert len(result) > 0

    def test_search_with_filters(self, benchmark, search_engine):
        """基准测试带有过滤器的搜索。"""
        def search_with_filters():
            return search_engine.search(
                "Product",
                filters={'price_min': 10, 'price_max': 100}
            )

        result = benchmark(search_with_filters)

# 运行基准测试
# pytest test_performance.py --benchmark-only
# pytest test_performance.py --benchmark-compare

4. JMH用于Java基准测试

// PerformanceBenchmark.java
import org.openjdk.jmh.annotations.*;
import java.util.*;
import java.util.concurrent.TimeUnit;

@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
@Fork(value = 1, warmups = 1)
@Warmup(iterations = 3, time = 1)
@Measurement(iterations = 5, time = 1)
public class CollectionPerformanceBenchmark {

    @Param({"100", "1000", "10000"})
    private int size;

    private List<Integer> arrayList;
    private List<Integer> linkedList;
    private Set<Integer> hashSet;

    @Setup
    public void setup() {
        arrayList = new ArrayList<>();
        linkedList = new LinkedList<>();
        hashSet = new HashSet<>();

        for (int i = 0; i < size; i++) {
            arrayList.add(i);
            linkedList.add(i);
            hashSet.add(i);
        }
    }

    @Benchmark
    public void arrayListIteration() {
        int sum = 0;
        for (Integer num : arrayList) {
            sum += num;
        }
    }

    @Benchmark
    public void linkedListIteration() {
        int sum = 0;
        for (Integer num : linkedList) {
            sum += num;
        }
    }

    @Benchmark
    public void arrayListRandomAccess() {
        for (int i = 0; i < size; i++) {
            arrayList.get(i);
        }
    }

    @Benchmark
    public void linkedListRandomAccess() {
        for (int i = 0; i < size; i++) {
            linkedList.get(i);
        }
    }

    @Benchmark
    public boolean hashSetContains() {
        return hashSet.contains(size / 2);
    }

    public static void main(String[] args) throws Exception {
        org.openjdk.jmh.Main.main(args);
    }
}

// 运行:mvn clean install
//      java -jar target/benchmarks.jar

5. 数据库查询性能

# test_database_performance.py
import pytest
import time
from sqlalchemy import create_engine
from app.models import User, Order

class TestDatabasePerformance:
    @pytest.fixture
    def db_session(self):
        """创建测试数据库会话。"""
        engine = create_engine('postgresql://localhost/testdb')
        Session = sessionmaker(bind=engine)
        return Session()

    def test_query_without_index(self, db_session, benchmark):
        """在没有索引的情况下测量查询性能。"""
        def query():
            return db_session.query(User).filter(
                User.email == 'test@example.com'
            ).first()

        result = benchmark(query)

    def test_query_with_index(self, db_session, benchmark):
        """在有索引的情况下测量查询性能。"""
        # 假设email列已建立索引
        def query():
            return db_session.query(User).filter(
                User.email == 'test@example.com'
            ).first()

        result = benchmark(query)

    def test_n_plus_one_query(self, db_session):
        """识别N+1查询问题。"""
        start = time.time()

        # ❌ N+1查询
        users = db_session.query(User).all()
        for user in users:
            orders = user.orders  # 为每个用户触发单独的查询

        n_plus_one_time = time.time() - start

        # ✅ 预加载
        start = time.time()
        users = db_session.query(User).options(
            joinedload(User.orders)
        ).all()

        eager_time = time.time() - start

        assert eager_time < n_plus_one_time, "预加载应该更快"
        print(f"N+1: {n_plus_one_time:.3f}s, Eager: {eager_time:.3f}s")

    def test_pagination_performance(self, db_session, benchmark):
        """测试分页效率。"""
        def paginate():
            return db_session.query(Product)\
                .order_by(Product.id)\
                .limit(50)\
                .offset(1000)\
                .all()

        results = benchmark(paginate)
        assert len(results) == 50

6. 实时监控

// performance-monitor.js
class PerformanceMonitor {
  constructor() {
    this.metrics = [];
  }

  async measureEndpoint(name, fn) {
    const start = performance.now();
    const startMemory = process.memoryUsage();

    try {
      const result = await fn();
      const duration = performance.now() - start;
      const endMemory = process.memoryUsage();

      this.metrics.push({
        name,
        duration,
        memoryDelta: {
          heapUsed: endMemory.heapUsed - startMemory.heapUsed,
          external: endMemory.external - startMemory.external,
        },
        timestamp: new Date(),
      });

      return result;
    } catch (error) {
      throw error;
    }
  }

  getStats(name) {
    const measurements = this.metrics.filter(m => m.name === name);
    const durations = measurements.map(m => m.duration);

    return {
      count: measurements.length,
      mean: durations.reduce((a, b) => a + b, 0) / durations.length,
      min: Math.min(...durations),
      max: Math.max(...durations),
      p50: this.percentile(durations, 50),
      p95: this.percentile(durations, 95),
      p99: this.percentile(durations, 99),
    };
  }

  percentile(values, p) {
    const sorted = values.sort((a, b) => a - b);
    const index = Math.ceil((p / 100) * sorted.length) - 1;
    return sorted[index];
  }
}

// 在测试中的使用
const monitor = new PerformanceMonitor();

test('API endpoint performance', async () => {
  for (let i = 0; i < 100; i++) {
    await monitor.measureEndpoint('getUsers', async () => {
      return await fetch('/api/users').then(r => r.json());
    });
  }

  const stats = monitor.getStats('getUsers');

  expect(stats.p95).toBeLessThan(500); // 95百分位低于500ms
  expect(stats.mean).toBeLessThan(200); // 平均值低于200ms
});

性能指标

响应时间

  • 平均值:平均响应时间
  • 中位数(P50):第50百分位
  • P95:第95百分位(SLA目标)
  • P99:第99百分位(最差情况)
  • 最大值:最慢响应

吞吐量

  • 每秒请求数:处理的请求数量
  • 每秒事务数:完成的事务
  • 数据传输量:MB/s或GB/s

资源利用率

  • CPU:使用百分比
  • 内存:堆,RSS,外部
  • 网络:带宽使用
  • 磁盘I/O:读写操作

最佳实践

✅ 做

  • 明确定义性能要求(SLAs)
  • 使用真实数据量进行测试
  • 监控资源利用率
  • 测试缓存效果
  • 使用百分位数(P95,P99)而不是平均值
  • 测量前预热
  • 在类似生产的环境中运行测试
  • 识别并修复N+1查询问题

❌ 不做

  • 仅使用小数据集进行测试
  • 忽略内存泄漏
  • 在不切实际的环境中测试
  • 只关注平均响应时间
  • 跳过数据库索引分析
  • 仅测试快乐路径
  • 忽略网络延迟
  • 无统计意义的比较

工具

  • 负载测试:k6, JMeter, Gatling, Locust, Artillery
  • 基准测试:JMH(Java), pytest-benchmark(Python), Benchmark.js(Node.js)
  • 分析:Chrome DevTools, py-spy, VisualVM
  • 监控:Prometheus, Grafana, New Relic, Datadog
  • 数据库:EXPLAIN ANALYZE, pg_stat_statements

示例

另见:压力测试,持续测试,代码度量分析,进行全面的性能分析。