性能工程、Profiling 与压测可信度
从用户体验、系统指标、性能画像、瓶颈定位、优化验证到容量基线的工程闭环
阅读边界: 这页不替代容量治理、SRE 或测试质量页。容量治理回答“系统能承接多大业务量”,SRE 回答“线上信号和恢复闭环怎样治理”,测试质量页回答“如何把性能测试纳入质量体系”。本页专门回答:一个性能问题怎样被度量、画像、定位、优化、验证,并防止下次回退。
定义用户体验
建立基线
采集画像
定位瓶颈
小步优化
验证与防回退
| 常见现象 | 错误反应 | 性能工程判断 |
| 接口变慢 | 先加缓存、扩机器或改线程池 | 先确认慢在 CPU、锁、GC、数据库、网络、下游、序列化还是排队。 |
| 压测 TPS 上不去 | 继续提高并发数 | 看瓶颈曲线是否已经到拐点,继续加压可能只是在制造排队和错误。 |
| 平均延迟正常,用户仍觉得卡 | 只看平均值 | 看 P95/P99、冷启动、慢依赖、长尾对象、前端等待和重试链路。 |
| 优化后测试环境变快,线上没变化 | 怀疑机器差异 | 检查流量模型、数据规模、缓存热度、依赖行为和观测口径是否一致。 |
| 层级 | 关键指标 | 它回答什么 | 容易误读 |
| 用户体验 | 页面可交互时间、接口端到端延迟、任务完成时间 | 用户是否真的感到快 | 服务端快不代表用户端快。 |
| 服务层 | QPS、错误率、P95/P99、并发、队列深度 | 服务是否正在排队、失败或抖动 | 平均延迟会掩盖长尾问题。 |
| 运行时 | GC、线程池、事件循环延迟、锁等待、对象分配率 | 语言运行时是否成为瓶颈 | CPU 不高也可能被 GC 或锁拖住。 |
| 系统层 | CPU、内存、IO wait、磁盘延迟、网络重传、上下文切换 | 机器和 OS 是否限制吞吐 | 资源水位低不代表没有局部瓶颈。 |
| 依赖层 | 数据库慢查询、缓存命中率、下游 P99、连接池、第三方限额 | 瓶颈是否在系统外部 | 只优化本服务会错过真正瓶颈。 |
CPU Profiling
- 回答 CPU 时间花在哪里。
- 适合查热点函数、序列化、加密、正则、循环和忙等。
- 看火焰图时优先找宽栈,而不是只看最深调用。
Memory Profiling
- 回答对象、缓存和缓冲区在哪里增长。
- 适合查内存泄漏、临时对象膨胀、集合过大和直接内存问题。
- 要区分内存占用高、泄漏和正常工作集。
Lock / Thread Profiling
- 回答线程是否在等待锁、队列、IO 或下游。
- 适合查线程池耗尽、死锁、热点锁、阻塞调用和队列堆积。
- 线程多不等于并发能力强。
IO / Network Profiling
- 回答慢在磁盘、网络、DNS、TLS、连接池还是远端服务。
- 适合查尾延迟、连接重建、重传、慢查询和远程存储抖动。
- 本地 CPU 正常时尤其要看这一层。
| 原则 | 为什么重要 | 最低动作 |
| 先基线,后优化 | 没有基线就不知道优化是否真的有效 | 记录当前流量、延迟、错误率、资源水位和版本。 |
| 在代表性流量下采样 | 空闲时的画像通常没有价值 | 在真实峰值、压测或可复现慢场景中采集。 |
| 一次只改一个主要变量 | 多个改动一起上会让归因失效 | 记录假设、改动、指标变化和副作用。 |
| 保留优化证据 | 性能问题很容易复发 | 把火焰图、压测报告、瓶颈假设和回归阈值留档。 |
| 症状 | 优先怀疑 | 看什么证据 | 常见动作 |
| CPU 高,吞吐不上升 | 热点计算、序列化、加密、正则、GC、忙等 | CPU 火焰图、GC 日志、系统态占比 | 优化热点路径、减少分配、调整算法或批处理。 |
| CPU 低,延迟高 | IO 等待、下游慢、锁等待、连接池耗尽 | Trace、线程 dump、连接池指标、下游 P99 | 隔离依赖、调整超时、扩连接池、优化慢查询。 |
| P99 高,P50 正常 | 长尾对象、缓存未命中、冷启动、少数慢依赖 | 分位数、按对象 / 租户 / 地域切片、trace 样本 | 切片定位、预热、热点治理、依赖隔离。 |
| 压测越压错误越多 | 排队、超时预算不闭合、重试风暴、限流缺失 | 队列深度、重试次数、错误码、超时分布 | 加背压、限流、熔断、降级和重试上限。 |
| 内存持续上涨 | 缓存无界、对象泄漏、队列堆积、直接内存 | Heap dump、对象分配、队列长度、RSS 与堆对比 | 设置边界、修泄漏、缩短生命周期、改缓冲策略。 |
和容量治理页的分工: 容量治理页负责水位、扩容、降级和成本治理;这里聚焦压测作为性能证据是否可信,以及如何把性能画像转成可复用基线。
| 校准项 | 低可信表现 | 高可信做法 |
| 流量形状 | 只压单接口、固定速率、没有突刺和用户路径 | 按真实旅程、读写比例、峰值突刺和热点对象构造流量。 |
| 数据分布 | 测试库小、索引干净、没有历史脏数据 | 接近生产数据量、冷热比例、租户差异和边界对象。 |
| 缓存状态 | 只测全热缓存或全冷缓存 | 分别记录冷启动、预热后、热点失效和回源压力。 |
| 依赖参与 | 下游全 mock,数据库和队列不承压 | 明确哪些依赖真实参与,mock 部分写清误差和风险。 |
| 观测完整度 | 只有 TPS 和平均延迟 | 同时保留分位数、错误率、资源、下游、队列、重试和火焰图。 |
| 优化手段 | 适合解决 | 副作用 | 上线前必须确认 |
| 缓存 | 热点读、重复计算、下游保护 | 一致性、穿透、雪崩、内存成本 | 失效策略、回源保护、权限和租户边界。 |
| 批处理 / 合并请求 | 高频小请求、频繁写入、网络往返过多 | 延迟增加、失败粒度变粗 | 最大批量、超时、部分失败处理。 |
| 异步化 | 慢任务、非关键路径、削峰 | 一致性、可见性、补偿和积压 | 状态机、幂等、重试、死信和用户反馈。 |
| 索引 / 查询优化 | 慢查询、扫描过大、排序聚合慢 | 写入变慢、索引膨胀、计划漂移 | 生产数据分布、执行计划、写入成本。 |
| 扩容 | 可横向扩展的计算和流量压力 | 成本上升、下游被打穿、冷启动 | 瓶颈是否真在当前层,扩容后下游是否承接得住。 |
基准测试
对核心算法、序列化、查询和关键组件保留稳定 benchmark,记录版本和环境。
性能门禁
对高风险路径设置回归阈值,不要求所有代码都压测,但核心路径必须有红线。
发布观测
发布后观察 P95/P99、错误率、资源水位、慢查询和下游调用,不只看流水线绿灯。
性能档案
沉淀每次瓶颈、火焰图、优化动作、压测曲线和副作用,避免团队反复踩同一个坑。
| 性能证据字段 | 要写清楚什么 | 缺失后的风险 |
| 目标与口径 | 核心接口、用户旅程、P95 / P99、错误率、业务成功率、资源水位和统计窗口,明确谁是验收方。 | “变快了”无法被复核,不同团队拿不同指标争论结果。 |
| 环境与版本 | 代码版本、配置、机器规格、实例数、数据规模、缓存状态、依赖参与方式和限流策略。 | 测试结果不可复现,也无法判断线上不一致来自环境还是优化本身。 |
| 流量模型 | 读写比例、用户路径、峰值突刺、热点对象、租户分布、并发模型和持续时间。 | 压测只证明“压测脚本能跑”,不能代表真实业务压力。 |
| 瓶颈证据 | 火焰图、Trace、线程 dump、GC 日志、慢查询、队列深度、下游 P99 或资源曲线,至少能支撑一个明确假设。 | 优化动作靠猜,后续回退或复盘时无法解释为什么改这里。 |
| 优化结果 | 改动前后同口径指标、收益幅度、副作用、成本变化、风险接受人和是否进入长期门禁。 | 只留下“优化完成”,没有留下是否值得、是否安全、是否可持续的判断。 |
| 回归门禁 | 最低要求 | 失败时动作 |
| 核心路径红线 | 关键接口或用户旅程的 P95 / P99、错误率、吞吐和资源水位有明确阈值。 | 阻断发布或进入灰度观察,不允许用平均值掩盖长尾恶化。 |
| 同口径对比 | 基线、压测、灰度和线上观测使用同一统计口径,差异必须标注来源。 | 暂停结论,先校准指标口径和数据窗口。 |
| 副作用检查 | 缓存命中、数据库写入、下游压力、队列积压、成本和错误分布没有被优化转嫁。 | 要求补充下游和成本证据,必要时回退优化。 |
| 发布观察窗 | 上线后有明确观察时长、看板、告警、回滚 owner 和降级动作。 | 不能扩大流量,先完成观察和回滚预案。 |
| 性能档案归档 | 报告、图表、假设、结论、门禁阈值和后续待办进入可检索档案。 | 本次优化不能沉淀为组织知识,下次同类问题仍会从零开始。 |
| 误区 | 为什么危险 | 修正方式 |
| 性能优化就是把代码写快 | 真实瓶颈经常在 IO、锁、GC、下游、数据模型和队列 | 先画像定位,再决定改代码还是改架构。 |
| 压测通过就等于生产没问题 | 压测无法完全覆盖数据分布、依赖抖动、真实热点和变更风险 | 把压测当证据之一,并接入灰度、观测和回滚。 |
| 加缓存一定更快 | 缓存可能增加一致性、权限、回源和内存风险 | 确认瓶颈是可缓存读路径,再设计失效与保护。 |
| 扩容是最稳妥的优化 | 如果瓶颈在串行点、锁、数据库或下游,扩容只会推高成本 | 先找瓶颈层,再判断是否可横向扩展。 |
| 检查项 | 最低要求 |
| 性能目标 | 明确用户体验目标、核心接口分位数和业务成功率,而不是只写“要快”。 |
| 基线记录 | 保留当前版本、环境、流量、数据、资源和关键指标。 |
| 瓶颈证据 | 至少有一种能支撑判断的证据:火焰图、Trace、线程 dump、GC 日志、慢查询或资源曲线。 |
| 优化假设 | 每个优化动作都写清楚它要降低什么成本、影响哪条链路、可能带来什么副作用。 |
| 验证与回退 | 优化后用同口径指标验证,并保留回退或降级路径。 |