2. 误差预算台账设计
误差预算台账是告警降噪与阈值联动的“会计账簿”。没有台账,SRE 团队就无法回答“我们还有多少故障余量”“当前消耗速度是否危险”等核心问题。本章从数据结构与存储选型两个维度,构建可落地的误差预算台账体系。
2.1 台账数据结构设计
误差预算台账的核心是时间窗口、预算总量、已消耗量、剩余量四要素。设计时需区分滚动窗口与固定窗口两种模式,并定义消耗计算规则。
2.1.1 时间窗口模型
| 窗口类型 | 定义 | 适用场景 | 示例 |
|---|---|---|---|
| 滚动窗口 | 以当前时刻为终点,向前滑动固定时长 | 实时监控、动态告警 | 过去 28 天的滑动窗口 |
| 固定窗口 | 按自然日历周期(周/月/季度)划分 | 财务核算、SLO 报告 | 2025 年 3 月自然月 |
| 混合窗口 | 滚动窗口 + 固定窗口联合计算 | 既有实时告警又有月度报告 | 滚动 28 天 + 当月累计 |
实践建议:生产环境优先使用滚动窗口作为告警依据,固定窗口用于管理层汇报。两者可并存,但告警阈值必须基于滚动窗口,避免月末“突击消耗”导致漏报。
2.1.2 台账字段定义
每条误差预算记录应包含以下核心字段:
{
"service_name": "payment-api", // 服务标识
"slo_name": "latency_p99_500ms", // SLO 名称
"window_type": "rolling", // 窗口类型
"window_seconds": 2419200, // 28 天 = 2419200 秒
"budget_total": 0.999, // 预算总量(99.9%)
"good_events": 8640000, // 好事件数
"total_events": 8648640, // 总事件数
"budget_consumed": 0.001, // 已消耗量
"budget_remaining": 0.998, // 剩余量
"consumption_rate": 0.0000357, // 消耗速率(/小时)
"burn_rate": 0.5, // 燃烧速率
"recorded_at": "2025-03-15T10:00:00Z" // 记录时间戳
}
2.1.3 消耗计算规则
- 预算总量 = 1 - SLO 目标值(例如 SLO 99.9% → 预算总量 0.001)
- 已消耗量 = 1 - (好事件数 / 总事件数)
- 剩余量 = 预算总量 - 已消耗量
- 燃烧速率 = 已消耗量 / (窗口时长 × 预算总量)
关键公式:当剩余量 ≤ 0 时,SLO 已被违反;当燃烧速率 > 1 时,表示消耗速度超过预算分配速度,需要触发告警。
2.2 台账存储选型
误差预算台账需要同时支持高写入吞吐(每秒数千条记录)、低延迟查询(告警判定 < 1s)、历史回溯(90 天以上)。三种主流方案对比如下:
| 维度 | Prometheus | Thanos | ClickHouse |
|---|---|---|---|
| 存储模型 | 时序数据库(TSDB) | Prometheus + 对象存储 | 列式存储 |
| 写入性能 | 单机 100 万样本/秒 | 水平扩展,无上限 | 单机 50 万行/秒 |
| 查询延迟 | 毫秒级(近期数据) | 毫秒级(近期),秒级(历史) | 毫秒级(全量数据) |
| 历史数据保留 | 本地有限(通常 15-30 天) | 无限(对象存储) | 无限(按分区管理) |
| 告警集成 | 原生 Alertmanager | 兼容 Alertmanager | 需额外开发 |
| 运维复杂度 | 低 | 中 | 高 |
| 成本 | 中(SSD 存储) | 低(对象存储) | 中(按需计算资源) |
选型避坑:不要只用 Prometheus 存储误差预算台账。Prometheus 的本地存储无法长期保留历史预算数据,且单机容量有限。生产环境至少使用 Thanos 或 ClickHouse 作为长期存储。
2.3 推荐架构:Thanos + ClickHouse 分层存储
结合两种存储的优势,推荐采用分层台账架构:
2.4 台账表结构设计(ClickHouse 示例)
若选择 ClickHouse 作为历史存储,推荐使用物化视图 + 聚合表结构:
-- 原始事件表(按天分区)
CREATE TABLE error_budget_events (
service_name String,
slo_name String,
window_type String,
window_seconds UInt32,
good_events UInt64,
total_events UInt64,
recorded_at DateTime
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(recorded_at)
ORDER BY (service_name, slo_name, recorded_at);
-- 聚合台账表(每小时聚合)
CREATE MATERIALIZED VIEW error_budget_hourly
ENGINE = SummingMergeTree()
PARTITION BY toYYYYMM(hour)
ORDER BY (service_name, slo_name, hour)
AS SELECT
service_name,
slo_name,
toStartOfHour(recorded_at) AS hour,
sum(good_events) AS total_good,
sum(total_events) AS total_events
FROM error_budget_events
GROUP BY service_name, slo_name, hour;
查询优化:使用 SummingMergeTree 引擎自动合并相同主键的行,查询时直接读取聚合结果,避免全表扫描。对于 90 天数据,单次预算查询可在 50ms 内完成。
2.5 台账数据生命周期管理
| 数据层级 | 保留时长 | 存储介质 | 查询用途 |
|---|---|---|---|
| 热数据 | 最近 7 天 | Prometheus / Thanos Receive | 实时告警、燃烧速率计算 |
| 温数据 | 7 ~ 30 天 | Thanos Store (对象存储) | 周报、趋势分析 |
| 冷数据 | 30 ~ 90 天 | ClickHouse | 月度 SLO 报告、审计 |
| 归档数据 | 90 天以上 | 对象存储(压缩后) | 合规审计、年度复盘 |
台账设计核心原则:写入路径要简单(避免复杂 JOIN),查询路径要快速(预聚合 + 索引),存储成本要可控(分层 + 压缩)。误差预算台账不是 OLTP 系统,不要追求事务一致性,接受最终一致性。