2276 字
约 7 分钟
15
S27-A21 · 实测工具的版本:为什么两次测的数字对不上

同一套压测脚本,同一条 SQL,两个人测出的 P99 差 30%。查了半天发现:一个用压测工具 1.2(默认 keep-alive),另一个用 1.5(默认关闭 keep-alive)——而"连接复用"直接决定了"测的是服务端还是网络"。

实测的可比性不只取决于"机器与数据",还取决于"工具本身":版本、默认参数、计时点、以及它怎么算"完成"。这个"从不被写"有个可理解的原因:工具被认为是"中立的"——它只是"打请求、量时间",不该影响结论。而实际上"它怎么打、怎么量"直接决定了结论指向谁。
而这些在报告里几乎从不被写。


1. 工具的四种"版本差异"

差异 例子
默认参数变化 keep-alive、并发模型
计时口径变化 是否含建连、是否含首字节
统计方式变化 分位算法、是否含失败请求
依赖库变化 TLS 实现、HTTP/2 支持

第一项最常见:工具升级时"默认值"改了,而脚本没写显式参数——于是"同一脚本"跑出不同结果。第一项的"隐蔽"在于"脚本看起来没变":版本一变、默认值一变,而"同一条命令"的含义就变了——于是"两次测试"实际是"两个实验"。

第三项的"额外"问题是"失败请求怎么算":有的工具把超时算进 P99(拉高),有的直接丢弃(拉低)——而"丢弃"会让"系统越差、延迟越好看"。
第三项很隐蔽:"分位数"的算法不同(插值 vs 最近秩),在样本少时差得很多。

2. 五种"必须写进报告"的项

项 为什么
工具名与版本 复现的前提
关键参数(非默认) 影响结果方向
计时点定义 数字含义
失败请求处理 是否计入分位
运行环境(客户端侧) 客户端也可能成瓶颈

第五项常被漏:压测客户端自己也可能是瓶颈——"QPS 上不去"有时是"客户端 CPU 打满"而不是"服务端慢"。第五项的"识别"很直接:客户端 CPU 与 GC 要跟服务端一起看。若客户端 CPU 到 90%,那"吞吐上限"是客户端给的,服务端可能还有余量。

// 一次可复现的报告头(六行)
tool: wrk 4.2.0 (keep-alive on, 4 threads, 100 connections)
timer: 从发出请求到收完响应体(不含 DNS)
failures: 5xx 计入统计,但单独标注
client: 8 vCPU / 同机房(RTT 0.2ms),CPU 峰值 40%
target: 4 vCPU / MySQL 8.0.34
runs: 5 轮 × 60s,报 P50/P99 与吞吐

3. 三种"工具本身成为瓶颈"的信号

信号 含义
客户端 CPU 打满 压不上去
客户端连接数受限 并发上不去
客户端 GC 频繁 延迟被污染

第三项的"污染机制"是"计时在客户端进行":请求发出后客户端自己停顿了,回来时"看起来"服务端用了更久——而服务端的真实耗时没变。
第三项在"JVM 写的压测工具"上常见:压测端自己的 GC 停顿会算进"响应时间"——于是"服务端 P99 变差"其实是"客户端 GC"。

修法很直接:看客户端侧的资源指标。若客户端 CPU/GC 有异常,那这一轮的"服务端数据"不可用。

"计时点"的三种定义,会让同一个数字差出一倍:

// 三种计时点的含义(同一请求)
A 只算服务端处理(不含建连/传输)→ 最快的数,适合"比算法"
B 从发出到收完(含建连、传输)    → 用户感知的数,适合"比体验"
C 含 DNS 与 TLS 握手(冷启动)    → 最慢的数,适合"比首访"
// 报告里必须写清是哪一种;否则"200ms"无法与其他人的"200ms"比较

4. "分位数"的三种算法差异

算法 差异
最近秩(nearest rank) 保守,样本少时偏高
线性插值 平滑,可能"造出"不存在的值
HDR Histogram 高精度,需工具支持

第一项与第二项在"样本少"时差得明显:100 个样本的 P99,两种算法可能差 5–10%。

所以在"对比两次测试"时,必须确认两边用同一种算法——否则"差异"可能只是算法差异。

5. 怎么让"两次测试可比"

做法 效果
固定工具版本(锁文件/镜像) 消除版本差异
显式写全部关键参数 消除默认值差异
固定计时点与统计方式 消除口径差异
同客户端、同机房 消除环境差异

第一项的"附带收益"是"参数也固定":镜像里可以放"脚本 + 参数 + 环境",于是"复现"变成"跑同一个镜像",而不是"照文档敲一遍"。
第一项最有效:把压测工具放进容器镜像并固定版本,于是"谁跑都是同一个工具"。

第二项的"显式"要具体到"连默认值也写":--keepalive 显式打开或关闭,而不是"用默认"。

// 显式参数(不留"默认值"的空间)
wrk -t4 -c100 -d60s --latency -H "Connection: keep-alive" http://target/api
// 对比组:显式关闭 keep-alive(用 --no-keepalive 或改造脚本)
// 关键:两组只差这一个参数

"用同一镜像"是最省心的复现方式,而它需要把"三件事"都放进镜像:

// 压测镜像里应包含(缺一项就可能不可比)
- 工具二进制 + 固定版本
- 脚本 + 全部参数(含"默认值"也显式写)
- 计时点与统计方式的说明(README 里一段)
// 用法:docker run bench:wrk-4.2.0 ...

6. 排错:五种"数字对不上"

现象 成因 处理方向
两人结果差 30% 工具版本/默认值 固定版本 + 显式参数
吞吐上不去 客户端瓶颈 看客户端资源
P99 忽高忽低 分位算法/样本少 固定算法 + 加样本
本地快、CI 慢 客户端环境不同 同镜像/同机房
服务端指标正常但延迟高 客户端 GC 查客户端

第四项在"CI 里跑压测"时常见:CI 的机器弱、网络绕,于是"同一脚本"跑出"更差的数"。第四项的"修法"不是"给 CI 加机器",而是"把压测从 CI 移到专门的压测环境"——因为"CI 的职责是验证功能",而"性能基线"需要稳定环境。

最后一个"验收":用同一镜像、同一参数跑两次,数字应当接近。若不接近,说明"还有未受控的变量"(客户端资源、网络、目标环境)。

// 重复性检查(同一镜像,跑两次)
run1: p99=210ms, tps=1900
run2: p99=214ms, tps=1880   ← 波动 < 3%,可信
run2: p99=380ms, tps=1200   ← 有未受控变量,先查再比
// 判据:自身波动 < 5% 时,"两组对比"才有意义

7. 边界:什么情况不必管工具细节

场景 为什么 做法
数量级差异(10 倍以上) 工具差异影响小 粗测即可
只判断"方向" 不需要精确 写明"粗测"
同一台机器、同一次会话 环境一致 记录时间即可
用同一容器镜像跑 版本一致 记录镜像 tag

最后一条与"基线"有关的边界:性能基线会随工具升级而漂移。所以"工具升级"应当被当成"基线重设"的事件——而不是"顺手升一下"。
判断口径一句话:这个数字"会不会被别人拿去复现"。会,就必须写清"工具与参数";不会(自己看趋势),记录时间点就够。


8. 自测

  1. 工具的四种版本差异里,哪一种最常见?为什么"升级工具"会改变结果?
  2. 报告里必须写的五项里,哪一项最常被漏?为什么"客户端也会成瓶颈"?
  3. "客户端 GC 频繁"为什么会污染服务端数据?
  4. 三种分位算法的差异在哪?样本少时哪一种更保守?
  5. 怎么让"两次测试可比"?为什么"显式写默认值"是必要的?
  6. 判断"是否需要记录工具细节"的那个问题是什么?
S27-A21 · 实测工具的版本:为什么两次测的数字对不上
https://www.clxhxhhr.top/posts/6589/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。