8.2 KiB
REPORT_GUIDE — 压测报告怎么读
目标读者:看完压测报告后,需要判断"系统能扛住吗"+"哪里是瓶颈"+"下一步改什么"的工程师 报告路径:
reports/final-report.md(主) +reports/{scenario}.json(原始) +reports/{scenario}.png(图)
1. 报告目录结构
reports/
├── S1.json # 场景 1 原始数据 (程序读)
├── S2.json # 场景 2
├── S4.json # 场景 4
├── baseline.csv # Excel 可打开的汇总表
├── s1.png # 场景 1 曲线图 (RPS / P99 / Error)
├── s2.png
├── s4.png
└── final-report.md # ← 你要看的总报告
2. 三步读完报告
第 1 步:看汇总表 (1 分钟)
| Scenario | Total | Err | 5xx | P50ms | P95ms | P99ms | Maxms | Stages |
|----------|-------|-----|-----|-------|-------|-------|-------|--------|
| S1 | 12500 | 0 | 0 | 86.59 | 119.23 | 200.50 | 450 | 5 |
| S2 | 25000 | 5 | 0 | 12.30 | 35.00 | 88.00 | 250 | 5 |
| S4 | 600 | 12 | 2 | 200.00 | 500.00 | 850.00 | 1200 | 4 |
每个字段的含义:
| 字段 | 含义 | 健康参考 (4G/2C prod) |
|---|---|---|
Scenario |
场景 ID (S1=登录, S2=读, S3=点赞, S4=铸造, ...) | — |
Total |
该场景总请求数 | 越大越好,代表你扛住了 |
Err |
客户端+服务端错误总和 | < 1% |
5xx |
服务端错误 (500-599) | < 0.1% (1‰) |
P50ms |
50% 请求在这个时间内 | < 100ms |
P95ms |
95% 请求在这个时间内 | < 300ms |
P99ms |
99% 请求在这个时间内 | < 1000ms (S4 写重可放宽到 2000ms) |
Maxms |
最慢的一次请求 | 一般 3-5x P99 |
Stages |
阶梯测试的阶段数 | = step-schedule 的元素数 |
判断模板:
- ✅ 全绿 → 系统扛得住,准备上线
- ⚠️ 某个 S* Err > 1% → 优先看那个场景
- 🚨 某个 S* 5xx > 1% → 服务端有问题,看 §3 定位
第 2 步:看拐点 (KneeRPS) (2 分钟)
每个 scenario 标题下会出现一行:
**⚠️ 拐点**: stage 3 @ 3 RPS (p99 暴涨 514%)
含义: 当 RPS 升到 3 时,p99 延迟比 stage 2 暴涨 514% (5.14 倍)。
判定逻辑 (在 reporter/knee.go):
- 逐 stage 比 p99
- 第一次涨幅 > 50% 时,标记为拐点
- 全程没涨 > 50% → 显示 "✅ 拐点未触发"
怎么用这个数字:
- S1 拐点 RPS = 15 → 你的登录服务,超过 15 QPS 就开始劣化。生产预估峰值 10 QPS,留 50% buffer
- S4 拐点 RPS = 2 → 铸造接口很重,2 QPS 就劣化了。要么优化,要么限流
举例:
| 拐点 RPS | 业务含义 | 行动项 |
|---|---|---|
| ≥ 期望峰值的 2x | ✅ 健康 | 上线,加监控 |
| ≈ 期望峰值 | ⚠️ 临界 | 加缓存 / 异步化 / 限流 |
| < 期望峰值 | 🚨 不达标 | 重构 + 复测 |
第 3 步:看阶梯表 + 曲线图 (5 分钟)
阶梯表 (md 里每个场景下):
### 阶梯结果
| Stage | TargetRPS | Total | Err | 5xx | P50ms | P95ms | P99ms | Maxms |
|-------|-----------|-------|-----|-----|-------|-------|-------|-------|
| 1 | 2 | 600 | 0 | 0 | 80 | 100 | 110 | 130 |
| 2 | 5 | 1500 | 0 | 0 | 82 | 105 | 115 | 140 |
| 3 | 10 | 3000 | 0 | 0 | 85 | 110 | 130 | 180 |
| 4 | 15 | 4500 | 0 | 0 | 95 | 130 | 200 | 350 |
| 5 | 20 | 6000 | 5 | 0 | 120 | 200 | 450 | 800 |
怎么读:
- Total 应该是
TargetRPS × Duration(近似,因为有误差) - P99ms 应该随 TargetRPS 上升平滑增加 (10-30% 涨幅/stage 是正常)
- Err / 5xx 应该全程 < 1%
- 如果某 stage 突然 P99 翻倍 → 拐点,看上面 KneeRPS
曲线图 (s1.png 等):
- X 轴: Stage 编号 (1, 2, 3, ...)
- Y 轴: 三个值 — RPS (蓝)、P99ms (绿)、Error% (红)
- 怎么看:
- 三条线平稳上升 = 正常
- P99 突然陡升 = 拐点
- Error% 突然跳起来 = 服务挂了
3. 定位瓶颈 — 常见模式
模式 1: P99 阶梯上升,但 Error 一直 0
含义: 系统扛得住,但在变慢。 原因: GC 抖动 / DB 慢查询 / 锁竞争。 行动:
- 看 PG 慢查询日志:
pg_stat_statementsORDER BYmean_exec_timeDESC - 看应用层 profile:
pprofheap + cpu - 检查连接池配置: 可能太小
模式 2: P99 阶梯上升 + Error 也开始涨
含义: 系统到极限。 原因: 资源耗尽 (CPU 100%, 连接池满, DB 锁)。 行动:
- 看 server metrics feed:
tail -f metrics-feed.jsonl top看 CPU/内存,iostat看 IO- 检查是否有连接泄漏 (
netstat | grep TIME_WAIT)
模式 3: 阶梯早期就 5xx > 5%
含义: 系统本身有问题,不是负载问题。 原因: 代码 bug / 配置错误 / 依赖缺失。 行动:
- 看 5xx 的具体响应体 (在 log 里)
- 检查 error 码,对照业务错误码定义
- 看是不是 auth/JWT 过期
模式 4: 第一个 stage P99 很高,后续反而低
含义: 热身不够 / 缓存没预热。 原因: Redis 冷启动 / JIT 编译 / DB 连接池启动慢。 行动:
- 第一次 stage 加长 (例如先 2min 预热)
- 或者用
--rps=1先跑 1-2min 预热,再开阶梯
模式 5: S4 (Mint) 在很低的 RPS 就拐
含义: 写路径太重。 原因: 铸造涉及事务 / 签名 / OSS 上传,本身就是慢操作。 行动:
- 检查 mint 是不是同步阻塞 (能不能异步化?)
- 看 mint 数据是否需要落库 (能否用 append-only?)
- 考虑限流: 服务端拒绝 > 2 QPS 的 mint 请求
4. 怎么写出行动项
读完报告,应该能回答三个问题:
Q1: 系统能扛住业务预期峰值吗?
- 业务预期峰值 → 比对拐点 RPS
- 拐点 ≥ 2x 峰值 → ✅ 可以上线
- 拐点 ≈ 1x 峰值 → ⚠️ 加监控告警,谨慎上线
- 拐点 < 峰值 → 🚨 必须先优化
Q2: 拐点在哪里?为什么?
看哪个 stage 拐的,然后:
- CPU 100% → 计算密集,优化算法或加机器
- DB CPU 100% → 慢查询,加索引或读写分离
- PG 连接数满 → 连接池配置 / 服务降级
- PG 锁等待 → 事务设计问题
- 磁盘 IO 满 → 加 SSD 或缓存
Q3: 下一步改什么?
行动项模板:
## [Loadtest 2026-06-15] 行动项
### P0 (上线前必修)
- [ ] **S2 Read 拐点 100 RPS < 业务预期 150 RPS**
- 根因: PG `assets` 表全表扫描,10 万行
- 修复: 加 `idx_assets_star_id_status` 索引
- Owner: @dba
### P1 (1 周内修)
- [ ] **S4 Mint 拐点 2 RPS**
- 根因: 同步写 OSS + 同步落库
- 修复: mint 流程拆成 precreate + 后台 worker
- Owner: @backend
### P2 (技术债)
- [ ] 压测期间 CPU 持续 80%,考虑扩容到 4C
5. JSON 原始数据怎么读 (高级)
reports/S1.json 长这样:
{
"scenario": "S1",
"total_requests": 12500,
"errors": 5,
"five_xx": 0,
"p50_us": 86591,
"p95_us": 119231,
"p99_us": 200502,
"max_us": 450000,
"stages": [
{
"stage_idx": 1,
"target_rps": 2,
"total_requests": 600,
"errors": 0,
"five_xx": 0,
"p50_us": 80000,
"p95_us": 100000,
"p99_us": 110000,
"max_us": 130000
},
...
]
}
单位说明:
- 所有
_us后缀 = microseconds (微秒,1ms = 1000us) - 例:
p99_us: 200502= 200.5 ms
怎么用:
- 画自己的图 (用 Excel/Google Sheets 打开 baseline.csv 最方便)
- 跟历史报告对比 (跨版本性能回归)
- CI 集成: 解析 JSON,断言 P99 < 某个阈值
6. 常见问题
Q: "5xx=0 但 Err=5" 是什么意思?
A: 5xx 是服务端错,Err 是总错 (含 4xx)。Err > 5xx 表示有客户端错 (一般是 401/403/404)。看 log 里具体错误码。
Q: 为什么 P50 很低但 P99 很高?
A: 正常 — 长尾效应。99% 都快但 1% 慢。如果 P99 太高说明有少数请求卡住,看是不是 GC / 锁 / IO 抖动。
Q: Max 比 P99 高很多,是不是异常?
A: 可能是单个网络抖动,正常。Max / P99 < 5x 都是健康。
Q: 同一个场景不同次跑,数据差很多?
A: 检查 prod 是否有其他流量在跑 (业务)。压测应在凌晨,业务低峰。
7. 进一步
- 想优化场景,见
seed/README.md - 想加新场景,在
scenarios/新建s8_xxx.go,模仿 s1_login.go 的 BeginStage/EndStage 模式 - 想加新的红线指标,见
lib/circuit.go