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 定义的完整决策流程,帮助你在实际项目中快速落地:
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 分钟。