ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

JMeter性能测试全链路:造数据、方案、监控与易错点

JMeter性能测试全链路:造数据、方案、监控与易错点

简介:《性能测试-Jmeter.pptx》是一份面向测试工程师与性能测试初学者的培训课件,围绕JMeter在项目中的完整落地流程展开,适合需要系统梳理性能测试方法或补齐实战环节的读者。课件共1个pptx文件,压缩包约12.48MB,以图文和案例演示为主,将数据制造、方案设计、脚本执行、监控调优与报告编写串联为可操作的实践链路。已有1150人学习,在性能测试入门与内部培训场景中具有一定参考价值。内容不止于工具操作:造数据部分对比JMeter参数化脚本与存储过程、Java代码两条路径的效率及适用条件;方案设计强调性能需求规格与环境软硬件清单;脚本执行涉及JMeter 5.1.1、多线程循环和Jenkins持续运行;监控调优覆盖CPU、内存、磁盘I/O、响应时间与事务成功率,并提及AWR、慢日志、ServerAgent等观察手段;最后落到报告结构与瓶颈分析。读者可据此建立较完整的性能测试知识框架,并对照案例复盘自己的测试流程。

1. 性能测试的全链路地图:五个环节与一个最容易被低估的起点

性能测试这条线,说窄很窄——一个 JMeter 脚本、一份方案、几张监控图;说宽也宽,从造数据到出报告,中间任何一环断了,前面的结论就站不住。这份培训笔记把整条链路拆成五段:造数据、设计性能测试方案、执行脚本、监控调优、写报告。很多人上手就奔着“调线程数、看吞吐”去,真正卡住进度的往往是造数据:100 万条铺底数据怎么在一晚上灌进去,是用 JMeter 模拟 HTTP 请求一条条打,还是写存储过程直插数据库,选错了要么慢到天亮,要么脏数据返工。

这张地图适合已经会用 JMeter 发请求、但还没独立承担过一次完整压测的人,也适合把零散知识点串成一条线来复习的人。梳理下来会发现,五个环节里真正决定报告可信度的,不是工具用得多熟,而是数据是否真实、环境是否列清、指标口径是否说死、监控时间轴是否对齐。这四件事在教程里通常一句话带过,在实战里却是血泪经验。

2. 造数据:100 万条铺底数据的两条路线与五个步骤

造数据是压测开始前最容易被低估的一步。它不是“随便往库里插点数据”,而是要让数据库具备接近生产的体量:表够大、索引有深度、统计信息不失真、分页查询不会因为只有几百行而秒回。数据造得不对,后面所有响应时间都是假的。这一章先把数据分类和两条技术路线讲清楚,再落到具体步骤和参数。

2.1 先分清三类数据,再决定用什么工具

执行负载压力测试前需要准备的数据,按用途分三类:初始化数据是系统能跑起来的基础数据,比如字典项、科室、用户、权限;铺底数据是历史数据,用来把表撑到真实量级,让索引、执行计划、分页逻辑都按生产规模工作;参数化数据是每次请求都要变化的字段,比如登记号、姓名、证件号、时间戳。三类数据的目标不同,造法也不同——初始化数据靠手工或导入,铺底数据靠批量脚本,参数化数据靠 JMeter 的 CSV 或函数。

以一个影像登记类系统为例,新建登记模块需要 100 万条,登记保存模块也需要 100 万条,涉及登记主表、患者就诊表等多张表。两种造法各有明确的适用边界:

维度JMeter 参数化脚本存储过程 / Java 代码
实现原理模拟 HTTP 请求,走完整业务链路写库绕过应用层,直接 INSERT
速度约 2-5 万条/小时约 10-20 万条/小时
数据真实性高,字段值由业务逻辑生成取决于 SQL 是否还原了全部关联
前置成本抓包 + 参数化,一天内可开工必须理清表关系、约束、序列、外键
主要风险慢,长时间占用压测机SQL 写错产生脏数据,往往要清库重来
适用场景表结构不熟、时间充裕、要求数据真实表结构清楚、数据量大、追求速度

选择第一种的典型理由是:不熟悉业务库的表间关系,而排期又留出了足够时间。这个理由很实在——速度是可以等的,脏数据是等不回来的。一个判断题:如果 100 万条按 3 万条/小时算,需要连续跑 30 多个小时,这个数字必须提前写进排期,而不是开工后才发现。

2.2 五步流程:从抓包到 20 线程 × 50000 次

确定走 HTTP 脚本路线后,步骤是固定的五步,每一步都有明确的通过标准。

第一步,用抓包工具抓出“登记保存”接口的完整参数信息,包括 URL、请求头、表单字段、Cookie 或 Token。第二步,对可变字段做参数化,用 CSV Data Set Config 喂唯一值,用函数助手生成姓名、证件号、时间这类数据。第三步,先调试:设 10 个线程各跑一次,看响应是否全部成功、库里是否真的多出 10 条、字段值是否符合业务格式。第四步确认无误后,才开始批量造。第五步按总量倒推线程与循环:100 万条 = 20 线程 × 50000 次循环。

# 非 GUI 模式运行造数据脚本,参数从命令行覆盖,便于在服务器上长时间跑 jmeter -n -t reg_data.jmx \ -Jthreads=20 \ # 线程数:并发写入的模拟用户数 -Jloops=50000 \ # 每个线程循环次数,总条数 ≈ threads × loops -l result_$(date +%m%d_%H%M).jtl \ -e -o report_dir

这段命令的关键在于把线程数和循环数做成可传参数,而不是写死在 jmx 里。总条数的算法是线程数 × 循环次数 × 单请求条数;如果接口一次能提交多条,单次请求就能放大几倍产出。运行方式上,长时间造数据建议丢到 Jenkins 上持续跑,因为本地 PC 要连续几天不关机,中途休眠、断网、系统更新都可能让前功尽弃。JMeter 版本上,客户端与 Jenkins 上部署的版本保持一致(例如都用 5.1.1),避免 jmx 文件在高版本里打开后元件行为变化。

提示:造完数据不要立刻开压,先做两件收尾——用 count 核对目标表条数、抽样检查主子表关联是否完整;Oracle 上补跑一次统计信息收集,否则执行计划仍按旧数据量走,压出来的响应时间没有参考价值。

2.3 存储过程路线:快十倍的代价是理清表关系

第二种方式是写存储过程或 Java 代码直接插库。它快,是因为跳过了 HTTP 解析、业务校验、事务提交这些环节,纯做 INSERT。代价是必须把表之间的关系画清楚:主表插入后要拿到主键去插子表,序列和外键要提前处理,非空字段、唯一约束、枚举值都要和生产保持一致。

-- 批量插入审计类数据:循环 300 次为一批,commit 放在循环外减少日志刷盘 create or replace procedure T_AUDIT_INSERT_sp as begin for i in 1 .. 300 loop insert into t_audit_new( FID, VERSION, BIZID, REQID, SOURCESYS, TARGETSYS, RESULTCODE, BODY, CREATETIME ) values( sys_guid(), -- 主键,用 GUID 避免手工维护序列 '1.0', '4aa066b7-1660-4ae7-b84c-98eaea24a3ce', -- 业务单号,需保证格式合法 '63528dad-4489-af6f-4ab05be641b4', 'SRC', 'TGT', 0, -- 结果码,按真实取值范围填 '{"code":"12345678","msgId":"12345678:abort"}', -- JSON 体注意转义与长度 sysdate -- 时间字段用当前时间,便于后续按时间查 ); end loop; commit; end;

参数上有几个容易翻车的地方:sys_guid()生成的主键长度必须和字段定义一致;JSON 体里的引号在 Oracle 字符串中不需要额外转义,但长度不能超过字段上限;循环次数决定单次执行插入多少行,300 到 1000 都可以,太大反而让回滚段压力集中。最大的风险不在语法,在语义——SQL 写错不会报错,只会安静地插入一批脏数据,等压测跑出漂亮曲线之后才发现数据是错的,返工成本比一开始慢一点高得多。所以这条路线的正确姿势是:先在测试库小批量跑 1000 条,用业务 SQL 反查一次关联完整性,再放大批量。

3. 性能测试方案:需求规格、环境清单与指标口径

方案是整个压测的契约,写清楚“压什么、压到多少、在什么环境压、怎么算通过”。方案写虚了,执行阶段就会不停返工;方案写实了,报告里的每个数字都能追到出处。这一章的四个部分对应方案里最容易写空的地方。

3.1 需求规格要问到「端到端还是服务端」

性能测试需求规格说明书一般由项目组提供,里面至少要有四类数字:并发用户数、事务响应时间、事务成功率、持续时间。看起来简单,实际需要追问的细节很多:响应时间是端到端(含网络与前端渲染)还是服务端处理时间;并发是线程数还是在线用户数;成功率按请求算还是按事务算;持续时间是 30 分钟峰值还是 8 小时稳定性。这些口径不提前说死,报告里的“平均响应时间 800ms”就没法被认可。

一个实用的问法是让对方给出一条完整业务链路的期望值,比如“登记保存在 50 并发下 90% 响应时间不超过 2 秒,成功率 100%,连续跑 2 小时不出现错误率上升”。这句话同时锁定了并发、分位值、成功率和时长,比“系统要支持 50 并发”有用得多。需求里没给分位值时,默认在报告里同时给平均、90%、最大三个值。

3.2 环境清单:三台机器的硬件与软件两套表

测试环境必须列清楚,而且要硬件、软件分两张表,因为排障时首先要排除的就是环境差异。下面是一份可直接套用的模板(IP 用示例网段):

服务器类型IPCPU内存硬盘操作系统
测试客户端10.0.0.1116 核8G500GWindows 10
应用服务器10.0.0.1216 核64G500GWindows Server 2012 R2
数据库服务器10.0.0.1316 核64G500GWindows Server 2012 R2
服务器类型IP软件配置
测试客户端10.0.0.11JMeter 5.1.1 及 ServerAgent 客户端
应用服务器10.0.0.12JDK 1.8、Tomcat 8.0、Nginx 1.10
数据库服务器10.0.0.13Oracle 19c

重点不在表格好不好看,在于三个信息不能缺:压测机配置(它自己会不会先成为瓶颈)、中间件与 JDK 版本(不同版本的默认线程池和 GC 参数差异很大)、数据库版本(11g 和 19c 的 AWR 脚本行为不同,后面会讲)。客户端只有 8G 内存却要开 500 线程,这件事本身就要在方案里写清楚,否则结果会被质疑。

3.3 指标口径与采集方式对照

方案里还要说明每个指标用什么方式采集。这张表是执行阶段的作业指导书:

指标名称参考说明监测方式
数据库 Top SQL查看耗时或资源消耗最高的 SQLAWR
系统整体处理情况数据库整体负载与等待事件AWR、ADDM
用户数线程组中的线程数JMeter
事务响应时间每笔事务的平均响应时间变化JMeter
事务成功率错误率为 0 说明成功率 100%JMeter
CPU 使用率服务端 CPU 占用情况JMeter + ServerAgent
内存使用率服务端内存占用情况JMeter + ServerAgent

之所以要把“用什么采”写进方案,是因为同一指标不同工具的口径不同:JMeter 的事务响应时间含网络往返,服务端日志里的耗时不含;ServerAgent 采的 CPU 是整机使用率,而容器或 JVM 视角看到的只是一部分。方案里写清楚来源,报告里才不会出现两个对不上的数字。

3.4 场景设计:从单接口基准到混合比例

场景设计要回答“怎么压”。常见的四类场景按顺序递进:基准测试用单接口、低并发跑通链路,拿到一个干净的性能基线;单接口负载逐步加压到目标并发,观察拐点;混合业务场景按生产比例分配各接口的线程数,比如登记占 60%、查询占 30%、保存占 10%,这才能反映真实压力;稳定性测试在目标压力的 70%-80% 下连续跑数小时,看内存、连接池、错误率是否随时间劣化。

梳理下来会发现,混合场景最容易设计错的地方是比例来源——拍脑袋给比例,压出来的瓶颈和线上完全不是一回事。正确做法是从生产日志或监控里统计一段时间内各接口的调用占比,按比例换算成线程数,并在方案里附上统计口径和时间窗。

4. 脚本执行:控制器、集合点与三层监控工具链

脚本执行不是点一下启动就完事,它包含三件事:脚本结构要能被别人看懂、执行参数要能复现、服务端要有对应的监控。这一章按脚本骨架、结果查看、三层监控(系统层、JVM 层、数据库层)展开。

4.1 脚本骨架:用例命名、两种控制器与集合点

命名按用例形式来,名字里带上模块、场景、并发数和时长,例如登记保存_混合场景_50并发_2h。这样聚合报告、结果文件、报告截图三者能对上号,写报告时不会拿错数据。请求的组织方式看业务语义:多个请求之间同步进行、互不依赖,用简单控制器分组即可;请求之间有先后顺序、需要合并成一个业务事务来统计,用事务控制器,并勾选生成父样本,让整组请求的耗时算作一个事务的响应时间。

并发真实性靠同步定时器,也就是集合点的概念,放在请求执行之前。它的作用是让线程到达后先等待,攒够指定数量再一起释放。这里有个必须设的参数:超时时间。设成 0 表示无限等待,一旦线程数凑不齐,压测机会一直挂着;一般设 5000 到 10000 毫秒,超时后不再等待直接释放,同时能在结果里看到超时提示。

4.2 聚合报告怎么看:别把请求吞吐当业务 TPS

聚合报告要加,但要看懂。Average 是平均响应时间,容易被少量慢请求拉高或被大量快请求拉低;Median 是中位数;90% Line 表示 90% 的请求在这条线以下,它比平均值更能反映大多数用户的体验;Error% 是错误率;Throughput 是吞吐量。关键陷阱是吞吐量的单位:聚合报告统计的是请求数/秒,一个事务包含 5 个请求时,请求吞吐是事务 TPS 的 5 倍。要拿业务 TPS,必须看事务控制器那一层的数据,或者在报告里明确写出换算关系。

执行时还有两个细节:循环次数和调度器不要混用,压测场景通常设“永远循环 + 调度器持续时间”,保证各场景时长一致、结果可比;每跑完一个场景就记录一次结果(截图 + jtl 文件 + 关键指标表格),不要等全部跑完再回头找,否则监控曲线和场景对不上。

4.3 ServerAgent 与 PerfMon:4444 端口是第一个要验的

服务端资源监控靠 JMeter 的 ServerAgent。在服务器上启动代理:Windows 双击startAgent.bat,Linux 执行startAgent.sh,默认监听 4444 端口。然后在 JMeter 端通过插件管理器安装 PerfMon Metrics Collector 监听器,填上服务器 IP、端口和要采集的指标(CPU、内存、磁盘 I/O、网络)。

# 在压测机上验证到服务端 4444 端口是否通,先排除网络问题再谈监控 nc -zv 10.0.0.13 4444 # 若不通,回服务端确认 agent 是否在跑、防火墙是否放行

端口不通是最常见的翻车点,而且报错往往只有一行连接失败,看不出原因。排查顺序是:agent 进程在不在、端口有没有被防火墙拦、压测机和服务端是否在同一网段。如果确实无法打通,方案里要写明退路——用 nmon、top、vmstat 这类系统自带工具在服务端本地采样,事后与 JMeter 结果按时间对齐。

4.4 jconsole:内存看回落,线程看停顿

Web 容器内部的情况要靠 JVM 工具,jconsole 是基于 JMX 的可视化监视与管理工具。使用前需要在服务端开启远程端口,本地输入IP:端口连接。它主要看两个页签。

内存页签相当于可视化的 jstat,用于观察收集器管理的虚拟机内存。判断标准不是“用了多少”,而是每次 Full GC 之后占用能不能回到原来的水平,以及压力撤除一段时间后是否回落。如果曲线呈阶梯状一路上移、GC 后也回不去,基本可以判定存在内存泄漏。

线程页签相当于可视化的 jstack,遇到线程停顿就来这里看。线程长时间停顿的典型原因有三类:等待外部资源(数据库连接、网络资源、设备资源)、死循环、锁等待(活锁或死锁)。点开具体线程能看到堆栈,堆栈停在哪个方法,瓶颈方向就在哪。

4.5 AWR 快照:场景边界要手工打点

数据库层的监控靠 AWR。默认快照频率是 1 小时一次,问题是压测场景往往只有十几分钟,默认快照会把几个场景糊在一起,报告里全是平均值,看不出单个场景的拐点。所以场景复杂时,要在每个场景开始和结束时手工打一次快照。

-- 场景开始前执行,标记起点 exec DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT(); -- 场景结束后再执行一次,然后生成报告 @?/RDBMS/ADMIN/awrrpt.sql -- ? 代表 ORACLE_HOME

生成报告时按快照区间选,就能得到干净的单场景数据。还有一个版本差异要注意:Oracle 11g 想拿到数据库自己给出的 SQL 优化建议,需要额外执行ADDMrpt.sql;19c 已经把两个脚本合并,只跑 awrrpt 即可。如果场景很简单、全程只有一个,也可以等所有场景跑完再统一生成报告,但要在方案里写明采用哪种方式。

5. 易错点:读监控数据时最容易翻车的五个地方

工具会用不等于数据会读。同一个曲线,读错方向就会把结论带偏,甚至得出“系统没问题”的错误判断。这一章五条都是执行阶段高频出现的判断失误,每条按“漏在哪、为什么漏、怎么补”说清楚。

5.1 日志只搜 ERROR,漏掉连接类和内存类关键字

易漏点:排查时习惯性只过滤 ERROR 级别,结果日志里干干净净,就下结论“没有异常”。实际上很多瓶颈根本不写在 ERROR 里。

为什么漏:连接被拒绝、内存分配失败这类问题,往往以 WARN 或独立的异常栈出现;而且大用户量、大并发下的功能问题,恰恰只在压力上来时才在日志里留下痕迹,压力一撤就消失。另外日志分布在客户端和服务端两处,只翻一处等于只看了一半。

怎么补:把检索关键字固定成一组,每次压测都搜一遍:OutOfMemoryError与Cannot allocate memory表示虚拟内存异常,要关注是否有溢出;SQLException表示数据库语句执行异常,通常后面跟着数据库返回的具体信息;Connection closed与connection refused表示连接被关闭或拒绝,一般是连接数上限撑不住当前压力。这三类关键字分别指向内存、SQL、连接池三个方向,比单向搜 ERROR 有效得多。

5.2 CPU 到 80% 就下结论说瓶颈在 CPU

易漏点:看到服务端 CPU 长期 80% 以上,直接写“CPU 瓶颈,建议扩容”。

为什么漏:CPU 占用高本身不一定是问题,实现最优性能的一方面就是充分利用硬件资源。真正要区分的是 CPU 消耗在哪里。如果消耗在 User,说明业务计算密集;消耗在 Sys,说明系统调用频繁,可能是上下文切换或 I/O 密集;如果是 Wait,那 CPU 其实在等别的资源,这时继续盯 CPU 就是看错了方向。

怎么补:先看三态分布再下结论。遇到 Wait 偏高,要顺着看磁盘 I/O 和其他硬件资源,典型的例子是大表全表扫描没有走索引,CPU 长时间 80% 以上处于 wait 状态,这时候该做的是补索引或改 SQL,而不是加 CPU。同理,Sys 偏高时先看线程数和连接池配置,别急着换机器。

5.3 内存曲线没回落就判内存泄漏

易漏点:压测过程中内存一路上涨,压力撤除后过几分钟还没降下来,就写“存在内存泄漏”。

为什么漏:JVM 的内存释放依赖 GC 时机,压力刚撤时堆里还留着大量待回收对象,短时间不回落是正常现象。真正的判断依据是 Full GC 之后的水位,以及观察窗口是否足够长。只看“当前占用”这个瞬时值,结论一定是错的。

怎么补:把判断分成两步。第一步看每次 Full GC 后的占用是否回到基线,如果每次 GC 后水位都比上一次高,阶梯状上移,这是强信号;第二步在压力撤除后保留足够长的观察窗,看内存是否最终恢复。两条都满足才写“疑似泄漏”,并在报告里附上内存曲线截图和 GC 次数,让别人能复核你的判断。

5.4 AWR 只取默认快照,场景边界糊成一片

易漏点:全程不手工打快照,最后直接生成 AWR 报告,发现 Top SQL 里混着造数据、调试、正式压测三个阶段的语句。

为什么漏:默认 1 小时一次的快照粒度对十几分钟的场景来说太粗,而且造数据阶段本身就会产生大量 INSERT,这些语句会污染 Top SQL 排序。等发现数据不对时,快照区间已经过去了,没法重来。

怎么补:把“场景前后各打一次快照”写进执行清单,和启动脚本放在同一个步骤里。生成报告时按场景对应的快照 ID 区间取,造数据阶段单独取一段做对照。这个动作每次只花几秒,但决定了数据库层的数据能不能用。

5.5 压测机、服务端、数据库三块时间没对齐

易漏点:报告里写“14:20 出现响应时间尖刺”,配的服务端 CPU 曲线却在 14:35 才冲高,读者一看就质疑数据真实性。

为什么漏:JMeter 结果时间戳用的是压测机本地时间,ServerAgent 采的是服务端时间,AWR 快照用的是数据库时间,三台机器时区或时钟不同步时,曲线天然错位。压测时大家盯着各自的屏幕,很少有人先去核时间。

怎么补:压测开始前统一三台机器的时钟源,或者至少在开始和结束各记录一次三边时间的偏差值,写进结果记录里。分析阶段以压测机时间为基准做平移对齐,报告里注明偏移量。这个动作看起来琐碎,却是让报告站得住脚的关键——曲线对不上,后面的分析再精彩也没人信。

6. 复习清单:一张自查表与四问收尾

把前面五章压缩成一张可以随身带的表。复习的时候不要从头读,直接按环节自问,答不上来的那一格就是漏洞所在。

环节必须答得上来的问题答不上就回看
造数据三类数据分别是什么?100 万条按当前速率要跑多久?选 HTTP 脚本还是存储过程的判断依据是什么?2.1、2.2
方案响应时间是端到端还是服务端?环境表里客户端配置写了吗?每个指标的采集方式是什么?3.1、3.2、3.3
脚本用简单控制器还是事务控制器?集合点超时设多少?命名能不能对应到报告?4.1
结果聚合报告的吞吐是请求还是事务?90% Line 和平均值差多少?4.2
监控4444 端口通不通?jconsole 连不上时看什么?AWR 快照打了没有?4.3、4.4、4.5
判断日志搜了哪几类关键字?CPU 三态分布如何?内存看的是 GC 后水位吗?5.1、5.2、5.3

配套的四个动作可以固化成开场检查:造数据前算一次总时长并写进排期;压测前核对三台机器时间偏差;每个场景前后各打一次数据库快照;场景结束立刻记录结果文件与截图,不隔夜补。

# 每次开压前的固定动作,四条命令走完再启动线程组 date # 记录压测机时间 nc -zv 10.0.0.13 4444 # 验证服务端 agent 端口 sqlplus / as sysdba <<< "exec DBMS_WORKLOAD_REPOSITORY.CREATE_SNAPSHOT();" # 打起点快照 ls -lh result_*.jtl # 确认上一轮结果已归档,避免覆盖

我刚独立做压测那会儿,最惨的一次是 100 万条数据跑了一整夜,第二天发现库里只多了三十多万条,原因是中途网络抖动导致部分请求失败,而我没在脚本里加错误收集,也没设断点续跑。从那以后我每次造数据前都强制走一遍“总量 ÷ 速率 = 时长”的算式,再顺手把失败重试和结果落盘配上,宁可多花十分钟配置,也不想再赌一整夜。希望这份清单能帮你少走一遍这样的弯路。

本文还有配套的精品资源,点击获取

返回列表