简介:《性能测试-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 用示例网段):
| 服务器类型 | IP | CPU | 内存 | 硬盘 | 操作系统 |
|---|---|---|---|---|---|
| 测试客户端 | 10.0.0.11 | 16 核 | 8G | 500G | Windows 10 |
| 应用服务器 | 10.0.0.12 | 16 核 | 64G | 500G | Windows Server 2012 R2 |
| 数据库服务器 | 10.0.0.13 | 16 核 | 64G | 500G | Windows Server 2012 R2 |
| 服务器类型 | IP | 软件配置 |
|---|---|---|
| 测试客户端 | 10.0.0.11 | JMeter 5.1.1 及 ServerAgent 客户端 |
| 应用服务器 | 10.0.0.12 | JDK 1.8、Tomcat 8.0、Nginx 1.10 |
| 数据库服务器 | 10.0.0.13 | Oracle 19c |
重点不在表格好不好看,在于三个信息不能缺:压测机配置(它自己会不会先成为瓶颈)、中间件与 JDK 版本(不同版本的默认线程池和 GC 参数差异很大)、数据库版本(11g 和 19c 的 AWR 脚本行为不同,后面会讲)。客户端只有 8G 内存却要开 500 线程,这件事本身就要在方案里写清楚,否则结果会被质疑。
3.3 指标口径与采集方式对照
方案里还要说明每个指标用什么方式采集。这张表是执行阶段的作业指导书:
| 指标名称 | 参考说明 | 监测方式 |
|---|---|---|
| 数据库 Top SQL | 查看耗时或资源消耗最高的 SQL | AWR |
| 系统整体处理情况 | 数据库整体负载与等待事件 | 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 万条数据跑了一整夜,第二天发现库里只多了三十多万条,原因是中途网络抖动导致部分请求失败,而我没在脚本里加错误收集,也没设断点续跑。从那以后我每次造数据前都强制走一遍“总量 ÷ 速率 = 时长”的算式,再顺手把失败重试和结果落盘配上,宁可多花十分钟配置,也不想再赌一整夜。希望这份清单能帮你少走一遍这样的弯路。
本文还有配套的精品资源,点击获取