我的测试学习站TEST LEARNING HUB
开始学习
首页/我的面试题/如何分析性能测试结果,如何进行优化,代码级别监控优化

如何分析性能测试结果,如何进行优化,代码级别监控优化

先看哪些指标

性能分析不能只看平均值。核心指标:

  • 吞吐:RPS/TPS、请求数、事务数。
  • 延迟:p50、p90、p95、p99、最大延迟。
  • 错误率:HTTP 错误、业务错误、超时、重试。
  • 并发:VU、连接数、线程数、队列长度。
  • 资源:CPU、内存、磁盘 I/O、网络、GC。
  • 依赖:数据库、缓存、MQ、第三方接口。

两个常用方法:

  • RED:Rate(请求率)、Errors(错误率)、Duration(延迟)。
  • USE:Utilization(使用率)、Saturation(饱和度)、Errors(错误)。

分析流程

  1. 确认测试有效性

    • 压测机是否成为瓶颈?
    • 数据量、业务比例、思考时间是否接近真实?
    • 是否只压了单接口,没有覆盖链路?
  2. 看整体曲线

    • 加压阶段:延迟是否随并发线性上升?
    • 稳态阶段:是否出现抖动、错误、超时?
    • 拐点:哪个并发/ RPS 开始明显变差?
  3. 定位瓶颈层

    • 客户端:压测机 CPU、网络、端口耗尽。
    • 网络/网关:带宽、连接数、限流、超时。
    • 应用:CPU、GC、线程池、连接池、锁。
    • 数据库:慢查询、锁等待、连接数、磁盘。
    • 缓存/MQ:命中率、延迟、堆积。
    • 外部依赖:第三方接口超时。
  4. 下钻到代码

    • 看 trace 的慢 span。
    • 看火焰图/CPU profile。
    • 看慢查询和执行计划。
    • 看日志中的超时、重试、异常。
  5. 形成假设并验证

    • 每次只改一个点。
    • 改完重新压测,对比同一指标。
    • 保留基线和回归测试。

常见瓶颈

应用层

  • N+1 查询:循环里查数据库。
  • 同步阻塞:在请求线程里做大量计算、文件 IO、远程调用。
  • 锁竞争:全局锁、长事务、热点 key。
  • 序列化开销:大对象 JSON、重复序列化。
  • 连接池过小:数据库/HTTP 连接等待。
  • 线程池配置不合理:队列堆积或频繁创建线程。
  • 内存泄漏:缓存无上限、对象未释放。
  • GC 频繁:堆太小、对象分配过多。

数据库

  • 缺索引、索引失效。
  • 隐式类型转换。
  • like '%xxx%' 全表扫描。
  • 大偏移分页。
  • 长事务和锁等待。
  • 连接数打满。
  • 批量写入逐条提交。

缓存和 MQ

  • 缓存命中率低。
  • 缓存击穿/雪崩。
  • 大 key、热 key。
  • MQ 堆积、消费慢。
  • 重试导致重复消费。

基础设施

  • 带宽不足。
  • 连接数限制。
  • 磁盘 IOPS 不足。
  • 容器 CPU/内存 limit 过低。
  • 负载均衡策略不合理。

代码级优化

1. 减少数据库访问

# 不好:N+1
for order in orders:
    user = db.query_user(order.user_id)

# 好:批量查询
users = db.query_users([o.user_id for o in orders])
user_map = {u.id: u for u in users}

2. 批量写入

# 不好:逐条提交
for item in items:
    db.insert(item)

# 好:批量插入
db.bulk_insert(items)

3. 避免阻塞

  • 把耗时操作放到异步任务/队列。
  • 用非阻塞 IO 或线程池。
  • 设置合理的超时和熔断。
  • 不要在请求链路里做大文件处理。

4. 缓存

  • 热点数据放 Redis 或本地缓存。
  • 设置 TTL 和容量上限。
  • 防击穿:互斥锁/逻辑过期。
  • 防雪崩:随机 TTL、降级。

5. 减少序列化

  • 只返回必要字段。
  • 避免重复序列化/反序列化。
  • 大响应考虑分页或流式返回。

6. 锁和事务

  • 缩小锁范围。
  • 避免长事务。
  • 用乐观锁/版本号替代大范围悲观锁。
  • 热点账户/库存考虑分段或队列。

7. JVM/运行时

  • 调整堆大小和 GC 参数。
  • 分析 GC 日志,减少 Full GC。
  • Node.js 避免阻塞事件循环,CPU 密集任务用 worker。
  • Python 用多进程/异步,避免 GIL 影响。

代码级监控

指标

  • 接口耗时、错误率、QPS。
  • 数据库查询次数、慢查询数、连接池使用率。
  • 缓存命中率、Redis 延迟。
  • 线程池活跃数、队列长度。
  • GC 次数和耗时。
  • 自定义业务指标:下单成功率、支付成功率。

工具

  • APM:OpenTelemetry、SkyWalking、Pinpoint、Datadog、New Relic。
  • 指标:Prometheus + Grafana。
  • 日志:ELK/EFK、Loki。
  • 链路追踪:Jaeger、Tempo、SkyWalking。
  • Profiling:async-profiler(Java)、py-spy(Python)、clinic/--prof(Node)、pprof(Go)、Pyroscope(持续 profiling)。

埋点示例

import time
from prometheus_client import Histogram, Counter

REQUEST_DURATION = Histogram(
    "api_request_duration_seconds",
    "API request duration",
    ["path", "method", "status"],
)
DB_QUERY_COUNT = Counter(
    "db_query_total",
    "Database query count",
    ["operation", "table"],
)

def timed(path, method):
    def decorator(fn):
        def wrapper(*args, **kwargs):
            start = time.perf_counter()
            status = "200"
            try:
                return fn(*args, **kwargs)
            except Exception:
                status = "500"
                raise
            finally:
                REQUEST_DURATION.labels(path, method, status).observe(
                    time.perf_counter() - start
                )
        return wrapper
    return decorator

优化闭环

  1. 建立基线:版本、配置、数据量、指标。
  2. 压测并定位瓶颈。
  3. 只改一个点,重新压测。
  4. 对比 p95/p99、错误率、资源使用。
  5. 把关键指标做成看板和告警。
  6. 把核心场景加入 CI 性能回归,防止回退。

面试回答模板

我会先确认压测有效性,避免压测机或数据失真;然后看 RPS、p95/p99、错误率和资源指标,用 RED/USE 方法定位是客户端、应用、数据库、缓存还是外部依赖的问题。应用层重点看 N+1、阻塞调用、锁竞争、连接池和 GC;数据库看慢查询、索引、锁等待和连接数。优化时每次只改一个点,改完重新压测对比。代码级会用 APM、Prometheus、trace 和 profiling 做持续监控,把核心指标接入看板和 CI 性能回归。