文档目录

1.4 SLO 的四要素写法

上一节:1.3 测量点 | 下一节:1.5 延迟预算 配套代码:01-metrics-and-slo/04-writing-slo


一句话结论

「P99 < 200 ms」不是一个 SLO,只是一半句话。 完整的 SLO 必须包含四要素:指标 + 阈值、负载与数据前提、测量点、持续时长。少了任何一项,验收时都会吵架(而且通常是上线后才吵)。


一、用「验收合同」理解 SLO

假设你要装修房子,和施工队签合同。你在合同上写:

「装修要好。」

施工队会问你一堆问题:

你的原话 施工队必须追问 对应 SLO 的要素
「装修要好」 「哪方面好?水电?墙面?隔音?」 指标
「墙面要平」 「多平算平?误差几毫米?」 阈值
「要平」 「房子多大?什么结构?现在什么状况?」 负载与数据前提
「要平」 「用谁的尺子量?从哪量到哪?」 测量点
「要平」 「验收时看一次,还是住半年后还得平?」 持续时长

SLO 就是你和系统签的验收合同。 一份只有「装修要好」的合同,结局一定是扯皮。


二、四要素详解

要素 1:指标 + 阈值

必须明确是哪个指标(P99?P95?错误率?吞吐?)和具体数值。

❌ 模糊 ✅ 明确
「响应要快」 P99 < 200 ms 且 P999 < 500 ms
「要稳定」 错误率 < 0.1%,超时率 < 0.05%
「能扛住」 稳定支撑 500 RPS

要点:延迟类 SLO 至少写两个分位数(P99 + P999,或 P95 + P99)。只写一个分位数时,你不知道「剩下那 1% 有多糟」。

要素 2:负载与数据前提

这是最常被忽略、也是争议最大的一项。

同一份代码,在不同负载下的表现可以差几个数量级:

前提 为什么必须写
QPS / 并发模型 1 RPS 和 10000 RPS 是两个世界(第 0 章的排队效应)
数据量 1 万行可能全表扫描,1 亿行走索引——执行计划不同,结论不可迁移
数据分布 均匀分布会让缓存命中率虚高、锁竞争虚低;真实业务是幂律分布
缓存状态 冷缓存(发布后)与热缓存的 P99 可以差几十倍

要素 3:测量点

必须写明数据是在哪测的(见 1.3 节):

  • 从压测客户端观测(最接近用户)
  • 从网关观测
  • 从应用内埋点观测(只代表服务自身处理时间)

这三个数字不能互换,所以合同里必须写清楚「用谁的尺子量」。

要素 4:持续时长与验收方式

必须写 为什么
持续多久 跑 30 秒可能只是 JVM 冷启动阶段,跑 10 分钟才进入稳态
预热多久不计入 预热期数据混入统计会让 P99 虚高
取几轮、取哪一轮 跑 3 轮取中位数,还是取最好的一轮?(后者是自欺)

三、完整 SLO 的写法示例

❌ 不完整(常见的写法)

订单查询接口 P99 < 200 ms。

✅ 完整

订单查询接口(GET /orders/{id}) 在 500 RPS 恒定到达率、1000 万行订单数据、幂律访问分布(前 1% 用户占 30% 请求)、缓存预热 5 分钟后 的条件下: 从压测客户端观测,稳态维持 10 分钟:

  • P99 < 200 ms
  • P999 < 500 ms
  • 错误率 < 0.1%
  • 单实例 4C8G,无外部缓存

对比一下:第一句话用的是「读一遍就懂,但没法验证」;第二句话任何人都能照着复现。

建议同时定义一条「崩溃线」

SLO 是「合格线」,但压力测试还需要一条「终止线」:

降级判定:当 P99 > 1 s 或错误率 > 1% 持续 30 秒,视为不可接受,压力测试应停止并记录此时的负载与状态。

有了这条线,压力测试才有明确的终点,也才能测出「崩溃点」在哪里。


四、SLO 不是越快越好

这一节要防一个常见误区:把 SLO 定得极其严格。

后果 说明
成本暴涨 P99 从 200 ms 压到 50 ms,可能要多加 3 倍机器或引入复杂缓存
团队疲于救火 无法达标 → 告警天天响 → 所有人开始忽略告警
掩盖真正的目标 用户可能根本感知不到 150 ms 和 200 ms 的差距

SLO 应该定在「用户能感知到差别的那条线」附近,而不是「技术上能压到多快」。

判断方法:问业务方「如果这个操作要等 500 ms,用户会怎样?」「1 秒呢?」「2 秒呢?」——通常能问出一个「再慢就会有明显流失」的临界点,SLO 就设在它稍前面一点。

补充概念:SLO 对应一个「错误预算」——如果 SLO 是 99.9%,那一个月内允许有 0.1% 的请求违约。这个预算可以被「花掉」,也是第 8 章告警策略的基础。


五、SLO 表模板

字段 示例
接口 GET /orders/{id}
关键路径 客户端 → 网关 → 订单服务 → PostgreSQL
目标 QPS 500(峰值),恒定到达率模型
P50 / P95 / P99 / P999 20 / 60 / 200 / 500 ms
错误率阈值 < 0.1%
负载前提 1000 万行数据,幂律分布,缓存预热 5 分钟
测量点 压测客户端
持续时长 稳态 10 分钟
环境 单实例 4C8G,无外部缓存
崩溃线 P99 > 1 s 或错误率 > 1% 持续 30 秒
测量日期与版本 2025-xx-xx,commit abc1234

六、本节小结

  1. SLO 的四要素:指标 + 阈值、负载与数据前提、测量点、持续时长。缺一项就无法验收。
  2. 负载前提是最常被忽略、也最容易引发争议的一项(QPS、数据量、数据分布、缓存状态)。
  3. 延迟类 SLO 至少写两个分位数,并单独列出错误率。
  4. 建议同时定义一条「崩溃线」,作为压力测试的终止条件。
  5. SLO 不是越快越好——定在「用户能感知的临界点」稍前面,否则成本和告警噪声会失控。

七、自测

  1. 下面这条 SLO 缺了哪些要素?

    「用户服务接口的响应时间不超过 100 ms,错误率低于 0.1%。」

  2. 为什么「数据分布」必须写进 SLO 前提?请举一个它会导致结论完全不同的例子。
  3. 一个团队把 SLO 定成「P99 < 10 ms」,结果天天告警、天天加班。请说出这个 SLO 可能的三个问题。
  1. 缺:① 负载前提(多少 QPS?数据量多大?缓存是否预热?)② 测量点(客户端 / 网关 / 应用内?)③ 持续时长与验收方式(跑多久?预热多久?取几轮?)④ 指标不够明确(「响应时间不超过 100 ms」是 P50 还是 P99?还是最大值?)。⑤ 没有说明环境(单实例规格?副本数?)。
  2. 因为分布决定了缓存命中率和锁竞争程度。例子:一个「按用户 ID 查订单」的接口,用均匀分布压测时,每个 key 访问概率相同,缓存命中率可能只有 30%(因为 key 太分散);而真实业务是幂律分布(少数活跃用户占了大部分请求),缓存命中率可能达到 85%。两者的 P99 会差好几倍——用均匀分布测出的「达标」,在真实流量下可能完全不达标(反之也可能过度悲观)。
  3. ① 可能远超用户感知需求:用户根本区分不出 10 ms 和 50 ms,这个 SLO 是在追求数字而不是体验;② 可能超出性价比:为了从 50 ms 压到 10 ms 需要引入复杂缓存、加机器、做预计算,成本可能翻几倍;③ 没有区分场景:所有接口一刀切,而实际上「下单」和「查历史订单」的用户容忍度完全不同。正确做法:按接口分级(例如核心链路 P99 < 100 ms、一般查询 P99 < 300 ms),并让告警基于错误预算(第 8 章),而不是瞬时超标。