真实负载指还原生产环境中用户行为、数据分布和依赖响应,需基于APM日志分析RPS占比与耗时,模拟思考时间、冷启动及数据生命周期,并同步监控JVM GC、线程与对象状态。

压测前必须明确“真实负载”具体指什么
很多人一上来就写JMX脚本,结果压出来的数据和线上毛关系没有。真实负载不是“并发数越多越好”,而是要还原生产环境里用户怎么用、数据怎么分布、依赖怎么响应。比如电商下单接口,线上可能是60%查用户、30%扣库存、10%发MQ,但压测脚本只狂刷下单,那根本测不出DB连接池或Redis限流的真实瓶颈。
实操建议:
- 从APM或网关日志里抽样至少24小时的真实调用链,统计各接口的RPS占比、平均响应时间、错误码分布
- 用
wrk或curl手动模拟一次典型请求链路,记录每个环节耗时(如Nginx → 网关 → 服务A → DB → 服务B),避免把网络延迟全算在应用头上 - 特别注意冷启动场景:首次请求是否触发大量类加载或Spring Bean初始化?用
jstat -class $PID看加载类数量变化
JMeter线程组配置必须匹配业务节奏
把线程数设成500、循环次数设为1,等于让500个用户同时点“提交订单”按钮——这根本不是用户行为,是DoS攻击。真实用户有思考时间、有失败重试、有页面跳转间隔。
实操建议:
- 用
Constant Throughput Timer代替固定线程数,按目标TPS反推并发量,更贴近流量曲线 - 给每个请求加
Uniform Random Timer(比如1000–3000ms),模拟用户操作间隙 - 用
__machineIP()函数区分不同负载机行为,避免所有机器同一秒发起数据库查询导致锁竞争 - 禁用
HTTP Cache Manager,否则本地缓存会掩盖CDN或代理层问题
数据准备不能只靠CSV,得考虑生命周期和一致性
压测用的用户ID如果全是静态的1–1000,那缓存命中率虚高,DB索引也跑不起来;如果每次压测都生成新数据又不清理,几天后磁盘爆满、慢查询飙升。
实操建议:
- CSV文件必须放在NFS或共享存储上,确保所有负载机能读到同一份数据(JMeter分布式模式下,本地路径不互通)
- 用
__counter()+__machineIP()生成全局唯一ID,避免不同机器写入相同主键报错 - 提前在DB里预热热点数据(如TOP 100商品),否则首次查询会触发全表扫描+缓存填充,拉高首分钟延迟
- 对写操作,压测脚本末尾加清理逻辑(如
DELETE FROM order WHERE create_time > ?),或直接用临时表
JVM监控指标必须和压测过程绑定采样
压测跑完只看JMeter聚合报告,等于蒙眼开车。你看到TP99从200ms涨到800ms,但不知道是GC停顿导致的,还是线程阻塞,还是JNI调用卡住。
实操建议:
- 压测开始前执行
jstat -gc $PID 1000持续输出,重点关注YGC次数突增和GCT(总GC耗时)是否超过5% - 用
jstack $PID > jstack.log在延迟飙升瞬间抓线程快照,搜索WAITING或BLOCKED状态线程堆栈 - 配合
jmap -histo $PID定期采集对象分布,看是否有某类对象(如StringBuilder或byte[])持续增长 - 别信“默认参数最稳”——G1GC在高吞吐场景下,
-XX:MaxGCPauseMillis=200可能比默认50ms更稳,因为避免了频繁GC
真实负载压测最难的不是跑起来,而是把“用户行为→系统资源→JVM状态”这条链路每一环都对齐。少一个环节,优化方向就可能完全跑偏。


















