多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

LoadRunner压测实战:如何精准定位CPU瓶颈与性能拐点

LoadRunner压测实战:如何精准定位CPU瓶颈与性能拐点 看到标题里的 LoaRunner第一反应是你想问的应该是 LoadRunner。这个拼写在社区里经常被写歪我这里统一按 LoadRunner 来写。开篇直接说结论性能测试里“CPU 瓶颈”是最容易被提到、也最容易误判的话题。很多团队一看到 CPU 用满就急着加机器或者反过来看到 CPU 不高就断定系统没问题这两种我都见过不少。这篇内容我打算从定义、测试设计、脚本处理、场景执行到结果分析完整拆一遍特别适合刚转性能测试的工程师以及那些会录制脚本但没搞明白“为什么 CPU 高就等于瓶颈”的开发同学。你看完之后至少能回答三个问题CPU 瓶颈到底是什么、怎么用 LoadRunner 把它压出来、以及拿到曲线后怎么判断它在哪一刻出现。1. 性能测试里的CPU瓶颈到底说的是什么1.1 CPU使用率只是表象背后还有排队和上下文切换如果把服务器比作一间厨房CPU 就是那位负责炒菜的大厨。来的订单多了大厨的锅一直没停过使用率自然高。但真正决定出餐速度的不只是大厨有多忙还有厨房里积压了多少单子。这对应到系统上就是“处理器队列长度”。我这里反复强调一个观点不要单独看 CPU 使用率。在 Windows 上要结合 Processor Queue Length在 Linux 上要结合 Load Average 或 run queue 来看。比如一台 4 核服务器CPU 使用率已经 95%同时处理器队列长度稳定在 8 到 10那就证明 CPU 不仅在满载工作而且请求已经开始排队了。反过来如果你的 CPU 到了 90% 但队列长度基本是 0说明任务调度还跟得上只是计算单元本身就忙这虽然不是理想状态但危害没那么大。还有一个经常被忽略的指标是上下文切换。系统从执行线程 A 切换到线程 B 也要花费 CPU 周期如果线程数设置得过多或者锁竞争激烈上下文切换率会飙升。LoadRunner 的资源监控图里可以直接加 Context Switches/sec我习惯把它和 CPU 放在一张图上一起看。如果 CPU 高、上下文切换也高那问题往往不只是资源不足更可能是并发模型不合理或者线程池配置太大。这方面判断错了后面做的优化方向基本都会偏。1.2 怎么区分CPU瓶颈和代码效率问题CPU 高不一定等于资源不够用。有一些服务就是纯粹代码写得低效把一个本来 O(n) 的循环写成了 O(n^2)用户一多CPU 立刻打满。这种时候你把 CPU 从 4 核加到 16 核可能只是把爆雷时间往后拖了一点本质没解决。我自己的区分方法是做基线对比测试先用低并发跑一遍比如 100 个虚拟用户看响应时间和 CPU 的对应关系然后把并发往上加加到 300、500。如果 TPS 一直在涨响应时间也基本稳定到某个并发点 CPU 打满之后 TPS 开始平台甚至下降这属于典型资源型瓶颈。如果低并发时 CPU 才 40%响应时间就已经随着并发翻倍式上涨那你应该先怀疑代码逻辑和内部锁竞争。另外注意看 CPU 态拆分。Linux 的 top 输出里有 us 和 syus 高说明用户态代码在大量计算sy 高说明系统调用、内核态处理占了很高比例。锁竞争和线程切换最常表现为 sy 偏高纯粹的大计算逻辑则表现为 us 高。用 LoadRunner 的 Unix Resources 监控器也能拉到 User CPU 和 System CPU 的拆分我看到 sy 偏高时会推荐开发先去查锁相关代码而不是无脑增加 CPU 核数。1.3 CPU瓶颈和其他资源瓶颈的连带关系性能瓶颈很少是孤立出现的。CPU 和内存、磁盘 I/O、网络之间经常互相掩盖。举一个最常见的例子Java 应用内存泄漏老年代持续增长GC 频率变高Full GC 时 CPU 被垃圾回收线程大量占用最后展示出来的就是 CPU 使用率很高。这时候你要是直接加 CPUGC 压力反而可能更大因为分配内存的能力增强垃圾产生得更快。正确方向是堆内存调节或代码修复。类似的情况还有 Linux 上的 iowait。当磁盘读写成为瓶颈时CPU 会在等待 I/O 返回iowait 会被算进 CPU 时间的一部分。iowait 高说明真正卡在存储而不是 CPU 算不动。LoadRunner 的分析图不会直接告诉你“这是 I/O 瓶颈”它只给你数据你得自己去识别。所以做 CPU 瓶颈定位时永远要把内存、磁盘队列、网络流量放在旁边一起看至少做到心里有数知道你看到的 CPU 高是真的在算还是在等别人。2. 用LoadRunner做CPU瓶颈分析的设计思路2.1 先定指标和阈值再谈测试很多人上来就录制脚本录完直接跑场景跑完打开 Analysis 看一大堆图结果越看越乱。我建议把顺序反过来先明确你要采集哪些指标、判定阈值是什么再开始跑测试。针对 CPU 瓶颈分析我至少会记录下面这些指标指标作用常见阈值参考CPU 使用率判断整体繁忙程度持续高于 80% 就要提高警惕Processor Queue Length / Load Average判断有多少任务在排队队列持续大于核数说明饱和Context Switches/sec判断线程切换是否过高数值比平时高 5 倍以上需要关注事务响应时间判断用户感知是否恶化跟目标 SLA 对比TPS每秒事务数判断系统吞吐能力出现平台或下降就是容量拐点错误率排除异常失败导致的假现象越高越要优先排查错误响应阀值不是绝对的。如果你的系统本来就要求 CPU 90% 以上运行比如某些计算密集型服务那 80% 就不算预警线。所以我会在实际项目里先抓一段空闲期的基线数据再做一轮小并发探路测试确认正常水位后再定阈值。2.2 场景设计决定你能不能压出CPU瓶颈场景设计直接决定了测试结果有没有意义。想找 CPU 瓶颈我最推荐的是阶梯式加压场景。也就是把虚拟用户数分批往上加每批保持一段时间观察 CPU、TPS、响应时间的变化。比如 100 个用户跑 10 分钟然后增加到 200 个用户再跑 10 分钟这样 CPU 曲线会呈现明显的台阶你能清楚看到拐点发生在哪一级。相比之下一开始就把 500 个用户全部压上去曲线直接拉满你只能知道“系统挂了”却不知道它是从什么时候开始撑不住的。稳定持有场景适合验证系统在某个业务量下是否稳定不适合找瓶颈。还有一个容易被忽略的点是思考时间的设置。思考时间模拟的是用户浏览页面时的停顿。如果把思考时间设成一个很大的数CPU 压力会变低可能压不出瓶颈如果设成 0压力又过于集中不符合真实情况。我通常会在脚本参数里把思考时间设为真实用户的平均值这样结果既能反映生产问题也不至于失真。想专门压 CPU 时可以额外做一个思考时间很小的对比测试。2.3 选对监控指标别只看“CPU占用率”LoadRunner 的 Controller 里有现成的资源监视器。Windows 服务器用 Windows ResourcesUnix/Linux 服务器用 Unix Resources。两者监控的指标有细微差别Unix Resources 默认拿的是当前活动窗口的平均值Windows Resources 可以选很多性能计数器。具体到 Linux 服务器要用 LoadRunner 自带的 Unix Resources 监控必须保证目标机器开启了 rstatd 服务。不同发行版的启动命令有点差异CentOS 上通常是service rstatd startUbuntu 上可能需要装rstatd包再启动。如果你们安全策略不允许开这个服务就不要硬开直接在被测机上跑一个 nmon 或pidstat把日志落成本地文件测试结束后再手动对齐时间轴。LoadRunner 的分析图时间轴需要和本地监控数据对齐测试开始前我总会顺手在两边都记一个“当前时间点”免得后面数据对不上。3. 实操过程从脚本到分析工具完整跑一遍3.1 录制与参数化脚本层面别带坑LoadRunner 的操作路径很固定VuGen 录脚本调用场景然后 Controller 跑场景最后 Analysis 出报告。很多人直接拿录制的原始脚本就跑这种做法在压 CPU 的时候很容易失真。首先是协议选择。Web 应用一般选 Web - HTTP/HTML用 URL-based 录制往往能在事务定义上更精准。录制完成后检查是否有动态值常见的是会话 ID、时间戳和隐藏参数。这些不做关联的话每个 Vuser 拿到的可能都是同一个会话服务器端会有缓存和命中问题CPU 很难被真正压到。参数化也一样所有用户都拿同一个用户名登录接口查询缓存命中率高CPU 使用率会被严重低估。事务要主动打点。不要在 VuGen 里只看默认的Action。用lr_start_transaction(login)和lr_end_transaction(login,LR_AUTO)把核心步骤包起来这样后期在 Analysis 里能看到每一步的真实耗时而不是整段脚本混在一起。我在给团队做培训时一直强调脚本不是能跑通就算成功能分事务、能参数化、能在运行时设置里控制思考时间和迭代次数的脚本才对分析有真正价值。运行时设置建议这样调迭代次数按场景需要来pacing 如果不做特殊限制就设成“每次迭代后等待 0 秒”思考时间按用户模型来。目标是想压 CPU就把思考时间设到很短比如 0 到 3 秒随机想贴近真实就按照业务统计的均值。3.2 场景里的集合点与思考时间设置Controller 里建场景时你会遇到两种模式手动场景和目标场景。手动场景里你自己指定 Vuser 数量和调度方式适合做阶梯加压。目标场景里你指定一个目标 TPSLoadRunner 会自动增加或减少 Vuser适合做容量探测但如果你没设置好“持续时间”和“停止条件”它可能会为了追目标导致 CPU 曲线抖动反而不利于定位。集合点Rendezvous我不建议大家轻易用它设置在某一个操作前面后会让大量 Vuser 在同一时刻涌向同一个动作制造出人为并发尖峰。这种尖峰如果做得激进CPU 曲线会瞬间飙到顶然后因为请求排队而往下掉数据反映的是“突击行为”而不是自然业务负载。定位 CPU 瓶颈我们更想要的是平滑递增的负载曲线。持续时间也很重要。如果你希望看到稳定状态下的 CPU 趋势场景要跑足够久一般不少于 15 分钟否则预热和初始化的噪音还没消掉CPU 数据不具备代表性。阶梯测试时每一级至少跑 5 到 10 分钟否则无法判断该级是否已经稳定。3.3 用Controller搭配系统监控抓取CPU数据场景执行中我习惯同时在 Controller 里打开资源监控窗口。先在 Controller 的监控器配置里添加被测服务器然后勾选需要的计数器。Windows 服务器我会勾这几个% Processor Time、% Privileged Time、Processor Queue Length、Context Switches/sec、Available MBytes。Linux 服务器则勾选 CPU 利用率、队列长度和内存相关的几个基础计数器。轮询间隔不用太短5 秒一次就够。太频繁会产生大量监控数据包反而不稳定也不容易发现问题。场景运行过程中每隔一段时间看看监控器里的数据有没有异常跳变如果监控窗口里的曲线已经从平缓变成锯齿状往往意味着有什么定时任务在干扰或者网络抓包有抖动。还有一点容易忽略负载生成器本身也是一台真实机器。如果你在本地 Windows 上跑 300 个 Vuser然后被测服务器反而是 CPU 16 核负载机可能比服务器更早崩溃。所以我会在被测服务器之外也顺手把 Load Runner Agent 所在的负载机 CPU 加入监控。这个细节很多人要等负载机卡死才反应过来那时候测试数据已经废掉一半。3.4 用Analysis定位CPU瓶颈的关键走向场景跑完Controller 会生成 Analysis 结果文件。打开后第一件事不是看性能摘要而是先打开“平均事务响应时间”和“CPU 使用率”两张图把它们合并在一起。合并图的操作很简单在图例区右键点击要合并的图选择 Merge两个图就可以叠加。你会在叠加图上看到一条 CPU 曲线和一条响应时间曲线。当 CPU 达到某个高点时响应时间突然成倍拉长这个交叉位置就是容量拐点。同时看 TPS 曲线。正常情况下并发增长时 TPS 也涨到某个点开始平行于 X 轴甚至掉头向下。TPS 不再涨但 CPU 还在高位运转这就是饱和状态。此时如果响应时间也同步恶化CPU 瓶颈基本坐实。我这里会再翻一下 Unix/Linux 的 CPU 状态拆分子图看是用户态高还是系统态高为后面优化方向提供线索。4. 判断CPU瓶颈的典型曲线与数据规律4.1 饱和点怎么从图上看出来我这里给一个典型的拐点示例。某次给一个 web 服务做压测并发从 50 到 200 的情况简化如下并发用户数CPU使用率TPS平均响应时间5038%50000.08s10071%92000.12s15089%93000.40s20094%90501.05s看到这个表CPU 瓶颈的判断就非常清楚。100 并发之前系统吞吐几乎线性增长100 到 150 之间 CPU 继续上升但 TPS 几乎没涨说明系统已经从“算得过来”进入“排队等待”的状态200 并发时响应时间翻倍TPS 反而往下掉这就是过饱和了。如果你的 LoadRunner 图表出来以后整体趋势和上面接近那不需要过度分析直接定位到 100 到 150 这个区间做深入调查即可。CPU 瓶颈通常不会是一个单点而是一段区间你要找的是“拐点前的那一级”。4.2 多核环境下的CPU统计陷阱很多 CPU 瓶颈分析翻车都翻在多核统计上。最经典的现象是一台 8 核机器总 CPU 使用率显示 50%看起来系统很健康实际上其中一个核已经 100%其他核在空闲。某些应用是单线程的或者只有一种关键线程无法跨核扩展瓶颈集中在单核上。这种时候 LoadRunner 的 Unix Resources 监控可能只给你平均 CPU 值很容易掩盖真相。我建议在 Linux 上用top之后按1查看每个核的负载或者用 LoadRunner 的自定义计数器逐核拉取数据。Windows 上也有类似问题。Windows 资源监视器里的 % Processor Time 分成Processor(_Total)和Processor(单核编号)默认看 _Total 会掩盖单核热点。对锁粒度较粗的应用或者某些信号处理类服务单核跑满的情况非常常见所以从监控选择上就要把整机和单核都拉进来不要只选一个总计。顺便说一句超线程也会干扰判断。逻辑核和物理核的关系并非完全等价某些计算密集逻辑在超线程开启时表现会规律性波动。遇到曲线周期性冲高回落的先检查是不是超线程调度的问题不要急着下结论说代码有缺陷。4.3 瓶颈类型切换CPU瓶颈和内存、IO之间的互相转移性能瓶颈不是一成不变的。同一个系统在中午高峰期可能表现为 CPU 高到了凌晨定时任务批量执行时可能表现为磁盘 I/O 高再后来缓存淘汰造成内存压力时又表现为 CPU 高。LoadRunner 压出来的只是一段时间内的快照你要学会根据曲线形状识别瓶颈正在往哪里转移。一个典型场景是内存页换出。系统物理内存不足时会频繁在内存和磁盘之间交换页面。磁盘换页消耗 I/OI/O 等待增加表现在 CPU 上可能是 iowait 高也可能是 CPU 被中断处理和内核态占用。这时候 LoadRunner 的 CPU 图看起它就是“CPU 瓶颈”但真正修的方向却是内存。所以每次定位到 CPU 高我都会顺手确认三件事物理内存是否还有余量、磁盘队列长度是否正常、网络是否存在重传和延迟。只有排除了这三位之后我才会把高 CPU 归因到计算资源不足或代码效率问题。5. 常见问题与排查技巧速查表5.1 CPU很高但TPS上不去优先检查什么这是压测群里被问得最多的一个问题。我把排查顺序固定成下面几步先看线程和进程状态。如果被测应用是 Java直接抓一份jstack如果是其他语言就用对应工具看线程是不是大量阻塞在锁上。锁竞争导致的表现往往是 CPU 高、吞吐低因为线程在忙等或者反复重试。再看数据库和外部依赖。有些应用的大量计算不在自己这层而是在 SQL 里数据库执行计划写得差一条大查询就能把数据库 CPU 拉满应用层 CPU 反而不高但 TPS 依然上不去。LoadRunner 的量是在应用层压出来的它本身不会告诉你数据库慢你得靠日志链路去查。最后再看脚本自身有没有问题。很多人录完脚本没有把思考时间去掉每个迭代之间空转很久Vuser 数量看起来不少实际每秒发出去的请求很少。这种情况下 CPU 高不是系统瓶颈而是你自己把负载机或者应用线程耗在了等待上。5.2 负载机自己CPU高导致结果失真我自己踩过这个坑印象特别深。一次给客户压测被测服务器 16 核始终说 CPU 上不去怀疑系统容量太富余。后来我切到负载生成器一看CPU 已经 95%原来是为了省成本只开了一台负载机300 个 Vuser 全压在同一个机器上。此时 CPU 瓶颈在负载机而不在服务器数据完全失真。LoadRunner 的 Agent 自身开销其实不低尤其遇到页面含有大量资源时脚本解析、HTTP 解析都要吃 CPU。常规经验是一台 8 核负载机跑 200 到 300 个轻量 Web Vuser 差不多但具体能压多少个要看你脚本的复杂度和被测服务器响应速度。更快的方法是直接监控负载机 CPUCPU 超过 80% 就加一台负载机把部分 Vuser 迁过去。5.3 压测结束后CPU不回落意味着什么场景明明停了LoadRunner 的 Vuser 应该已经停止了但被测服务器的 CPU 还是高居不下。这种情况也不能算少见。首先查是不是有定时任务被压测触发比如日志切割、监控采集、数据备份往往一到整点就启动CPU 就会被这些后台任务吃掉。区分方法很简单场景跑完后观察 5 到 10 分钟如果 CPU 持续高位用 top 按 CPU 排序看具体是哪个进程在跑。还有一种可能是应用本身的异步任务还在排队处理压测时积压的消息或线程栈。如果这种情况持续很久说明应用对压测期间产生的负载有滞后处理这本身也是一个值得记录的性能表现。我会在测试记录里专门加一行“恢复期 CPU 走势”用来判断系统在流量退去后的自愈能力。5.4 几条让我省下不少时间的实操教训第一录制之前先把被测系统的环境信息记全包括内核参数、CPU 核数、内存、中间件版本。后面写测试结论时这些信息比曲线图本身更有说服力也能避免“CPU 太高是不是你们环境有问题”这类扯皮。第二LoadRunner 的 Analysis 里合并图功能非常实用但别只在一个查询维度里看。我会分别做“CPU vs TPS”和“CPU vs 响应时间”的合并第一个看重不重第二个看卡不卡。第三压测过程中不要频繁打开被测服务器上的监控工具。有些图形化工具本身就会占用 CPU 和网络干扰采样。最好用轻量级的命令比如pidstat 1或者nmon -s 5留一份本地日志给后面分析。第四定位到 CPU 瓶颈之后别急着结束。我会再跑一遍只有核心业务的场景把不必要的组件全部摘掉确认瓶颈是否跟随核心业务出现。这一步能帮你判断瓶颈到底是业务自身问题还是测试中被额外组件拖垮的。6. 顺带说一句JMeter用户怎么复用这套分析方法6.1 LoadRunner和JMeter的监控思路对照热搜词里出现了“jmeter性能测试步骤”我就多说一句。很多人问这两个工具在 CPU 瓶颈分析上差异大不大我的结论是LoadRunner 的优势在 Controller 和 Analysis 一体化监控和分析的衔接流程是现成的JMeter 本身只是压力生成器要靠外部插件才能做系统资源监控。JMeter 做 CPU 瓶颈分析一般是装 PerfMon 插件并在被测机上启动 ServerAgent再配合 InfluxDB 和 Grafana 把线程组结果和 CPU 数据放到同一个看板。也有直接拿 JMeter 输出 JTL 文件后解析出 TPS 和响应时间再用本地监控日志手动对齐的做法。工具链路不同但分析逻辑是一致的并发用户上升到某个点CPU 达到高位TPS 停止增长响应时间开始抬头这个重合区间就是瓶颈所在。所以你在 JMeter 里也想做阶梯加压时可以用 Custom Thread Groups 插件模拟 LoadRunner 的阶梯场景效果非常接近。6.2 建议从工具转向指标体系我见过不少人纠结“LoadRunner 和 JMeter 到底用哪个”但对 CPU 瓶颈分析来说工具只是入口。你真正要吃透的是四个指标之间的联动关系并发数、CPU 使用率、TPS、响应时间。不管你是用 LoadRunner 的 Analysis 合并图还是用 JMeter Grafana 画曲线只要这四者之间的关系清楚了瓶颈迟早能挖出来。我个人建议做 CPU 瓶颈定位时第一件事不是打开脚本改参数而是先确认监控数据链路是通的、时间轴是对齐的。一次正式压测如果监控数据没齐我会直接放弃这轮结果重新跑一轮。因为 CPU 瓶颈分析判断的核心是曲线之间的相对位置少一个指标曲线就可能把方向带偏。这个习惯看起来有点强迫症但救过我太多次值得你参考。
返回列表