指标分四层
看性能问题时,先确认指标属于哪一层。层次混在一起,讨论就会失焦。
| 层 | 典型指标 | 回答的问题 | 更新频率 |
|---|---|---|---|
| 业务 | 下单成功率、支付转化率、订单量 | 用户是否完成目标 | 分钟级 |
| 应用 | QPS、P99、错误率、超时率 | 服务是否健康 | 秒级 |
| 依赖 | 数据库慢查询、缓存命中率、消息积压 | 瓶颈是否在下游 | 秒到分钟 |
| 主机 | CPU、内存、磁盘、网络、连接数 | 资源是否饱和 | 秒级 |
业务指标是最终判据,应用指标用于定位,依赖和主机指标用于解释原因。只有主机指标没有业务指标,会出现“机器很闲但用户在下单失败”;只有业务指标没有应用指标,知道出事了却不知道在哪一层。
延迟指标
常用分位数
| 指标 | 含义 | 用途 | 注意 |
|---|---|---|---|
| 平均延迟 | 所有请求的算术平均 | 粗略趋势 | 会被快请求拉低,不能代表体验 |
| P50 | 一半请求快于此值 | 典型体验 | 看不出尾部问题 |
| P95 | 5% 的请求慢于此值 | 常用 SLO 门槛 | 样本需几千以上 |
| P99 | 1% 的请求慢于此值 | 尾部体验 | 样本需一万以上 |
| P999 | 0.1% 的请求慢于此值 | 极端尾部 | 样本需十万以上 |
延迟必须带口径:客户端、服务端入口、业务内部还是下游单次。同一个请求,四种口径能差出几倍。报告里写“P99 20ms”而不写口径和样本量,等于没写。
直方图与合并
分位数不能直接平均。两个实例各报 P99,一个是 10ms,一个是 1s,平均得 505ms,但合并后的真实 P99 是 1s。下面这段输出来自 .local/reports/be-arch/be-arch-perf-02-3-result-capacity-headroom-run.txt,总样本 2000:
instance_a_p99=10ms instance_b_p99=1s average_of_p99=505ms merged_p99=1s total=2000
正确做法是合并直方图的桶计数,再重新计算分位数。合并的前提是桶边界一致;桶不一致时,只能重新采集,不能强行合并。代码里如果只存了一个 p99 字段,这个指标在聚合后就失去意义。
分位数之外
分位数描述分布,但不描述“多久”。两个系统 P99 都是 100ms,一个每 10 秒出现一次尖峰,一个持续抖动,用户体验不同。补充指标:
- 超时率:超过 SLO 上限的请求占比;
- 慢请求数:绝对数量,避免小流量下分位数失真;
- 最大延迟:用于发现极端个案,但不能作为 SLO;
- 延迟分布:按 10ms、50ms、100ms、500ms、1s 分桶,直观看形状。
吞吐指标
| 指标 | 定义 | 常见误区 |
|---|---|---|
| QPS | 每秒查询数 | 把到达数当完成数 |
| RPS | 每秒请求数,常与 QPS 混用 | 未说明是否含被拒绝请求 |
| TPS | 每秒事务数,事务边界需定义 | 一个请求可能包含多个事务 |
| 完成率 | completed / started | 分母用 offered 会低估 |
| 拒绝率 | rejected / started | 限流生效时拒绝率上升是预期 |
| 超时率 | timed_out / started | 与错误率分开统计 |
压测报告至少给三组数:到达、完成、拒绝。只给一个“QPS 5000”,无法判断系统是轻松完成还是靠拒绝维持。
吞吐和延迟不是独立指标。系统接近饱和时,吞吐趋于平稳,延迟快速上升;这时再加压力,完成的请求不会变多,只会让排队更深。
并发与排队
利特尔法则
稳定系统中:
并发数 L = 到达率 λ × 平均停留时间 W
例如到达率 200 rps、平均停留 50ms,则平均在途请求为 10。反过来,若连接池只有 8 个,系统会在池上排队,实际停留时间变长,直到 L 与池容量重新平衡。
利特尔法则只适用于稳定状态。压测刚开始、流量突增、系统正在恢复时,L 与 λ×W 不相等,不能拿瞬时值硬套。
排队指标
| 指标 | 含义 | 高了说明什么 |
|---|---|---|
| 在途请求数 | 已进入未完成 | 处理不过来或下游慢 |
| 队列长度 | 等待被处理的请求 | 消费者不足或处理变慢 |
| 等待时间 | 排队耗时 | 容量不足的直接信号 |
| 池占用率 | 已用连接/线程占上限比例 | 接近 1 时排队会非线性增长 |
| 利用率 | 资源忙时间占比 | 超过 70%–80% 后延迟通常明显上升 |
排队是非线性的。利用率 50% 时,延迟约等于服务时间;利用率 80% 时,排队显著增加;利用率 95% 时,轻微波动就会造成长队列。容量规划留余量,本质上是在避开这段非线性区。
错误与可用性
| 指标 | 公式 | 说明 |
|---|---|---|
| 错误率 | 失败请求 / 总请求 | 需区分业务错误与系统错误 |
| 成功率 | 成功请求 / 总请求 | 与错误率互补,但分母要一致 |
| 超时率 | 超时请求 / 总请求 | 单独统计,不并入业务错误 |
| 可用性 | 可用时间 / 总时间 | 按请求算还是按时间算要写明 |
可用性“几个 9”的年度停机时间:
| 可用性 | 年停机 | 月停机 | 说明 |
|---|---|---|---|
| 99% | 3.65 天 | 7.2 小时 | 一般内部系统 |
| 99.9% | 8.76 小时 | 43.8 分钟 | 常见在线服务 |
| 99.95% | 4.38 小时 | 21.9 分钟 | 较高要求 |
| 99.99% | 52.6 分钟 | 4.38 分钟 | 核心交易链路 |
| 99.999% | 5.26 分钟 | 26 秒 | 成本极高,需专门设计 |
这些数字是理论上限,不是承诺。达到几个 9,取决于故障检测、切换和恢复的实际速度,而不是写在文档里的目标。
错误预算
错误预算 = 1 − SLO。月 SLO 99.9% 意味着每月允许约 43.8 分钟不可用,或者按请求折算的失败比例。预算用完,应该冻结变更、优先修复;预算充足,可以承担更高风险的发布。错误预算的作用是把“要不要发”从争论变成算式。
资源饱和度
| 资源 | 指标 | 饱和信号 | 备注 |
|---|---|---|---|
| CPU | 使用率、运行队列长度 | 使用率长期 > 80%,队列 > 核数 | 容器要看 cgroup 限额 |
| 内存 | RSS、堆使用、GC 频率 | RSS 接近限额,GC 频繁 | Go 看 heap 与 GC pause |
| 磁盘 | IOPS、等待时间、使用率 | await 上升,队列变长 | 日志和数据库常互相影响 |
| 网络 | 带宽、重传、连接数 | 重传上升,连接耗尽 | 连接数不是越多越好 |
| 连接池 | 占用率、等待时间 | 占用率接近 1 | 等待时间是更早的信号 |
| 协程/线程 | 数量、阻塞数 | 数量持续增长 | 泄漏通常表现为单调上升 |
饱和度指标要结合延迟看。CPU 90% 但 P99 稳定,可能只是压测机忙;CPU 50% 但 P99 飙升,瓶颈可能在锁、I/O 或下游。
缓存指标
| 指标 | 公式 | 用途 |
|---|---|---|
| 命中率 | 命中次数 / 总查询 | 判断缓存是否有效 |
| 回源率 | 未命中次数 / 总查询 | 与命中率互补 |
| 穿透率 | 查询不存在键的比例 | 识别恶意或无效请求 |
| 淘汰率 | 淘汰次数 / 写入次数 | 判断容量是否不足 |
| 热 key 集中度 | Top N key 的查询占比 | 识别倾斜 |
| 平均回源延迟 | 回源总耗时 / 回源次数 | 估算缓存失效的代价 |
命中率不是越高越好。把不常访问的数据也缓存起来,命中率可能上升,但内存被占满后淘汰增加,真实收益下降。更有用的指标是“命中带来的延迟节省”和“回源给下游的压力”。
治理指标
| 机制 | 指标 | 说明 |
|---|---|---|
| 限流 | 通过数、拒绝数、拒绝率 | 拒绝是保护生效,不是故障 |
| 熔断 | 状态、触发次数、半开成功数 | 状态变化比次数更有信息量 |
| 重试 | 重试次数、重试成功率、放大倍数 | 放大倍数 = 总请求 / 原始请求 |
| 超时 | 超时数、超时阶段分布 | 定位是哪一段耗尽预算 |
| 舱壁 | 各舱占用、排队、拒绝 | 确认隔离是否真的隔离 |
| 降级 | 降级触发次数、持续时间 | 确认降级有退出条件 |
| 幂等 | 重复请求数、命中去重数 | 判断客户端或消息是否重发 |
这些指标要能按调用方、按接口、按实例拆分。只有一个全局“熔断次数”,无法判断是哪个下游、哪个版本、哪台机器触发的。
公式速查
| 目的 | 公式 | 示例 |
|---|---|---|
| 并发数 | L = λ × W | 200 rps × 50ms = 10 |
| 到达率 | λ = 日请求量 × 峰值系数 / 86400 | 100 万 × 3 / 86400 ≈ 34.7 rps |
| 实例数 | N = 峰值 QPS / 单实例容量 × 冗余系数 | 500 / 200 × 1.5 ≈ 4 |
| 缓存命中后延迟 | 命中率 × 缓存延迟 + 未命中率 × 回源延迟 | 0.9×1ms + 0.1×50ms = 5.9ms |
| 重试放大 | 总请求 = 原始请求 × (1 + 重试率 × 重试次数) | 1 + 0.1×2 = 1.2 倍 |
| 错误预算 | (1 − SLO) × 总请求 | 0.001 × 100 万 = 1000 次 |
| 利用率 | 忙时间 / 总时间 | 45s / 60s = 75% |
公式只给数量级,不能替代实测。峰值系数、单实例容量、冗余系数都要用真实数据校准;没有数据时,写“示例值”。
单位换算
| 场景 | 换算 |
|---|---|
| 延迟 | 1s = 1000ms = 1,000,000µs |
| 吞吐 | 1k QPS = 1000 QPS;1 万 QPS = 10k QPS |
| 数据量 | 1KB = 1024B;1MB = 1024KB;1GB = 1024MB |
| 带宽 | 1Gbps ≈ 125MB/s(理论值) |
| 可用性 | 99.9% = 每月约 43.8 分钟 |
| 百分比 | 0.1% = 千分之一 = 1000 ppm |
报告里统一用 ms 和 QPS,避免同一张表里出现秒、毫秒、微秒混用。带宽和存储的 GB 含义不同(十进制与二进制),跨团队沟通时写清单位。
SLO 怎么写
SLO 不是“系统要稳定”,而是一句可计算的承诺。一个可用的写法包含四部分:
| 部分 | 例子 | 说明 |
|---|---|---|
| 指标 | 下单接口 P99 延迟 | 选能反映用户体验的指标 |
| 阈值 | ≤ 400ms | 必须是可测的数字 |
| 统计窗口 | 28 天滚动 | 固定窗口,避免月初月末漂移 |
| 达标比例 | 99.9% 的请求 | 与错误预算对应 |
完整表述是:“28 天滚动窗口内,下单接口 99.9% 的请求 P99 延迟不超过 400ms。” 这句话能直接换成查询,也能直接算错误预算。
SLI、SLO 与 SLA
| 概念 | 含义 | 归属 |
|---|---|---|
| SLI | 实际测量出的指标 | 监控系统 |
| SLO | 内部目标 | 研发与 SRE |
| SLA | 对外承诺,通常带赔付 | 商务与法务 |
SLA 一般比 SLO 宽松,留出内部缓冲。把 SLO 直接写成 SLA,等于没有缓冲;把 SLA 当 SLO,团队会长期在“刚好不赔钱”的边缘运行。
选择指标的三个原则
第一,从用户动作出发。用户关心“下单成功且不太慢”,不关心某台机器的 CPU。
第二,少而可行动。一个核心链路三到五个 SLO 足够,太多会没人看。
第三,能被现有数据计算。没有采集的指标,先补采集,再谈 SLO。
指标采集的三种方式
| 方式 | 原理 | 适合 | 代价 |
|---|---|---|---|
| 拉取 | 监控系统定期抓取 /metrics | 服务端指标、容器指标 | 需要暴露端点,短生命周期任务易漏采 |
| 推送 | 应用主动上报 | 批处理、边缘任务、短任务 | 需要网关与去重,网络抖动会丢点 |
| 日志/追踪派生 | 从日志或 span 聚合 | 业务事件、错误分类、链路延迟 | 存储成本高,实时性较差 |
三种方式可以并存,但同一指标不要用两种方式重复采集,否则会出现两个略有差异的数字,排查时互相打架。
标签与基数
标签让指标可下钻,也会让序列数量爆炸。安全的标签:接口名、状态码、实例、区域、版本。危险的标签:用户 ID、订单号、请求 ID、原始 URL、错误消息。
一个粗略的判断:标签取值超过几百个,就要考虑换成日志或追踪。时间序列数量 = 指标数 × 每个标签的取值数之积;四个标签各 100 个取值,就是一亿条序列。
从指标到行动
指标本身不产生价值,能触发动作才有价值。建议给每个核心指标配一条判断路径:
| 观察 | 下一步 | 不要做什么 |
|---|---|---|
| 业务成功率下降 | 看应用错误率与下游错误率 | 直接重启 |
| P99 上升、QPS 不变 | 看排队、连接池、下游延迟 | 先加机器 |
| QPS 下降、拒绝率上升 | 看限流阈值与依赖容量 | 直接调高限流 |
| 超时率上升、下游延迟正常 | 看本服务排队与 GC | 只调大超时 |
| 单实例指标异常 | 摘流并保留现场 | 立即重启覆盖证据 |
这张表的作用是防止“看到指标就动手”。大部分错误处置不是因为不看指标,而是因为看了指标却跳过了定位。
十种常见误读
第一,平均延迟正常就认为系统正常。平均值掩盖尾部,P99 才是用户感知。
第二,把两个实例的 P99 平均。分位数不可平均,必须合并直方图。
第三,把到达数当吞吐。开环压测的到达数由速率决定,与系统能力无关。
第四,把拒绝算成错误。限流拒绝是保护生效,应单独统计。
第五,只看 QPS 不看完成率。高 QPS 伴随高超时,等于系统在空转。
第六,用 CPU 使用率判断瓶颈。CPU 不高但延迟高,问题可能在锁、I/O 或下游。
第七,忽略排队时间。连接池等待、队列等待都是用户真实等待。
第八,把命中率当缓存效果。命中率上升但回源延迟没降,说明缓存的是便宜数据。
第九,拿不同口径的数字比较。客户端 P99 和服务端 P99 不是一回事。
第十,用单次压测证明容量。单次结果受机器、数据、预热影响,至少三次并给区间。
最小仪表盘
如果只能保留一屏指标,建议按下面排列:
| 区域 | 指标 | 作用 |
|---|---|---|
| 顶部 | 业务成功率、订单量 | 判断是否影响用户 |
| 左中 | QPS、完成率、拒绝率 | 判断流量与保护状态 |
| 右中 | P50/P95/P99、超时率 | 判断延迟结构 |
| 左下 | 下游延迟、错误率、连接池占用 | 定位瓶颈层 |
| 右下 | CPU、内存、GC、协程数 | 判断资源饱和 |
每个指标都要能下钻到接口、实例和时间段。不能下钻的仪表盘,只能告诉你“出事了”,不能告诉你“去哪看”。
常见错误与边界
第一,指标没有单位。延迟是 ms 还是 s,吞吐是每秒还是每分钟,必须在图名和字段名里写清。
第二,采样率不一致。有的指标每秒采一次,有的每分钟一次,放在同一张图上会出现虚假的尖峰和平台。
第三,聚合维度丢失。只保留全局值,无法区分是单实例故障还是全站问题。
第四,保留时间太短。故障复盘常需要上周甚至上月的对比数据,至少保留 30 天。
第五,指标基数爆炸。把用户 ID、订单号放进标签,会导致时间序列数量失控;需要高基数分析时用日志或追踪,不用指标。
第六,把目标值画成实测值。SLO 线和实际延迟线要视觉区分,否则会被误读为“一直达标”。
小结
性能指标分业务、应用、依赖、主机四层。延迟看分位数和分布,不看平均;分位数不能平均,只能合并直方图。吞吐要区分到达、完成和拒绝。并发用利特尔法则估算,排队在利用率超过 70%–80% 后非线性上升。错误率、超时率和拒绝率要分开统计,可用性目标要能换算成错误预算。
资源、缓存和治理指标用于解释原因,不用于下最终结论。所有指标都要带单位、口径、聚合维度和采样周期。附录 C 把这些读数放进故障演练的检查表,确保演练时看的是同一组信号。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。