很多人刚接触压测时会产生一种错觉“压测不就是看 QPS 吗”但压测的本质从来不是“跑数字”而是找到系统的性能极限以及限制系统性能的真正瓶颈。本文会围绕下面几个核心问题建立一套性能分析认知压测到底是在测什么什么叫“压爆 CPU”为什么 CPU 没满 QPS 也会下降Arthas 火焰图到底在看什么数据库连接池为什么不是越大越好MySQL 为什么最终还是要读磁盘SSD、IO、Buffer Pool 到底是什么Grafana 里监测的 CPU 数据为什么有 100% 和 800%QPS 到底多少才算“好”一、压测的本质系统为什么会出现性能瓶颈1. 压测的目标与意义很多人以为压测 看 QPS其实这只是表象。压测真正的目标是找到系统在什么情况下开始性能下降。也就是系统什么时候开始变慢为什么开始变慢是哪个资源先耗尽系统还能不能继续扩容因此压测本质上是在研究系统资源竞争。2. 系统性能瓶颈的来源因为所有资源都是有限的。例如资源作用CPU负责计算内存负责缓存数据磁盘 IO负责读写数据网络带宽负责数据传输数据库连接负责并发访问数据库锁保证线程安全当大量请求同时到来时大家抢 CPU抢数据库连接抢锁抢磁盘 IO抢网络带宽最终一定会有某个资源先耗尽。这就叫性能瓶颈。3. CPU 成为瓶颈时的系统状态有人会说一句话“最好压爆 CPU。”为什么很多情况下会认为这是“健康”的结果因为CPU 是最容易扩容的资源。如果最终瓶颈是CPU 100%说明代码没有明显问题没有锁死没有连接池堵塞没有严重慢 SQL系统已经把硬件性能充分利用此时提升性能的方法相对直接增加机器资源即可。例如4 核升级 8 核QPS 往往会接近线性增长因此CPU 打满通常意味着系统已经接近当前硬件配置的性能上限。4. CPU 未打满但 QPS 下降的情况真正需要重点关注的情况往往不是 CPU 满载。而是CPU 仍有空闲但系统吞吐量已经开始下降。例如CPUQPS30%100040%120050%110060%900可以发现CPU 还有较多剩余资源但 QPS 已经开始下降这通常意味着系统内部已经出现阻塞或资源竞争。也就是说系统的问题并不是“算不过来”而是“某个环节开始堵塞”。常见原因包括问题现象锁竞争线程排队数据库连接池过小请求拿不到连接慢 SQLMySQL 阻塞GC 频繁JVM 暂停网络瓶颈请求发不出去线程过多CPU 大量上下文切换此时问题已经不能仅靠扩容解决。而是需要进一步进行性能瓶颈定位与系统分析。二、性能分析中的常用工具1. Grafana 的作用与定位Grafana 本质上是一个用于展示系统监控数据的可视化平台。在实际项目中通常会结合Prometheusnode_exporterJVM ExporterMySQL Exporter等组件对系统运行状态进行监控。常见监控指标包括CPU 使用率内存使用情况磁盘 IO网络流量JVM 状态MySQL 运行状态Grafana 更适合用于观察系统整体运行趋势分析资源使用情况判断系统是否出现异常辅助定位性能瓶颈方向例如CPU 是否持续升高内存是否存在泄漏磁盘 IO 是否出现瓶颈数据库负载是否异常需要注意的是Grafana 更偏向于“系统级监控”它能够帮助我们发现系统哪里可能存在问题但通常无法直接定位到具体是哪一行代码导致的问题2. top 命令的作用在 Linux 环境中top是最常用的系统资源查看命令之一。它主要用于观察当前机器的 CPU 使用情况内存占用情况进程资源消耗情况系统负载情况在压测过程中top 经常用于快速判断当前究竟是哪一个进程在消耗资源例如Java 进程 CPU 较高→ 可能是业务逻辑计算压力较大MySQL 进程 CPU 较高→ 可能是数据库查询或 SQL 存在性能问题因此top 更适合作为一种“快速定位资源消耗方向”的工具。3. 应用部署方式与监控方案在性能压测中不同的部署方式会直接影响资源监控与瓶颈分析的方法。常见场景通常分为以下两种。1应用与数据库部署在同一台机器在一些测试环境或小型项目中Java 服务MySQL 数据库可能会部署在同一台服务器。此时通过top即可同时观察Java 进程资源占用MySQL 进程资源占用例如Java CPU 占用较高→ 说明业务逻辑或 JVM 压力较大MySQL CPU 占用较高→ 说明数据库查询或 SQL 执行可能已经成为瓶颈这种部署方式的特点是环境简单排查方便资源会相互竞争因此在压测时经常会出现Java 与 MySQL 互相抢占 CPU、内存与磁盘 IO的情况。2应用与数据库独立部署在生产环境中更常见的是应用服务器独立部署数据库服务器独立部署这种架构的好处在于应用与数据库资源隔离更方便进行容量规划更容易定位性能瓶颈更适合后续扩容此时通常需要分别观察不同服务器的资源情况。例如登录应用服务器查看 Java 资源占用登录数据库服务器查看 MySQL 资源占用需要注意的是top 只能查看当前服务器上的资源情况无法直接观察其它机器的运行状态。3压测中的数据采集与监控无论是单机部署还是独立部署仅依赖 top 都很难完成完整的压测分析因为 top 更偏向于实时查看临时排查但压测往往还需要关注CPU 峰值QPS 变化趋势RT 波动GC 情况历史监控数据多服务器资源变化因此生产环境中通常会结合PrometheusGrafana构建统一监控体系。常见做法包括组件作用node_exporter采集 Linux 系统资源JVM Exporter采集 JVM 内部指标MySQL Exporter采集 MySQL 内部指标Prometheus统一采集监控数据Grafana统一展示监控图表这样可以统一监控监控对象关注指标应用服务器CPU、GC、线程数、JVM 内存数据库服务器CPU、IO、连接数、慢 SQL系统整体QPS、RT、错误率相比单纯使用 topGrafana 更适合长期监控、趋势分析与压测结果统计三、Arthas 火焰图CPU 时间分布分析工具1. 火焰图的基本概念火焰图Flame Graph是一种用于展示CPU 时间分布与调用关系结构的可视化工具。其核心作用是回答以下问题在采样周期内CPU 时间主要消耗在哪些方法上以及这些方法位于怎样的调用链中。通过对方法调用栈进行采样统计火焰图可以同时反映各方法的 CPU 时间占比方法之间的调用层级关系2. 火焰图中“宽度”和“高度”的含义火焰图的两个核心维度分别表示不同信息2.1 宽度CPU 时间占比在火焰图中每个矩形块的宽度表示该方法在采样时间内的 CPU 时间占比。宽度越大 → CPU 在该方法上的执行时间占比越高例如方法CPU 占比JSON 序列化50%SQL 查询30%日志打印10%在该场景中JSON 序列化方法对应的宽度最大表示其 CPU 消耗占比最高。2.2 高度调用栈深度火焰图的高度表示方法调用链的层级深度即当前方法处于多少层调用关系之中。高度 调用栈的深度Stack Depth例如调用链Controller → Service → DAO → JDBC在火焰图中表现为Controller 位于较低层DAO / JDBC 位于更高层每向上一层表示调用关系进一步深入需要注意方法高度越高 ≠ CPU 消耗越高高度仅反映调用结构而不代表资源消耗。3. 火焰图生成的运行条件火焰图依赖于采样统计机制通常需要在压测环境中生成而不是基于单次请求分析。Arthas 在采集过程中通过周期性采样记录线程执行栈例如以固定时间间隔采样当前 CPU 正在执行的方法栈单次请求分析存在以下限制执行时间较短采样数据量不足热点方法难以形成统计显著性因此推荐的采集流程如下持续压测 ↓ 系统进入稳定高负载状态 ↓ 执行 profiler start ↓ 持续采样 3060 秒 ↓ 执行 profiler stop该流程可以保证采样结果具备统计意义。4. 低 CPU 负载场景下的局限性当系统 CPU 使用率较低时例如约 5%CPU 大部分时间处于空闲或等待状态。在这种情况下采样结果通常包含大量非业务执行状态例如idlewaitparksleep由于业务方法执行时间占比较低热点方法在火焰图中的宽度差异不明显同时由于业务调用样本不足调用结构也难以体现稳定热点路径。因此在低负载条件下火焰图的分析价值相对有限。5. 推荐采样的 CPU 负载区间当 CPU 使用率处于中高负载区间通常为 70%90%时系统主要时间用于业务处理。此时采样能够更稳定地反映真实计算热点例如JSON 序列化与反序列化数据库查询与执行正则表达式处理加解密操作核心业务逻辑在该负载区间下热点方法在火焰图中宽度更集中调用结构更清晰同时高度调用链结构也更容易呈现稳定的业务路径。因此该区间通常用于性能分析与瓶颈定位的采样窗口。四、数据库连接池为什么不是越大越好1. 连接池的本质在多数系统中数据库连接通常被理解为一个 TCP 连接。但从数据库执行模型来看更关键的本质是数据库连接 ≈ 一个可被数据库调度执行的会话单元session在 MySQL 等数据库中每个连接通常对应一个会话上下文一个执行线程或线程调度单元一组资源占用内存、锁、文件句柄等因此连接池的本质是对数据库并发执行能力的受控放大2. 连接数过大的性能问题以如下环境为例数据库服务器8 核 CPU连接池配置500意味着500 个数据库执行线程竞争 8 个 CPU 核心在这种情况下CPU 大量时间会消耗在非业务计算上包括线程上下文保存线程上下文恢复CPU 调度切换该过程称为上下文切换Context Switch上下文切换本身具有固定开销当线程数量显著超过 CPU 核心数时该开销会快速上升。因此可以得到结论线程数过多会导致 CPU 调度开销增加从而降低系统整体吞吐能力TPS/QPS3. 连接数过小的问题连接池过小同样会带来性能问题。例如连接数5并发请求500此时系统表现为大量请求在连接池队列中等待可用连接可能导致以下现象响应时间RT上升吞吐量TPS下降请求排队堆积因此连接池大小需要在“资源利用率”和“排队延迟”之间进行平衡4. HikariCP 推荐配置模型HikariCP 提供了一个经验性计算公式connections (core_count × 2) effective_spindle_count例如CPU 核数8存储SSD则计算结果为(8 × 2) 1 17该结果通常低于直觉预期但其设计目标是避免数据库线程过多导致调度开销上升从实践角度看该公式更偏向于“稳定吞吐能力”而非“最大并发能力”。5. effective_spindle_count 的含义该参数来源于传统机械硬盘HDD架构。在 HDD 时代数据存储依赖物理磁盘每个磁盘spindle具备独立 I/O 能力并发 I/O 能力与磁盘数量相关因此定义effective_spindle_count ≈ 可并行处理 I/O 的物理磁盘数量用于估算数据库存储层的并发能力。6. SSD 时代的取值方式在现代存储架构中主流存储介质包括SSDNVMe云盘ESSD 等其特点是随机 I/O 性能显著提升并发访问能力大幅增强存储不再是主要瓶颈在此背景下数据库系统的主要瓶颈通常转移至CPU 调度锁竞争内存资源争用线程上下文切换因此在实际工程中通常简化为effective_spindle_count 1五、数据库为什么最终仍然需要读磁盘1. 数据不能仅依赖内存的原因数据库系统无法仅依赖内存存储数据其根本原因在于内存RAM在断电或系统崩溃后会丢失数据为了保证数据持久性数据库必须满足以下基本要求即使服务器发生断电或重启数据仍然可以恢复因此可以得到结论数据的最终持久化介质必须是磁盘2. 数据库为什么仍然具备高性能尽管数据最终需要写入磁盘但数据库的高性能并不依赖每次都访问磁盘。现代数据库普遍采用内存缓存机制来减少磁盘访问次数。例如 MySQL 的Buffer Pool缓冲池Buffer Pool 的作用是将热点数据页缓存在内存中从而加速访问。3. MySQL 的典型数据读取流程以查询语句为例select*fromuserwhereid1;其典型执行流程如下先查询 Buffer Pool内存 ↓ 如果命中直接返回结果 ↓ 如果未命中再从磁盘读取数据页 ↓ 加载至 Buffer Pool 后返回结果因此在实际运行中大部分查询请求并不会直接触发磁盘 IO4. 为什么磁盘 IO 会成为性能瓶颈不同硬件在访问延迟上存在显著差异介质延迟级别CPU纳秒级内存十纳秒级SSD微秒毫秒级HDD毫秒级可以看出磁盘访问延迟远高于 CPU 和内存因此在数据库系统中性能瓶颈通常由磁盘 IO 主导核心优化目标是减少磁盘访问次数提高内存命中率。5. IO 能力的定义与衡量方式IO 能力通常用于描述存储系统在单位时间内处理读写请求的能力。其标准衡量指标为IOPSInput/Output Operations Per Second不同存储介质的典型 IOPS 参考如下存储类型IOPS 级别HDD几百SSD几万NVMe十万级以上6. 总结可以归纳为以下关键关系磁盘 数据最终持久化载体 内存 数据访问加速层 IOPS 存储系统并发能力指标数据库性能优化的核心目标通常是最大化内存命中率最小化磁盘 IO 访问比例六、Grafana 中的 CPU为什么会看到 100% 和 800%1. 为什么 top 中会出现 800%在 Linux 系统中CPU 使用率通常是按单核维度进行统计的。基本规则如下单个 CPU 核心的最大使用率 100%因此对于多核系统1 核满载 100%8 核满载 800%这种表示方式称为非归一化 CPU 使用率Non-normalized CPU Usage其本质是将所有 CPU 核心的使用率进行累加。2. 什么是归一化 CPU 使用率归一化Normalized是指将多核 CPU 使用率转换为统一比例。其定义为无论 CPU 核心数量多少整机满载统一表示为 100%例如8 核满载 100%4 核满载 100%在这种情况下CPU 使用率表示的是整体资源利用率占比而非单核累加值。3. Grafana 如何判断 CPU 是否归一化CPU 指标是否归一化通常与 PromQL 的计算方式直接相关。在 Grafana 中CPU 使用率的展示结果本质上取决于查询语句是否对多核 CPU 数据进行了“平均处理”或“累加处理”。例如avg(...)或rate(...) / count(...)通常表示对多核数据进行了平均或归一化处理。相对地如果查询类似sum(rate(node_cpu_seconds_total[1m]))则通常表示将所有 CPU 核心的使用率进行了累加非归一化4. 总结可以将两种模式简单归纳为非归一化每个 CPU 核心单独统计并累加最大值 核数 × 100% 归一化将整体 CPU 使用率压缩为 0%100%在分析 Grafana CPU 曲线时应首先确认是否归一化否则容易误判系统负载状态。七、QPS 到底多少算“好”1. 为什么 QPS 没有统一标准QPS每秒请求数是系统在特定资源配置与运行条件下的综合结果指标其数值受到多种因素共同影响因此不存在统一的评判标准。主要影响因素包括因素影响CPU 核数并行计算能力内存缓存命中率数据库性能IO 延迟与查询效率网络请求吞吐与延迟业务逻辑复杂度单请求计算成本外部依赖RPC / AI 等引入额外延迟与不确定性因此可以得出结论QPS 必须结合具体系统架构与运行环境进行分析不能脱离上下文单独评价2. QPS 的本质系统吞吐能力结果QPS 本质上是系统在当前负载下的吞吐结果指标它反映的是系统当前是否已经接近资源瓶颈请求处理链路的整体效率是否存在阻塞CPU / IO / 锁 / 外部调用因此QPS 不是设计目标而是系统运行状态的结果表现3. 更有意义的分析方式结合资源观察在实际性能分析中QPS 通常需要结合以下指标一起判断CPU 使用率是否接近瓶颈平均/分位响应时间RT错误率是否出现退化线程池与连接池状态是否排队例如QPS 上升但 RT 急剧增加 → 可能进入排队或资源竞争CPU 未打满但 QPS 无法提升 → 可能存在 IO / 锁 / 外部依赖瓶颈QPS 稳定但错误率上升 → 系统已不再健康运行4. 判断系统性能是否健康的标准在压测过程中一个相对健康的系统状态通常表现为CPU 接近瓶颈 QPS 进入平台区不再随并发增长 响应时间保持在可接受范围 错误率维持较低水平此时通常说明系统已经接近当前资源上限性能瓶颈主要来自资源限制而非代码缺陷可以通过横向扩容提升整体吞吐能力八、完整的压测分析思路完整的压测分析流程本质上是一个从“负载递增”到“瓶颈定位”的闭环过程。一个标准的压测分析流程通常如下逐步增加并发 ↓ 观察 QPS 是否持续增长 ↓ 观察 CPU 是否接近瓶颈 ↓ 如果 CPU 先达到瓶颈 → 当前系统吞吐已接近上限 → 系统整体状态相对健康可通过扩容提升能力 ↓ 如果 CPU 未满但 QPS 停止增长或下降 → 系统存在其他瓶颈 ↓ 结合监控与工具定位问题 Grafana / top / Arthas / APM ↓ 进一步分析可能的瓶颈点 - 锁竞争 - SQL 性能问题 - GC 频繁或停顿 - 连接池限制 - IO / 外部依赖延迟 - 热点代码执行效率问题 ↓ 完成优化后重新进行压测验证这一流程构成一个完整的性能分析闭环。九、总结压测的本质并不是获取一个“QPS 数值”而是理解系统在不同负载下的行为变化。其核心目标在于理解系统资源是如何被消耗以及瓶颈最终出现在什么位置当能够清晰解释以下问题时才说明具备完整的性能分析能力CPU 为什么会成为瓶颈为什么线程数增加可能导致性能下降为什么数据库访问最终不可避免涉及磁盘 IO为什么火焰图需要在高负载下采样才有意义为什么连接池大小存在合理区间而非越大越好为什么 QPS 本身不能作为绝对性能标准可以归纳为所有性能问题本质上都是资源竞争与约束的结果压测的最终目标不是“跑出更高的数字”而是在明确系统极限的前提下验证系统是否具备稳定、可扩展的运行能力