3. SLI 设计原则:如何为不同服务定义有效的 SLI

SLI(Service Level Indicator)是 SLO 体系的基石。一个错误的 SLI 定义,会导致告警误报、自愈误触发,甚至让整个可观测性体系失去信任。本章聚焦四大黄金信号——延迟、错误率、吞吐量、饱和度,给出可落地的 SLI 定义原则与实战模板。

核心原则:SLI 必须可测量、可对比、可聚合、可归因。不可测量的指标不能作为 SLI;不可对比的指标无法设定 SLO;不可聚合的指标无法做全局评估;不可归因的指标无法驱动 Runbook 自愈。

3.1 四大黄金信号与 SLI 定义矩阵

Google SRE 经典四大黄金信号(The Four Golden Signals)是 SLI 设计的基础框架。下表给出每个信号的标准定义、典型测量方式与适用场景:

黄金信号 SLI 定义公式 典型测量方式 适用服务类型
延迟 请求在阈值内完成的百分比 P50 / P95 / P99 分位延迟 API 网关、数据库、微服务
错误率 成功请求 / 总请求 × 100% HTTP 5xx / 4xx 占比 Web 服务、RPC 调用
吞吐量 单位时间处理请求数 RPS / QPS / TPS 消息队列、批处理、CDN
饱和度 资源使用量 / 总容量 × 100% CPU / 内存 / 连接数利用率 基础设施、中间件、存储
实战建议:不要试图为每个服务定义全部四个 SLI。优先选择最能反映用户体验的 1-2 个信号。例如:用户侧 API 优先选延迟 + 错误率;消息队列优先选吞吐量 + 饱和度。

3.2 延迟 SLI:分位阈值与窗口设计

延迟 SLI 最常见的错误是使用平均值。平均值会掩盖长尾问题,导致 SLO 虚高。正确的做法是使用分位值(Percentile)并配合滑动窗口。

标准定义模板:

SLI: 延迟
指标名: http_request_duration_seconds
聚合方式: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))
阈值: ≤ 200ms
窗口: 过去 5 分钟
SLI 值: 满足阈值的请求占比 ≥ 99.9%

分位选择对照表:

分位 含义 适用场景 典型阈值
P50 半数请求的延迟 内部批处理、离线任务 500ms - 2s
P95 95% 请求的延迟 用户交互 API、Web 页面 200ms - 1s
P99 99% 请求的延迟 实时交易、支付、流媒体 100ms - 500ms
P999 99.9% 请求的延迟 金融核心、医疗、自动驾驶 50ms - 200ms
避坑提示:P99 延迟在低流量服务中可能不稳定。当每分钟请求数 < 100 时,建议使用 P95 或扩大窗口至 10 分钟,避免 SLI 抖动导致误告警。

3.3 错误率 SLI:区分有效错误与无效错误

错误率 SLI 的核心挑战是区分“服务自身错误”与“客户端错误”。将所有 4xx 计入错误率会导致 SLI 虚低,触发不必要的告警。

推荐的错误分类策略:

  • 服务端错误(5xx): 必须计入 SLI。代表服务自身故障。
  • 客户端错误(4xx): 选择性计入。仅当 4xx 由服务端配置错误导致时(如 429 限流、401 认证失败)才计入。
  • 超时错误: 必须计入。即使 HTTP 状态码为 200,只要响应时间超过客户端超时阈值,应视为错误。
  • 业务逻辑错误: 自定义错误码。如订单创建失败、支付扣款失败,需通过应用层指标暴露。

标准定义模板:

SLI: 错误率
指标名: http_requests_total{status=~"5.."}
聚合方式: sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m]))
阈值: ≤ 0.1%
窗口: 过去 5 分钟
SLI 值: 错误请求占比 ≤ 0.1%

3.4 吞吐量 SLI:基线对比与容量预警

吞吐量 SLI 不追求“越高越好”,而是追求“与预期基线一致”。吞吐量突降通常代表服务不可用,突增则可能触发饱和度问题。

定义方法:

  • 阈值: 吞吐量 ≥ 1000 RPS。适用于容量规划明确的场景。
  • 相对基线: 当前吞吐量 ≥ 过去 7 天同时段均值的 80%。适用于流量波动大的服务。
  • 同比对比: 当前吞吐量 ≥ 上周同一天的 90%。适用于周期性业务。

标准定义模板:

SLI: 吞吐量
指标名: http_requests_total
聚合方式: sum(rate(http_requests_total[5m]))
阈值: ≥ 历史基线均值的 80%
窗口: 过去 5 分钟,对比过去 7 天同时段
SLI 值: 当前吞吐量 ≥ 基线 × 0.8

3.5 饱和度 SLI:利用率与排队深度

饱和度 SLI 是容量规划的早期预警信号。当资源利用率接近 100% 时,延迟和错误率会急剧恶化。饱和度 SLI 的目标是在资源耗尽前触发告警。

关键指标与阈值:

资源类型 SLI 指标 预警阈值 危险阈值
CPU node_cpu_seconds_total{mode="user"} 80% 95%
内存 node_memory_MemAvailable_bytes 剩余 20% 剩余 10%
磁盘 I/O node_disk_io_time_seconds_total 利用率 70% 利用率 90%
连接池 db_connections_used / db_connections_max 80% 95%
队列深度 queue_length 100 500
关键认知:饱和度 SLI 的阈值应低于 100%。当 CPU 达到 95% 时,延迟可能已经恶化 10 倍。饱和度 SLI 的目标是“在用户感知到问题之前”触发自愈。

3.6 SLI 设计流程与决策树

以下 SVG 图展示了从服务类型到 SLI 定义的完整决策流程,帮助你在实际项目中快速落地:

SLI 设计决策树 服务类型识别 用户交互型 数据处理型 基础设施型 SLI 选择 延迟 (P95 ≤ 200ms) 错误率 (5xx ≤ 0.1%) 吞吐量 (基线对比) SLI 选择 吞吐量 (RPS ≥ 基线) 延迟 (P99 ≤ 500ms) 错误率 (重试率 ≤ 1%) SLI 选择 饱和度 (CPU ≤ 80%) 饱和度 (内存 ≤ 80%) 饱和度 (连接池 ≤ 80%) 输出:SLI 定义文档 + 阈值 决策要点 1. 每个服务至少选择 1 个黄金信号,最多不超过 3 个 2. 优先选择用户直接感知的信号(延迟、错误率) 3. 饱和度 SLI 作为容量预警,阈值应低于 100%

3.7 SLI 定义模板与 Runbook 联动

每个 SLI 定义必须包含以下字段,才能与 Runbook 自愈流程联动:

字段 说明 示例
SLI 名称 唯一标识 api_p99_latency
指标来源 Prometheus / Datadog / 自定义 Prometheus: http_request_duration_seconds
聚合方式 分位 / 比率 / 均值 histogram_quantile(0.99, rate(...))
阈值 数值 + 单位 ≤ 200ms
窗口 评估时间范围 5m
SLO 目标 SLI 达标百分比 99.9%
自愈 Runbook ID 触发哪个 Runbook runbook_latency_spike_v2
实战模板(YAML 格式):
sli_definitions:
  - name: api_p99_latency
    service: user-api
    signal: latency
    metric: http_request_duration_seconds
    aggregation: histogram_quantile(0.99, rate(...))
    threshold: 200ms
    window: 5m
    slo_target: 99.9%
    runbook_id: runbook_latency_spike_v2
    severity: critical

3.8 常见 SLI 设计错误与修正

以下列表总结了 SLI 设计中最常见的五个错误,以及对应的修正方案:

  • 错误 1:使用平均值代替分位值。 修正:改用 P95 或 P99,暴露长尾问题。
  • 错误 2:将所有 4xx 计入错误率。 修正:仅计入服务端错误(5xx)和业务错误码。
  • 错误 3:吞吐量 SLI 使用阈值。 修正:改用相对基线对比,适应流量波动。
  • 错误 4:饱和度 SLI 阈值设为 100%。 修正:设为 80% 预警、95% 危险,预留缓冲。
  • 错误 5:SLI 窗口过短导致抖动。 修正:低流量服务使用 10-15 分钟窗口,高流量服务使用 5 分钟。