一、压测基础认知:什么是服务压测、压测的核心目标、压测与功能测试的区别

好,咱们直接进入正题。

服务压测,说白了就是「给系统加点压力,看看它到底能扛多久」。我见过不少团队,功能测试跑得飞起,一上线就被用户骂到宕机。为什么?因为他们从来没问过自己一个问题:这个服务,到底能撑住多少并发?

1.1 什么是服务压测

服务压测,全称是「服务压力测试」。它模拟真实用户请求,持续向系统施压,直到系统出现瓶颈或崩溃。你想想看,一辆车出厂前要跑多少公里测试?一个服务上线前,当然也要跑几轮压测。

我个人习惯把压测分成三类:

  • 基准测试:单用户、单请求,摸清系统「正常状态」下的表现。比如一个接口响应时间 50ms,这就是基准。
  • 负载测试:逐步增加并发用户数,观察系统行为。我记得有一次做电商秒杀压测,并发从 100 涨到 500 时,响应时间从 80ms 飙到了 2s——这就是典型的负载拐点。
  • 压力测试:持续加压,直到系统崩溃。目的是找到系统的「极限水位」。我曾经压过一个消息队列服务,到 8000 QPS 时直接 OOM,嗯,那次教训挺深刻。

核心要点:压测不是「跑一次就完事」,而是一个持续逼近极限的过程。你压得越狠,暴露的问题越多。

1.2 压测的核心目标

很多人以为压测就是为了「测出最大并发数」。其实没那么简单。我总结下来,压测的核心目标就四个:

  1. 摸清系统容量:系统到底能扛多少 QPS?多少并发用户?这是最基础的目标。
  2. 发现性能瓶颈:是 CPU 满了?内存不够?还是数据库连接池耗尽?我在项目中遇到过,一个看似简单的查询接口,压到 200 并发时数据库连接池直接爆了,最后发现是连接没释放。
  3. 验证稳定性:系统在长时间高压下会不会内存泄漏?会不会频繁 Full GC?我曾经压过一个 Java 服务,跑了 12 小时后发现堆外内存涨了 2GB——这就是典型的稳定性问题。
  4. 指导容量规划:根据压测结果,决定需要多少台机器、多大带宽、多少内存。说白了,就是「用数据说话」。

我的经验:压测报告里一定要写清楚「建议扩容方案」。比如「当前单机 500 QPS 时 CPU 已到 85%,建议至少部署 4 台实例」。这样运维同学拿到报告就能直接干活。

1.3 压测与功能测试的区别

这个问题我经常被问到。其实区别很明显,我画个表格你就懂了:

对比维度 功能测试 压测
关注点 功能是否正确 性能是否达标
输入 单个或少量请求 大量并发请求
输出 通过/失败 响应时间、吞吐量、错误率
运行时间 几分钟到几小时 几小时到几天
环境要求 开发环境即可 需要独立压测环境
典型工具 Postman、JUnit Locust、JMeter、wrk

你想想看,功能测试就像检查一个水龙头能不能出水,压测则是把水龙头开到最大,看水管会不会爆。两者缺一不可。

避坑指南:我曾经见过一个团队,功能测试全部通过,上线后却被用户投诉「页面打不开」。后来一查,原来是某个接口在并发 50 时就会超时。功能测试只测了单用户,根本发现不了这个问题。所以,功能测试通过 ≠ 系统可用。

1.4 压测的知识体系框架

为了让你更直观地理解压测的整体结构,我画了一张图:

服务压测 什么是压测 基准测试 负载测试 压力测试 核心目标 摸清容量 发现瓶颈 验证稳定性 vs 功能测试 关注性能 大量并发 长时间运行 压测 = 给系统做「极限体检」 功能测试通过 ≠ 系统可用

这张图把压测的核心脉络串起来了。你记住一句话就行:压测不是跑个脚本就完事,而是一个「发现-分析-优化-再验证」的闭环过程。

1.5 一个简单的压测脚本示例

光说不练假把式。我写个最简单的 Python 压测脚本,用 requests 模拟并发请求。你感受一下:

import requests
import time
from concurrent.futures import ThreadPoolExecutor

def send_request(url):
    start = time.time()
    try:
        resp = requests.get(url, timeout=5)
        cost = time.time() - start
        return resp.status_code, cost
    except Exception as e:
        return None, time.time() - start

def run_pressure_test(url, concurrency=10, total_requests=100):
    with ThreadPoolExecutor(max_workers=concurrency) as executor:
        futures = [executor.submit(send_request, url) for _ in range(total_requests)]
        results = [f.result() for f in futures]
    
    success = sum(1 for code, _ in results if code == 200)
    avg_time = sum(cost for _, cost in results) / len(results)
    print(f"总请求: {total_requests}, 成功: {success}, 平均耗时: {avg_time:.3f}s")

if __name__ == "__main__":
    run_pressure_test("http://localhost:8080/api/health", concurrency=20, total_requests=200)

注意:这个脚本只是演示原理。实际压测中,我建议用 Locust 或 wrk 这类专业工具,它们能自动处理连接池、统计百分位延迟等细节。自己手写脚本容易漏掉很多关键指标。

1.6 压测前的准备工作

嗯,这里要注意。压测不是上来就开跑。我每次压测前都会做三件事:

  • 确认压测环境:和生产环境配置一致吗?网络带宽一样吗?数据库数据量接近吗?我曾经在压测环境上跑出 1000 QPS,上线后只有 300,因为压测环境的数据库是空表。
  • 明确压测指标:目标响应时间是多少?期望 QPS 是多少?错误率容忍上限是多少?没有指标,压测就是瞎跑。
  • 准备监控大盘:CPU、内存、网络、磁盘 IO、GC 情况,一个都不能少。我习惯用 Grafana 搭一个实时看板,压测时盯着看。

避坑指南:我曾经在压测前忘了关日志的 DEBUG 级别,结果压测时日志疯狂刷盘,把磁盘 IO 打满了,压测数据完全失真。所以,压测前一定要检查日志级别、缓存策略、连接池配置等「隐形因素」。

好了,这一章的内容就到这里。压测的基础认知,说白了就是三件事:知道它是什么,知道它要测什么,知道它和功能测试有什么不同。下一章我们会深入讲「压测流程设计」,包括怎么制定压测计划、怎么设计压测场景、怎么分析结果。到时候我会拿一个真实项目案例来拆解。

想要更多资料,联系可可