24.2 SLI、SLO 与错误预算
“数据库必须高可用”不是 SLO。它没有说明:
- 测什么;
- 从哪里测;
- 什么算好;
- 什么进入分母;
- 多长窗口;
- 允许多少失败;
- 没有数据怎么办;
- 未达标后改变什么。
可执行的 SLO 至少是:
service + journey
+ eligible event
+ good event
+ measurement point
+ target
+ window
+ exclusions
+ missing-data semantics
+ error-budget policy本节从用户事件开始,而不是从 PostgreSQL exporter 已经有什么指标开始。
24.2.1 可用性、延迟、正确性与数据新鲜度
先分清五个术语
| 术语 | 含义 | 例子 |
|---|---|---|
| SLI | 实际测量值 | 28 天 good/eligible = 99.93% |
| SLO | SLI 的内部目标 | 28 天至少 99.9% |
| SLA | 对外承诺与后果 | 未达 99.9% 触发服务补偿 |
| error budget | 目标允许的 bad events | 0.1% eligible events |
| control objective | 不适合平均掉的控制 | 无未解释数据差异 |
SLA 可能引用 SLO,但两者不是同义词。本章只设计内部服务治理合同,不起草 法律或商务条款。
ratio SLI 的基本形式
Google SRE Workbook 建议优先把 SLI 写成 good events 与 total/eligible events 的比率,因为它自然落在 0–1,并能与预算相连:
$$ \text{SLI}
\frac{\text{good events}} {\text{eligible events}} $$
关键不是公式,而是 event classification。
可用性:成功必须能核对
本章的下单可用性:
eligible
authorized
syntactically valid
admitted at application boundary
good
declared success response
AND idempotency token resolves to exactly one committed order事件分类:
| 情形 | eligible | good | 原因 |
|---|---|---|---|
| 合法请求,提交并返回成功 | 是 | 是 | 完整成功 |
| 合法请求,应用 500 | 是 | 否 | 用户失败 |
| 合法请求,连接在 commit 后断开,尚未核对 | 是 | 否,直到核对 | 不把 unknown 假装成功 |
| 合法请求,返回成功但查不到订单 | 是 | 否 | 错误承诺 |
| 合法请求,创建两张订单 | 是 | 否 | 幂等性失败 |
| 语法不符合已发布 API | 否 | 不适用 | 未被服务接纳 |
| 正确拒绝无权限身份 | 否 | 不适用 | 安全合同正常工作 |
| 应有权限却因配置错误被拒绝 | 是 | 否 | 服务失败,不是“安全排除” |
| 服务接纳后客户端取消 | 是 | 依实际结果 | 不能事后从分母删除 |
HTTP 2xx、SQL 无异常或 transaction commit 任一单独都不够。good event 应当
表达用户获得的承诺。
不要用“成功查询数”混合所有操作
高流量健康查询会淹没低流量关键操作:
health endpoint 100,000,000 good
place-order 1,000 all failed
combined SLI 99.999%所以 operation class 必须按后果拆分:
place-order;read-own-order;admin-report;background-reconcile。
不能为得到好看的总数,把不同用户旅程放入同一个分母。
延迟:问“多少事件够快”,不要只看平均值
本章延迟目标:
eligible:
same admitted order attempts as availability
good:
final reconciled response completes within 250 ms
target:
99% over rolling 28 days相应 SLI:
$$ \text{latency SLI}
\frac{\text{eligible events with latency} \le 250\text{ ms}} {\text{eligible events}} $$
快速失败不应自动成为 latency good。若一个请求 5 ms 返回 500,它在用户旅程 上既不可用,也没有完成目标动作。
平均延迟会掩盖尾部:
99 requests * 10 ms
1 request * 10 s
average 109.9 ms平均值看似低于 250 ms,但最慢用户等了十秒。比例 SLO 或分位数更能表达尾部。 用于预算时,固定阈值的 good/eligible 比例比“p99 的月平均”更容易严格累计。
histogram 边界必须在采集前确定
若 histogram 没有 0.25 秒 bucket,事后无法从聚合数据精确回答“多少请求
低于 250 ms”。因此 SLO 先于 instrumentation:
SLO threshold 250 ms
-> histogram bucket includes 0.25
-> counter labels fixed
-> recording rule fixed
-> alert query fixed这正是本章先写观察契约、下一章才实现指标的原因。
正确性:不要用 99.9% 原谅数据错误
某些正确性可以定义为比例,例如非关键搜索结果质量。但本章的订单不变量:
- 一个 idempotency token 只对应一张订单;
- tenant A 看不到 tenant B;
- 金额和状态迁移合法;
- acknowledged order 不丢失;
使用 control objective:
success:
scheduled or incident reconciliation finds zero unexplained mismatch
failure:
page
freeze affected writes
preserve reconciliation boundary
investigate and repair under authority这不是声称软件永远不会出错。它是规定“发现一条此类错误时,组织不能用剩余 错误预算继续正常发布”。
正确性 control 需要:
- 明确覆盖哪些 invariant;
- 固定 input boundary;
- 记录 job/version/query hash;
- 区分 known exception 与 unexplained mismatch;
- 防止 repair 覆盖原始证据;
- 定期证明 reconciliation 本身仍运行。
数据新鲜度:必须与某次 commit 关联
“replica lag 小于 5 秒”有至少三种含义:
- 接收 WAL 与 primary 的 byte distance;
- replay 记录的 timestamp 距当前 wall clock;
- 用户的一次已提交写入在读取路径上何时可见。
用户关心第三种。前两种用于诊断。
本章 freshness event:
eligible
probe has a known committed token
probe uses the declared read path
good
token becomes visible within 5 seconds流程:
write unique token
-> reconcile commit
-> poll replica/read endpoint
-> visible_at - committed_at这样能覆盖:
- WAL 传输和 replay;
- HAProxy route;
- PgBouncer;
- query path;
- cache;
- 应用序列化。
now() - pg_last_xact_replay_timestamp() 在空闲时会增长,不能替代这个 probe。
恢复就绪不是可用性 SLO
服务连续运行 28 天,不代表能从灾难恢复。恢复 control:
success:
isolated restore drill passed within 90 days
AND after material recovery-path changes
verification:
backup identity
required WAL
target marker
application invariants
isolated listener
stopped postmasterbackup completed 只能作为输入证据。第 21 章已经证明:
backup
-> WAL coverage
-> restore
-> start in recovery
-> reach target
-> promote in isolation
-> verify data
-> stop缺任一阶段,不能把 recovery readiness 标成 good。
四类目标的组合
| 目标 | 用户问题 | 主要测量 | 失败后果 |
|---|---|---|---|
| availability | 能否完成动作 | outcome counter + reconciliation | consume budget/page |
| latency | 是否足够快 | edge histogram | consume budget/page |
| freshness | 已提交状态何时可见 | commit-token probe | consume budget/route change |
| correctness | 数据是否可信 | invariant reconciliation | freeze/page |
| restore readiness | 能否恢复 | isolated drill evidence age | block risky change |
一个绿色目标不能抵消另一个红色 control。
24.2.2 测量点、统计窗口与排除条件
测量点越靠近用户,覆盖越完整
一条请求的观测点:
client
-> CDN / gateway
-> application
-> HAProxy
-> PgBouncer
-> PostgreSQL| 测量点 | 能覆盖 | 看不到 |
|---|---|---|
| PostgreSQL | SQL、transaction、lock、I/O | gateway/app/network errors |
| PgBouncer | pool wait、server assignment | app correctness |
| application | user operation、business outcome | client edge/network |
| gateway | user-visible HTTP result | commit correctness unless correlated |
| client/synthetic | end-to-end experience | 所有真实用户分布 |
通常使用:
primary SLI application edge or gateway
correctness join commit/outcome reconciliation
fallback independent synthetic path
diagnosis Pigsty + PostgreSQL component telemetry若应用不能立刻增加指标,可以暂用 synthetic probe,但要记录 coverage gap。不要 因为 PostgreSQL 指标更容易获得,就悄悄把 SLO 测量点向内移动。
事件记录与时序指标各有用途
原始事件适合核对:
event_time
operation_class
eligible
outcome_class
latency
release
synthetic/real
correlation_token_hash时序 counter/histogram 适合在线聚合与告警。不要把每个 order id 放进 metric label;需要 drill-down 时,用受控 event/log store 通过 correlation id 连接。
一个教学用关系表:
CREATE TABLE slo_event (
observed_at timestamptz NOT NULL,
service_id text NOT NULL,
operation_class text NOT NULL,
eligible boolean NOT NULL,
good boolean,
latency_ms integer,
outcome_class text NOT NULL,
release_id text NOT NULL,
synthetic boolean NOT NULL
);生产上不一定用 PostgreSQL 保存所有事件;这里用 SQL 展示语义。
过去 28 天:
WITH eligible AS (
SELECT *
FROM slo_event
WHERE service_id = 'pg36_shop'
AND operation_class = 'place-order'
AND observed_at >= clock_timestamp() - interval '28 days'
AND eligible
)
SELECT
count(*) AS eligible,
count(*) FILTER (WHERE good) AS good,
count(*) FILTER (WHERE NOT coalesce(good, false)) AS bad,
count(*) FILTER (WHERE good)::numeric
/ NULLIF(count(*), 0) AS sli
FROM eligible;good IS NULL 若表示 unknown,预算查询应暂按 bad 或单独阻断,而不能由
count(*) FILTER (WHERE good) 自动消失后仍声称完整。
延迟:
SELECT
count(*) FILTER (
WHERE good
AND latency_ms <= 250
)::numeric / NULLIF(count(*), 0) AS latency_sli
FROM slo_event
WHERE service_id = 'pg36_shop'
AND operation_class = 'place-order'
AND observed_at >= clock_timestamp() - interval '28 days'
AND eligible;这里要求 availability good 且足够快,避免快速错误通过延迟目标。
滚动窗口与日历报告是两件事
本章采用滚动 28 天:
at every evaluation:
[now - 28 days, now]优点:
- 每天都代表同样长度;
- 没有月初“预算重置”错觉;
- 适合持续 alert/budget decision。
28 天是四周,不是自然月。财务、客户或合规可能要求 calendar month/quarter 报告,应单独生成,不要把两个窗口混成一个数字。
推荐节奏:
continuous recording and alerts
weekly service/error-budget summary
quarterly objective and policy review
event-driven review after major incident or architecture changeGoogle SRE 的 SLO 实施章节也讨论四周滚动窗口、周汇总和季度报告。这些是实践 起点,不是对所有组织的强制周期。
长窗口防止遗忘,短窗口缩短响应
只有月窗:
- 稳定但反应慢;
- 一次快速事故可能在总体比例中暂时不显眼。
只有 5 分钟窗:
- 反应快;
- 流量低时一个错误就剧烈波动;
- 容易被短暂 blip 打扰。
multiwindow 同时要求长窗和短窗超过同一 burn threshold:
long window proves material budget consumption
short window proves condition is still active这比简单 error_rate > 1% for 5m 更贴近 SLO 后果。
排除条件应在事件发生前定义
本章默认排除:
- 在 admission 前被正确拒绝的语法错误;
- 正确拒绝的越权身份;
- 服务接纳工作前的客户端取消。
默认不排除:
- 计划维护;
- 发布导致的错误;
- 内部依赖故障;
- operator mistake;
- 容量不足;
- “已知问题”;
- 未核对的 unknown result。
原因很简单:用户并不会因为中断有 change ticket 就得到服务。
若产品真的有公开维护窗口,应在服务合同中定义独立承诺,而不是事故后从事件 表删除数据。
例外必须不可追溯篡改
一个 SLO exception 至少记录:
exception_id
approved_before_event
exact start/end
affected operation classes
reason
independent approver
event count before exclusion
event count after exclusion
evidence manifest禁止:
SLO missed
-> label incident "maintenance"
-> recompute report
-> target met这种做法破坏了 SLO 作为决策工具的价值。
telemetry 缺失不能等于零错误
PromQL 查询经常在 series 消失时返回空向量。若 dashboard 把空值渲染为 0, 会出现:
exporter down
ingestion broken
rule evaluation broken
dashboard shows 0 errors每个 SLI 必须定义:
- expected series cadence;
- no-traffic 与 missing telemetry 如何区分;
absent()或 freshness check;- 独立 synthetic fallback;
- metamonitoring route。
本章统一原则:
missing = unknown-and-monitored
missing != healthy低流量服务不能照搬高流量阈值
若 5 分钟只有两个请求,一个失败就是 50% error rate。可以:
- 运行 end-to-end synthetic probe;
- 延长窗口;
- 合并后果相同的 operation,但不能用无关流量稀释;
- 使用明确评审的 time-based 或 user-minute SLI;
- 对关键 batch 用“最近两次应完成周期”定义 freshness;
- 将零容忍 correctness control 独立出来。
降低告警灵敏度不能变成不观察低流量关键路径。
标签维度既要可诊断,也要有界
建议:
service
operation_class
environment
region / cell
outcome_class
synthetic谨慎:
release_id 有界保留或 exemplar
sql_fingerprint
error_class禁止无界:
customer_id
tenant_id
order_id
raw_sql
error_messagePigsty 组件关联使用 cls、ins、ip;应用 SLI 使用 service 维度。不要把
实例 identity 当成服务 identity。
24.2.3 错误预算如何约束变更速度
预算不是允许主动制造错误
目标 99.9% 不表示可以计划使用 0.1% 伤害用户。预算用于在可靠性与变化之间 做有数据的选择:
budget healthy
-> can take reviewed change risk
budget burns fast
-> stop adding correlated risk
-> investigate user-impacting failures
budget exhausted
-> prioritize reliability
-> only emergency/security/legal/direct-repair exceptions100% 通常不是合适的 ratio target:
- 它没有 error budget;
- 任一测量噪声都成为违约;
- 团队会隐藏错误或停止变化;
- 它仍不能保证 correctness 和 recovery。
“永不丢数据”这样的要求应拆成 durability/control design,而不是伪装成 100% request ratio。
事件预算
若目标 $T$,eligible event 数 $N$:
$$ B_\text{events}=N(1-T) $$
本章:
N = 10,000,000
T = 0.999
B = 10,000 bad events观察到 7,500 bad:
$$ \text{budget consumed} = \frac{7500}{10000} = 75% $$
剩余 25%,进入 constrained 边界。
等价时间预算
时间可用性解释:
$$ B_\text{time}=W(1-T) $$
28 天、99.9%:
window 2,419,200 seconds
budget 2,419.2 seconds
40.32 minutes不要把它用于篡改 event-based 结果。高峰五分钟可能消耗的用户失败数远大于 低谷四十分钟。
burn rate
$$ \text{burn}
\frac{\text{observed bad ratio}} {1-T} $$
99.9% 的 allowed bad ratio 是 0.001:
| observed bad | burn |
|---|---|
| 0.05% | 0.5x |
| 0.10% | 1x |
| 0.60% | 6x |
| 1.44% | 14.4x |
| 10% | 100x |
1x 持续整个窗口恰好用完预算;14.4x 若持续,会非常快地耗尽。
为什么 fast burn 使用 AND
可用性 page:
bad_ratio_1h > 14.4 * 0.001
AND
bad_ratio_5m > 14.4 * 0.001长窗证明这不是一个无关紧要的点;短窗证明问题仍在。若使用 OR:
- 长窗已受历史事故影响但当前恢复,仍持续 page;
- 短窗单个噪声也会 page。
具体查询和 for 将在第 25 章实现。
四状态政策
本章 policy:
healthy:剩余大于 50%
- 正常评审发布;
- 仍要投资预防性可靠性;
- 不能因为预算充足跳过变更控制。
watch:25%–50%
- 减少并发 L2/L3;
- 每周分析最大预算消费者;
- 提前安排修复。
constrained:0%–25%
- 暂停非必要高风险功能;
- material change 需 service + platform 共同例外;
- 可靠性修复优先;
- 加强观察窗口。
exhausted:小于或等于 0
- 冻结非紧急高风险变更;
- 打开 incident 或 reliability review;
- 只允许直接恢复可靠性、安全、法律或紧急动作;
- 恢复节奏需要记录共同决定。
policy 必须说明哪些变化仍能进行
“冻结所有变更”可能阻止修复。应按意图和风险判断:
| 变化 | exhausted 时 |
|---|---|
| 新推荐功能 | 通常冻结 |
| 无关 UI 文案 | 可按低风险政策评审 |
| 修复当前错误原因 | 允许,但仍需安全门禁 |
| 安全凭据紧急轮换 | 允许,不能跳过证据 |
| 扩容避免迫近 outage | 允许,需验证与回退 |
| 重新定义 eligible 排除错误 | 禁止 |
| 降低 SLO 让报表变绿 | 先与 stakeholder 重新谈判,不能追溯 |
错误预算影响速度,不取消风险管理。
预算的所有者
service owner
owns user priority and release trade-off
platform/reliability owner
owns measurement integrity and technical risk
data/security owner
can impose non-budget controls for correctness/privacy不能由开发团队单方面改分母,也不能由 DBA 单方面降低服务目标。
防止四种 gaming
事后改变 eligibility
错误发生后把它归类为“不算用户请求”。
把事故改名 maintenance
变更单不能让用户中断消失。
把测量点移到内部
gateway 错误很多时,改看 SELECT 1。
用大流量稀释关键路径
把失败的下单和成功的健康检查合并。
防线:
- versioned SLO policy;
- event classifier tests;
- before/after counts;
- independent approval;
- immutable exception;
- change log;
- 定期抽样原始结果。
从 SLO 到变更门禁
变更请求要读取当前预算:
change risk L2 schema release
budget state constrained
purpose new feature
decision defer
change risk L2 schema release
budget state constrained
purpose remove source of current failures
decision allow with joint approval, narrow blast radius,
canary, stop line and rollback门禁输入必须固定时间:
budget snapshot at approval
budget snapshot immediately before execution审批后若 fast-burn page 触发,执行器应自动停止,而不是拿旧截图继续。
本章 SLO 文件
完整机器可读政策:
重点不是 JSON 语法,而是同一 objective id 在三份文件中保持一致:
SLO-AVAILABILITY
definition in slo-policy
source/query/missing in observation-contract
burn alerts in alert-candidatesvalidator 会拒绝 missing objective、100% target、planned-maintenance exclusion、 missing=healthy 和 exhausted-with-no-action。
SLO 评审清单
- journey 与 operation class 是否明确?
- eligible 是否能由代码或查询判定?
- good 是否包含用户真正获得的结果?
- unknown outcome 如何核对?
- 快速失败会不会错误通过 latency?
- correctness 是否被独立 control 保护?
- freshness 是否与 commit token 关联?
- restore readiness 是否来自恢复而非 backup job?
- 测量点是否尽可能靠近用户?
- rolling window 与 calendar report 是否分开?
- exclusion 是否事先定义、不可追溯?
- missing telemetry 是否变成 unknown?
- 低流量是否有 synthetic 或其他明确策略?
- event budget 算术是否有自动测试?
- budget state 是否改变变更行为?
- 是否禁止用改分母、改测量点和混流量 gaming?
- 目标是否由真实 stakeholder 批准,还是仍为教学输入?
当这些问题有答案时,SLO 才能驱动告警和变更。下一节把这些决定放进可执行 操作流程。
上一节:服务目录与责任模型 · 返回本章目录 · 下一节:SOP、Runbook 与变更治理 · 查看全书目录 · 查看索引中心