4.7 Linux 压力测试实战

预计阅读时间:22 分钟

📖 目录

4.6:Linux 性能调优 性能调优中分析了 CPU、内存、磁盘 I/O 和网络的观测指标。但观测的前提是制造负载——压力测试让你在受控环境中验证系统瓶颈、评估容量上限和检验调优效果。

本文覆盖四种场景的压测工具:stress-ng(CPU/内存)、fio(磁盘 I/O)、sysbench(数据库)、wrk / hey(HTTP)。

学习目标

  • 掌握 stress-ng、fio、sysbench、wrk/hey 四类压测工具的安装与核心用法
  • 理解不同压测场景下关键指标(IOPS、QPS、P99 延迟)的含义与参考阈值
  • 能够识别压测结果中的常见陷阱(page cache 误导、客户端瓶颈、OOM 等)
  • 建立系统化的压测方法论:基线先行、单变量原则、逐步加压、多次取稳

前置知识

1. CPU 压力测试(stress-ng)

stress-ng 是 Linux 上最灵活的 CPU/内存压力生成工具,支持 200+ 种 stress 方法。

# 安装
sudo apt install stress-ng   # Ubuntu / Debian
sudo dnf install stress-ng   # RHEL / Fedora

# 生成 4 个 CPU 压力线程,持续 60 秒
stress-ng --cpu 4 --timeout 60s

# 混合模式:CPU + 内存 + I/O 同时压
stress-ng --cpu 2 --vm 2 --vm-bytes 1G --hdd 1 --timeout 120s

# 指定 CPU 方法(默认 random)
stress-ng --cpu 0 --cpu-method matrixprod --timeout 30s
stress-ng --cpu 0 --cpu-method all --cpu-method-list  # 列出所有方法

观察负载:同时在另一个终端运行 htopmpstat -P ALL 1 查看每核使用率,以及 uptime 观察 load average。

集群场景 批量压测多台机器:ansible all -i inventory -m shell -a 'stress-ng --cpu 4 --timeout 60s'

内存压力

# 生成 2 个虚拟内存 worker,各占 512MB,持续 60 秒
stress-ng --vm 2 --vm-bytes 512M --timeout 60s

# 渐进式内存压测(每步 +256MB,触发 swap 行为)
for size in 256 512 768 1024 1536; do
    stress-ng --vm 1 --vm-bytes ${size}M --timeout 10s
    free -h | head -2
done

观察指标:free -h(内存使用)、vmstat 1(si/so 换入换出)、cat /proc/meminfo(详细内存状态)。

2. 磁盘 I/O 基准测试(fio)

fio 是事实上的磁盘性能测试标准,支持自定义 IO 深度、块大小、读写比例和延迟分布。

# 安装
sudo apt install fio

# 顺序读测试(块大小 1MB,队列深度 32)
fio --name=seq-read --ioengine=libaio --direct=1 --bs=1M \
    --rw=read --iodepth=32 --size=4G --numjobs=1 \
    --runtime=30 --time_based --group_reporting

# 随机写测试(块大小 4KB,队列深度 16)
fio --name=rand-write --ioengine=libaio --direct=1 --bs=4K \
    --rw=randwrite --iodepth=16 --size=4G --numjobs=2 \
    --runtime=30 --time_based --group_reporting

# 混合读写测试(70% 读 + 30% 写)
fio --name=mix --ioengine=libaio --direct=1 --bs=4K \
    --rw=rw --rwmixread=70 --iodepth=32 --size=4G \
    --runtime=60 --time_based --group_reporting

fio job 文件(可复用配置)

命令行参数多时推荐写成 job 文件,便于团队复用与评审:

# randwrite.ini
[global]
ioengine=libaio
direct=1
runtime=60
time_based=1
group_reporting

[randwrite-4k]
rw=randwrite
bs=4k
iodepth=32
size=4G
numjobs=2

# 运行
fio randwrite.ini

fio 输出关键字段:

字段含义参考值(SSD)
IOPS每秒 I/O 操作数随机读 >50K / 随机写 >30K
BW(带宽)每秒传输量顺序读 >500MB/s
lat(p99/p99.99)延迟百分位随机读 <2ms(p99)
注意 测试文件 --size 应大于物理内存的 2 倍,避免全部被 page cache 缓存导致结果虚高。--direct=1 绕过页缓存直接测试磁盘原始性能。

3. 数据库基准测试(sysbench)

sysbench 支持对 CPU、内存、线程、互斥锁和 MySQL/PostgreSQL 做基准测试。

# 安装
sudo apt install sysbench

# CPU 基准
sysbench cpu run

# 内存基准
sysbench memory run

# MySQL 基准测试
# 先准备数据(建测试表 + 插入数据)
sysbench /usr/share/sysbench/oltp_read_write.lua \
    --db-driver=mysql --mysql-user=root --mysql-password=yourpass \
    --mysql-db=sbtest --table-size=1000000 prepare

# 运行混合读写负载(64 并发,持续 120 秒)
sysbench /usr/share/sysbench/oltp_read_write.lua \
    --db-driver=mysql --mysql-user=root --mysql-password=yourpass \
    --mysql-db=sbtest --table-size=1000000 \
    --threads=64 --time=120 --report-interval=10 run

# 清理
sysbench /usr/share/sysbench/oltp_read_write.lua \
    --db-driver=mysql --mysql-user=root --mysql-password=yourpass \
    --mysql-db=sbtest cleanup

sysbench 输出关键字段:

字段含义
queries总查询数 / 每秒查询数(QPS)
transactions总事务数 / 每秒事务数(TPS)
latency(avg / 95th / max)平均、P95、最大延迟
errors / reconnects错误数与重连数
对比基准 始终在调整配置前后各跑一次 sysbench,用同一参数集做 A/B 对比。比如对比 my.cnf 调优前后的 QPS 差异。

4. HTTP 负载测试(wrk / hey)

wrk — 多线程 HTTP 压测

# 安装
sudo apt install wrk

# 基本用法:4 线程 64 连接,持续 30 秒
wrk -t4 -c64 -d30s http://localhost:8080/

# 带请求头的压测
wrk -t2 -c100 -d60s -H "Authorization: Bearer token123" \
    -H "User-Agent: wrk-bench" http://localhost:8080/api

# 使用 Lua 脚本做 POST 压测
# 创建 post.lua:
#   wrk.method = "POST"
#   wrk.body   = '{"key":"value"}'
#   wrk.headers["Content-Type"] = "application/json"
wrk -t4 -c64 -d30s -s post.lua http://localhost:8080/api

hey — 更简洁的 HTTP 压测

# 安装(Go 编写,单二进制)
# 从 GitHub releases 下载或 go install
go install github.com/rakyll/hey@latest

# 基本用法
hey -n 10000 -c 50 http://localhost:8080/

# 带 body 的 POST
hey -n 5000 -c 20 -m POST \
    -H "Content-Type: application/json" \
    -d '{"name":"test"}' http://localhost:8080/api

# 输出包含:总请求数、成功/失败数、QPS、延迟分布(p50/p75/p90/p99/max)

wrk / hey 输出关键字段:

字段含义
Requests/sec(QPS)每秒请求数
Latency Distributionp50 / p75 / p90 / p99 / max 延迟
Socket errorsconnect / read / write / timeout 错误
Non-2xx / 3xx responses非正常 HTTP 状态码
客户端瓶颈 高 QPS 时压测客户端本身可能成为瓶颈。用 ulimit -n 65535 增大文件描述符、用 sysctl -w net.ipv4.ip_local_port_range="1024 65535" 扩大端口范围。

5. 综合负载模式

生产环境中瓶颈往往多个维度叠加。模拟真实场景的复合压测:

#!/bin/bash
# 综合压测脚本:CPU + 内存 + 磁盘 + 网络同时施压

# 后台启动各维度压力
stress-ng --cpu 2 --vm 1 --vm-bytes 512M --hdd 1 & STRESS_PID=$!

# 同时跑 HTTP 压测
wrk -t2 -c20 -d120s http://localhost:3000/

# 停止 stress-ng
kill $STRESS_PID
wait

在综合压测时用 4.6:Linux 性能调优 的方法同时监控所有维度:htop(CPU/内存)、iostat -x 1(磁盘)、ss -s(网络连接)、vmstat 1(系统整体)。

浸泡测试(Soak Test)——查内存与连接泄漏

浸泡测试用中高负载(约为容量上限的 60-80%)持续运行数小时甚至数天,专门暴露短时压测看不到的问题:内存缓慢增长、连接泄漏、临时文件堆积、缓存键膨胀。

# 浸泡测试要点(以 8 小时为例)
# 1. 固定负载:wrk -t4 -c64 -d8h(长跑需关注客户端本身的稳定性)
# 2. 每 15 分钟采集一次进程内存,画趋势线
while true; do
  ps -o rss= -p $(pgrep -f myapp) | awk '{printf "%s %s\n", strftime("%H:%M"), $1/1024}'
  sleep 900
done > /tmp/soak-mem.log
# 3. 判读:内存随时间线性增长且不回落 = 泄漏;
#    内存增长到平台后稳定 = 缓存预热,属正常

# 连接数监测(连接泄漏的典型信号)
ss -s | grep -i estab
# estab 数量随时间单调增长 = 连接泄漏,检查应用是否忘记关闭连接
浸泡测试的时长依据 至少覆盖业务的一个完整周期(如 24 小时业务中的低谷与高峰各一轮),并至少达到 GC/连接池重置周期的 2 倍,否则泄漏积累速度可能不足以被观测到。

6. 压测方法论

压测类型速查

不同目标用不同类型的压测,名称混用是压测报告最常见的低级错误:

类型目的负载形态典型时长
冒烟压测(Smoke)验证链路通、配置对1-5 并发,跑通即止1-5 分钟
基准压测(Baseline)建立对比基线固定并发,固定脚本10-30 分钟
负载压测(Load)验证日常峰值流量下的表现按预估峰值并发施压30-60 分钟
压力压测(Stress)找到容量上限与拐点持续加压到系统过载视拐点出现时间
浸泡测试(Soak)发现内存泄漏、连接泄漏中高负载持续运行数小时到数天
峰值压测(Spike)验证突发流量下的弹性瞬间从低并发跳到 2-3 倍峰值按尖峰窗口

上线前的标准组合是:冒烟 → 基准 → 负载(1 小时)→ 浸泡(至少 8 小时)。大多数团队只做前三种,漏掉浸泡测试——而内存泄漏这类问题恰恰只在长时间运行后暴露。

  1. 基线先行:永远先跑一次空载基线
  2. 单变量原则:每次只改变一个参数
  3. 多次取稳:每个场景跑 3 次取中位数(系统噪音不可消除)
  4. 逐步加压:从低并发逐步增加,记录拐点(系统从线性到饱和的转折点)
  5. 关注尾部延迟:avg 会骗人,p99/p99.9 才是用户体验的关键
  6. 数据持久化:每次压测结果保存到文件,标注环境配置和内核参数

完整流程:从基线到结论

以"给生产前的新版本 API 服务做容量评估"为例,跑通一遍完整流程:

  1. 基线先行:空载确认服务无异常日志、CPU idle 大于 95%,用 curl -w 记录单请求延迟作为 0 并发参考。
  2. 单变量加压:固定请求体、固定脚本,只改变并发数。并发序列取 1、2、4、8、16、32、64、128、256(2 倍递增,每档持续 30 秒)。
  3. 多轮取稳:每个并发档位跑 3 轮,取 QPS 与 P99 的中位数,丢弃明显异常轮次(如恰好赶上 GC 的偶然抖动)。
  4. 记录拐点:把 (并发, QPS) 记成表格,找到 QPS 停止线性增长的并发点。
  5. 交叉验证:在 QPS 拐点处同时看服务端 CPU、内存、网络指标,确认瓶颈在哪一层(应用、DB、网络)。
并发QPS(3 轮中位数)P99 延迟服务端 CPU结论
11808ms2%单请求延迟基线
81,40012ms15%线性增长区间
325,20028ms55%接近线性
646,10096ms88%拐点出现,QPS 增速放缓
1286,300310ms97%平台期,P99 急剧恶化

结论:该服务容量上限约 6,000 QPS / 64 并发。P99 从 32 并发开始快速恶化,说明排队已经形成——生产应把流量控制在拐点(64 并发)的 60-70% 以下,即约 3,500-4,000 QPS,同时配好限流与熔断。

压测记录模板

每次压测至少记录以下信息,保证结果可复现、可对比:

日期:2025-06-15 14:00
工具:wrk -t4 -c64 -d30s --latency
目标:10.0.0.5:8080 /api/v1/ping(版本 v1.2.3)
环境:8C16G / NVMe SSD / 内核 6.1.0 / sysctl: net.core.somaxconn=4096
服务端负载:CPU 平均 55%,无 GC 异常
结果:QPS 6100,P99 96ms,Socket errors: 0
备注:对比 6-12 基线(QPS 5800),提升 5%

7. 结果解读:数据到底说明了什么

QPS 拐点

QPS 随并发增加呈现三段形态:线性区(系统资源充足,QPS 随并发近似线性上升)、拐点(某个资源先到 100%,QPS 增速放缓)、平台区(QPS 不再上升甚至下降——队列堆积导致请求超时重试,进一步恶化)。拐点所在并发数就是"该配置下的容量上限",平台区是过载区,压测时不要长时间停留。

CPU 饱和

CPU 到 95%+ 且 QPS 不再增长,说明瓶颈在 CPU(计算型负载);CPU 远未打满 QPS 却到平台,说明瓶颈在别处——典型情况是锁竞争、连接数耗尽、数据库慢查询、或压测客户端本身到顶。判断方法:用 mpstat -P ALL 1 看单核是否打满(多线程应用单核 100% 说明锁串行化),用 pidstat -t 看哪个线程占 CPU。

延迟分布

延迟看分布不看均值:

  • p50/p75 低、p99 高:多数请求快,少量请求慢——典型的队列效应或 GC 停顿,检查线程池排队与 GC 日志
  • p99 与 p50 接近:服务稳定,没有明显长尾,可以继续加压测试极限
  • p99 随并发急剧恶化:连接池/线程池打满,请求在排队等资源,这是容量信号的第一个预警
  • max 远大于 p99:存在极端偶发(GC、锁等待、网络抖动),结合 top 观察峰值时刻的系统状态
# 典型延迟分布随并发升高的变化(单位 ms)
#        p50  p75  p90  p99   max
# 并发 8:   8   11   14   28    60   ← 稳定区,长尾小
# 并发 32: 15   22   38   96   250   ← 拐点附近,p99 开始爬升
# 并发 128: 45  120  260  310  980   ← 过载区,p99 接近 max
# 解读:p99-p50 的"尾部差"从 20ms 扩大到 265ms,
# 说明排队等待成为主导因素——此时加机器比调代码更有效
解释规则 任何"结果异常"先问三个问题:压测客户端资源够不够?目标服务是不是唯一变因?数据是不是 3 轮以上的稳定值?三者都确认后再下结论。

8. 容量规划:从压测结果推算生产配置

容量规划的核心是利用率换算:压测得到单机容量上限后,按目标峰值流量、安全水位和冗余度反推机器数量。公式:

所需实例数 = 目标峰值 QPS × 冗余系数 ÷ (单机容量 × 安全水位)

# 示例:压测得单机容量 6,000 QPS(P99 < 100ms)
# 业务目标:峰值 25,000 QPS,双活机房各承担 50%
# 安全水位 60%(留出突发与故障转移空间),冗余系数 1.5(一备一用)
所需实例数 = 25,000 × 1.5 ÷ (6,000 × 0.6) ≈ 10.4 → 取 12 台,每机房 6 台
场景安全水位理由
在线业务(秒级响应)40-60%突发流量 + 单实例故障转移
离线批处理70-85%可排队、可重试,容忍高峰
大促前的预留扩容30% 以下确定性高峰前的兜底余量

另外两个必须算的账:增长预算(月均流量增长 20% 时,容量至少要撑到下次扩容窗口)和故障预算(N+1 冗余只能扛一台故障,N+2 才能扛两台——按业务可用性目标决定)。

9. 在 CI 中集成压测

把压测放进流水线,防止"上线后才发现性能退化"。要点:压测环境与生产隔离、结果与基线对比、失败自动阻断。

GitHub Actions:每次 PR 跑 wrk 对比基线

# .github/workflows/perf.yml
name: Performance Regression Check
on:
  pull_request:
    branches: [main]
jobs:
  benchmark:
    runs-on: ubuntu-latest
    services:
      app:
        image: myapp:pr-build
        ports: ["8080:8080"]
    steps:
      - uses: actions/checkout@v4
      - name: Install wrk
        run: sudo apt-get install -y wrk
      - name: Run benchmark
        run: |
          mkdir -p reports
          wrk -t2 -c32 -d30s --latency http://localhost:8080/api/v1/ping \
            > reports/current.txt 2>&1
      - name: Compare with baseline
        run: |
          QPS=$(grep "Requests/sec" reports/current.txt | awk '{print $2}')
          echo "Current QPS: $QPS"
          # baseline 由上次 main 分支构建产物生成,存于 artifact
          if [ "$QPS" -lt "${BASELINE_QPS:-0}" ]; then
            echo "::error::QPS dropped below baseline"
            exit 1
          fi

Jenkins Pipeline:定时回归压测

pipeline {
    agent { label 'perf-host' }
    triggers { cron('0 2 * * 1') }  // 每周一凌晨 2 点
    stages {
        stage('Deploy test env') {
            steps { sh 'docker compose -f perf/docker-compose.yml up -d --build' }
        }
        stage('Run wrk') {
            steps {
                sh 'wrk -t4 -c64 -d60s --latency http://perf-app:8080/ > reports/$(date +%F).txt'
            }
        }
        stage('Alert on regression') {
            steps { sh 'perf/compare.sh reports/$(date +%F).txt' }
        }
    }
    post { always { sh 'docker compose -f perf/docker-compose.yml down' } }
}
CI 压测的坑 共享 Runner 的 CPU 会被其他任务干扰,压测结果波动大。方案:固定专用 runner(标签 perf-host)、每次压测前先跑 CPU 空载校验、阈值放宽到基线的 ±10% 而非 ±2%。

基线管理:让回归有参照物

CI 对比需要一个可靠的基线:把每次 main 分支合入后的压测结果写入仓库的 perf/baseline.json,回归任务只对比"当前 PR"与"最近一次基线",而不是与任意历史值比:

# perf/compare.sh —— 对比当前结果与基线,超标即失败
#!/bin/bash
# 用法:compare.sh 当前结果文件 [基线文件]
set -euo pipefail
CUR="$1"
BASE="${2:-perf/baseline.json}"

cur_qps=$(grep "Requests/sec" "$CUR" | awk '{print $2}')
base_qps=$(python3 -c "import json,sys; print(json.load(open('$BASE'))['qps'])")

# 阈值 ±10%:共享环境波动通常在这个范围内
limit=$(python3 -c "print(int($base_qps * 0.9))")
echo "current=$cur_qps baseline=$base_qps limit=$limit"
if [ "$cur_qps" -lt "$limit" ]; then
  echo "::error::性能回归:QPS $cur_qps 低于基线 90%($limit)"
  exit 1
fi

# 通过则更新基线(仅 main 分支执行,PR 只读不写)
if [ "${GITHUB_REF:-}" = "refs/heads/main" ]; then
  python3 -c "import json; json.dump({'qps': $cur_qps, 'date': '$(date +%F)'}, open('$BASE','w'))"
  git add perf/baseline.json && git commit -m "chore: 更新压测基线" && git push
fi

基线文件要随代码评审:谁改了基线、为什么改(服务重构?换机器?),都要在 commit message 里写清楚,否则基线的置信度会慢慢流失,回归检查形同虚设。

10. 压测工具选型对比

工具目标层特点适合场景
stress-ng系统(CPU/内存)200+ 压测方法,可组合宿主机极限、稳定性测试、散热验证
fio磁盘IO 深度/块大小/读写比例全可配磁盘选型、RAID 配置、文件系统基准
sysbench数据库OLTP 标准负载,可 A/BMySQL/PostgreSQL 配置调优验证
wrkHTTP多线程 + Lua 脚本,高吞吐单接口压力上限、Keep-Alive 长连接压测
heyHTTP单二进制、命令简单快速冒烟压测、CI 轻量回归
abHTTPApache 自带,全平台临时性简单压测(非 Keep-Alive 场景,与 wrk 差距大)
ghzgRPCGo 编写,支持 protobuf 负载微服务 gRPC 接口压测
locustHTTP(分布式)Python 脚本、分布式施压复杂用户行为流、多机联合压测

选择建议:单机快速验证用 wrk;需要模拟真实用户链路(登录→加购→下单)用 locust 写 Python 脚本;要压出数据库瓶颈直接 sysbench;日常巡检把 hey 塞进 CI 脚本最省事。

11. 压测报告模板:把数据变成决策

压测的产出不是一串数字,而是一份能让技术负责人、业务方快速判断"能不能上线、要不要加机器"的文档。先区分两种产出:记录是工具的原始输出,机器可读、可复现;报告是提炼后的结论,人可读、可决策。记录进基线库,报告走评审。

报告骨架

一份合格的压测报告包含以下章节,按读者关心程度从前往后排:

# 压测报告:API 服务 v1.2.3 容量评估
一、结论摘要(给决策者,一页内)
    - 结论:可上线。容量上限 6,000 QPS,建议限流阈值 4,000 QPS
    - 依据:5 轮压测中位数,见第四节
    - 风险:P99 在 64 并发后恶化,建议扩容时同步加缓存

二、测试目标与范围
    - 目的:评估 v1.2.3 较 v1.2.2 的性能回归与容量标定
    - 范围:/api/v1/ping、/api/v1/order 只读路径,排除登录/支付链路

三、环境与基线
    - 硬件:8C16G / NVMe SSD / 内核 6.1.0(与生产同规格)
    - 软件:服务 v1.2.3,JVM 参数 -Xms8G -Xmx8G
    - 基线:v1.2.2 同环境 QPS 5,800 / P99 92ms

四、结果摘要
    - 见正文表:分场景列出 QPS、P99、错误率与瓶颈层

五、瓶颈分析
    - 定位:CPU 88% 打满、锁竞争(mpstat 单核 100% + jstack AQS 排队佐证)
    - 证据:压测期间日志、监控截图、原始输出链接

六、建议与遗留
    - 短期:限流 4,000 QPS 并接入 Prometheus 告警
    - 中期:连接池 200 → 300 后重测验证
    - 遗留:登录链路尚未压测,排期 TICKET-1024

七、附录
    - wrk 参数、3 轮原始数据、基线文件版本号

结果摘要表

场景并发QPSP99错误率结论
回归基准(与 v1.2.2 同参数)646,10096ms0%通过(基线 5,800,+5%)
日常峰值模拟325,20028ms0%通过(低于 SLO 100ms)
压力上限1286,300310ms0.2%过载区,不用于容量标定
浸泡 8 小时646,05098ms0%通过(内存曲线平稳)
峰值尖峰(Spike)4→256 瞬时6,000180ms0.1%有损但可接受,限流兜底生效
决策规则示例 上线阻断条件:QPS 回归超基线 -5%、P99 超过 SLO、浸泡测试内存线性增长。命中任一条件报告结论即为"不通过";其余情况为"有条件通过",必须列出遗留项。

报告误区

  • 只贴原始输出:把 wrk 的 stdout 原样贴进周报,读者要自己翻字段——应该先给结论,原始数据放附录
  • 不写环境:没有硬件/内核/配置信息的 QPS 数字没有任何对比价值,两个月后连自己都说不清当时压的什么
  • 报喜不报忧:只写峰值 QPS,不写拐点前的 P99 恶化——上线的判断依据恰恰是尾部延迟
  • 没有遗留项:一份没有"下步待办"的报告等于告诉读者"压测到此结束",无法驱动后续调优
  • 数据造假式取整:只挑 3 轮里最好的一轮当结果——中位数才是稳定的代表值,单次最好值只说明"能跑到",不说明"稳定在"

归档与频率:重大变更、新版本、扩容前后必须出完整报告;日常每周跑一次基线对比,只更新结果摘要表。报告按 reports/2025-06/ 月度归档,回答"这台机器现在还行不行"时直接翻基线。

常见错误

问题现象解决方案
fio 结果虚高随机读 IOPS 远超硬件标称值测试文件大小未超过物理内存 2 倍,数据全部落在 page cache 中。加 --direct=1 绕过缓存,或增大 --size 使其超过可用内存。
wrk 连接超时高并发下出现大量 Socket errors / timeout压测客户端自身资源耗尽。执行 ulimit -n 65535 增大文件描述符上限,sysctl -w net.ipv4.ip_local_port_range="1024 65535" 扩大临时端口范围。
stress-ng OOM 被杀stress-ng 进程被 OOM killer 终止,dmesg 可见 oom-killer 日志分配的内存超过系统可用量。降低 --vm-bytes,或加 --vm-keep 让 worker 保持已分配内存而非反复申请释放。
sysbench 连接被拒FATAL: unable to connect to MySQL server数据库未启动、端口不对、用户密码错误、或 max_connections 不足。检查 MySQL 状态及 my.cnf 配置。
压测期间系统卡死系统响应极慢甚至无法登录压测负载过高导致系统过载。先用 stress-ng --cpu 1 --timeout 5s 小负载验证工具正常,再逐步加压;提前设置 timeout 或在远程终端预设 kill 命令。
压测结果波动大同一参数多次结果差异超过 20%共享 Runner、超线程、散热降频、cgroup 限制都会引入噪音。固定专用压测机、关闭超线程、每次先跑空载校验、每场景 3 轮取中位数。
P99 与基线差异大但 QPS 相同延迟曲线整体漂移wrk 必须加 --latency 才有百分位输出;时长太短(<30s)时预热期占比过高。统一用 60s + --latency,先跑 10s 预热。
压测机先到顶单机 wrk 压不出目标 QPS,客户端 CPU 先 100%施压能力不足。确认 ulimit 与端口范围后仍不够,就换多机分布式施压(locust 主从模式),客户端 CPU 超过 80% 即视为结果不可信。

最佳实践

  • 压测环境与生产隔离:在独立的测试机器或容器中压测,避免干扰生产流量,也避免生产数据影响测试结果。
  • 先建基线再对比:每次调优前后各跑一次相同参数的压测,用 A/B 对比验证效果,避免"调了感觉快了"的主观判断。
  • 关注尾部延迟而非平均值:avg 会被大量低延迟请求拉低,p99 / p99.9 才反映最慢 1% 用户的真实体验。
  • 记录完整环境信息:每次压测结果保存时标注内核版本、CPU 型号、磁盘型号、内核参数(sysctl)和负载情况,确保结果可复现。
  • 逐步加压找拐点:从低并发开始,按 2 倍递增,观察 QPS 从线性增长转为平台期的拐点——那就是系统的容量上限。
  • 压测也要告警:长时间浸泡测试时把服务端的 CPU/内存/连接数接入 Prometheus 告警,泄漏或 OOM 第一时间被发现,而不是等压测结束翻日志。

练习题

  1. fio 实操:分别测试你机器上 SSD 的顺序读、随机写、70/30 混合读写的 IOPS 和带宽。记录结果并对比参考阈值,判断磁盘性能是否正常。
  2. HTTP 压测对比:用 wrk 和 hey 对同一个本地 Web 服务发起 100 并发压测 30 秒,对比两者的 QPS 和 P99 延迟输出格式有何不同。
  3. 综合压测脚本:编写一个 shell 脚本,同时运行 stress-ng(2 CPU + 1 VM 512M)和 wrk(4 线程 32 连接 60 秒),压测结束后自动打印各维度的监控摘要(用 htop 批处理模式或 awk 解析 iostat 输出)。
  4. 容量规划计算:压测得单机容量 8,000 QPS,业务峰值 40,000 QPS,安全水位 60%,冗余系数 1.5,计算所需实例数并说明如何分摊到两个机房。
  5. 报告撰写:基于本章的完整流程做一次 30 分钟压测,按"11. 压测报告模板"的骨架产出报告(含结论摘要、结果表、瓶颈分析),给出"可上线/有条件通过/不通过"的结论。
  6. 基线回归演练:把 wrk 脚本和 compare.sh 放进 CI,人为改慢一个接口(如加 sleep 50ms),验证流水线能否在 PR 阶段阻断合并。
  7. 报告评审:找同事互换审查上一题的报告,重点核对:环境信息是否完整、结论与数据是否一致、遗留项是否可执行。

学习检查点

学完本章后,请检验自己是否掌握以下内容:

检查项自测问题验证方法
概念理解能用自己的话解释压力测试的目的和关键指标尝试向他人讲解
命令操作能不查文档完成压力测试工具使用、测试脚本编写在终端实际执行
原理掌握能说出压力测试的并发模型和资源消耗原理画出流程图
故障排查能独立排查测试环境问题、测试结果异常、性能瓶颈模拟故障并修复
最佳实践能说明为什么需要为压力测试配置监控和基线对比对比不同方案

本章总结

压测的本质是"用受控的负载验证假设":stress-ng 看系统极限、fio 看磁盘能力、sysbench 看数据库吞吐、wrk/hey 看 Web 服务容量。四类工具对应四层栈,任何一层的瓶颈都会拖垮整体。记住压测的铁律:基线先行、单变量、逐步加压、看 P99 不看均值——没有基线的压测是自嗨,没有复现记录的压测无法指导容量规划。

延伸阅读

↑ 回到顶部