系统设计:监控与告警系统

从零设计一个监控与告警系统,覆盖指标采集的拉与推模式、时序存储与降采样、查询与聚合、告警规则与抑制静默、通知分发与值班、可观测性三支柱,包含容量估算、架构图、数据模型、选型对比与面试追问。

系统设计:监控与告警系统

监控系统要回答三个问题:系统现在健康吗、哪里出问题了、出问题该通知谁。它面对的挑战不是功能复杂,而是数据量巨大(每秒百万级指标点)与告警必须精准(漏报误报都是事故)。

1. 需求分析

功能性需求

  • 指标采集:主机、容器、应用、中间件的指标上报
  • 存储与查询:按时序存储、支持区间查询与聚合函数
  • 告警:规则配置、阈值/表达式、触发与恢复
  • 通知:电话、短信、IM、邮件多渠道
  • 值班与升级:轮班表、超时升级、认领与关闭

非功能性需求

  • 采集延迟:秒级,指标写入后 10 秒内可查
  • 查询延迟:仪表盘 P99 低于 1 秒
  • 可用性:监控系统自身 99.99%,不能先于被监控系统挂掉
  • 保留期:原始数据 15 天,降采样数据 1 年

核心难点

  1. 写入洪峰:百万级 series,每 15 秒一个点
  2. 基数爆炸:标签维度组合导致 series 数量失控
  3. 告警风暴:一次故障引发上百条告警,必须收敛

2. 容量估算

  • 采集对象:10 万台主机,每台 500 个指标 → 5000 万 series
  • 采集间隔:15 秒 → 每 series 每天 5760 个点
  • 日写入点:5000 万 × 5760 ≈ 2.88 万亿点/天
  • 每点约 2 字节(压缩后)→ 约 5.8 TB/天原始数据
  • 保留 15 天:约 87 TB
  • 降采样(1 分钟粒度,保留 1 年):约 43 亿点/天 × 365 ≈ 1.6 万亿点,约 3 TB

结论:压缩与降采样是存储可行性的前提。时序数据库的压缩率可达 10 倍以上,正是为此设计。

3. 整体架构

被监控对象(主机/容器/应用)
      │ 上报(推) 或 被拉取(拉)
      ▼
  采集层(Agent / Exporter / 网关)
      │
      ▼
  写入缓冲(Kafka,削峰填谷)
      │
      ▼
  时序数据库(分片 + 副本)
      │
   ┌──┴───────────────┐
   ▼                  ▼
查询服务(仪表盘)   告警引擎(规则求值)
   │                  │
   ▼                  ▼
可视化前端         通知分发(抑制/静默/聚合)
                      │
                      ▼
                  值班系统(电话/短信/IM)

数据流

采集层把指标写入缓冲,时序库消费落盘;告警引擎周期性拉取规则涉及的时间序列做求值,命中则生成告警事件走通知链路。

4. 数据模型

时序数据模型

指标 = 名字 + 标签集合 + 时间戳 + 数值。标签是维度,也是查询与聚合的依据。

metric: http_requests_total
labels: {job="api", instance="10.0.0.1:8080", method="GET", status="200"}
value:  12345
ts:     1696000000

存储布局

series_index (
    series_id   BIGINT PRIMARY KEY,
    metric      VARCHAR(128),
    label_hash  CHAR(64),        -- 标签集合的哈希,用于去重
    labels      JSON
)

chunks (
    series_id   BIGINT,
    start_ts    BIGINT,
    end_ts      BIGINT,
    data        BLOB             -- 压缩后的时间戳与数值
)

设计要点

  • 标签集合相同则复用同一 series_id,避免重复存储标签
  • 数据按时间分块(如 2 小时一块),便于压缩与过期删除
  • 倒排索引(标签 → series_id)支撑多维查询

5. 指标采集:拉与推

拉模式 Pull

服务端周期性地从目标的 HTTP 端点抓取指标(Prometheus 风格)。

优点:目标健康状态一目了然(抓不到即异常)、配置集中、易做服务发现。
缺点:目标必须可被访问(跨网段/防火墙困难)、短生命周期任务难覆盖、需要服务发现配合。

推模式 Push

目标主动把指标推到网关或队列。

优点:穿透网络限制、适合短任务与边缘设备、天然支持离线缓存重传。
缺点:需要额外的健康检测(推不上不代表挂了)、推送方需处理重试。

对比

维度拉模式推模式
目标可达性需服务端可达目标目标可达服务端即可
健康检测抓取失败即异常需额外机制
短任务不友好友好
配置管理集中分散
典型代表PrometheusStatsD、OpenTelemetry

混合实践

主流方案是「推拉结合」:长驻服务用拉,短任务与边缘用推,统一汇入同一时序库。这样既保留健康检测能力,又覆盖全部场景。

6. 时序存储、降采样与查询

存储引擎要点

  • 列式存储:同一指标的值连续存放,压缩率高
  • 时间戳差分编码:等间隔采集的时间戳差分后几乎为常量,压缩极佳
  • Gorilla 类压缩:XOR 相邻浮点值,大幅降低数值存储
  • 按时间分片:过期数据整块删除,无需逐条清理

降采样

原始精度保留 15 天,之后按 1 分钟、5 分钟、1 小时逐级降采样,保留更长时间。

层级粒度保留期用途
原始15 秒15 天排障、精确回溯
一级1 分钟90 天趋势分析
二级5 分钟1 年容量规划
三级1 小时3 年长期报表

降采样时通常同时保留 max、min、avg、sum,避免聚合后丢失峰值信息(如 P99 延迟)。

基数控制

标签组合爆炸是时序库的头号杀手。做法:

  • 禁止把用户 ID、请求 ID 等高基数字段作为标签
  • 对标签数量设上限并做监控
  • 高基数场景改用日志或追踪而非指标

查询语言

类 PromQL 的查询支持:按标签选择、区间聚合(rate、sum、avg、histogram_quantile)、跨序列运算。

# 示例:最近 5 分钟各实例的请求速率
rate(http_requests_total[5m])
# 示例:按状态码聚合的 P99 延迟
histogram_quantile(0.99, sum(rate(http_latency_bucket[5m])) by (le))

查询优化

  • 时间范围裁剪:只读相关时间块
  • 倒排索引预筛:先定位 series 再做聚合
  • 查询结果缓存:仪表盘常用查询缓存数十秒
  • 预聚合:热门面板预先算好,避免实时扫全量

慢查询治理

限制查询时间范围与返回点数,超限拒绝;对大范围查询强制走降采样数据。

7. 告警规则与抑制静默

规则求值

告警引擎按固定周期(如每 30 秒)对规则表达式求值,满足条件持续 N 次后触发,恢复时发恢复通知。

alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 0.05
for: 2m
labels:
  severity: critical
annotations:
  summary: 5xx 错误率超过 5%

触发与恢复

  • for 持续时间避免抖动误报(瞬时毛刺不告警)
  • 恢复通知同样重要,避免值班人员不确定是否已恢复

抑制 Inhibition

当高层故障发生时,抑制其引发的下游告警。例如「机房网络故障」抑制该机房所有主机的「实例不可达」告警,避免告警风暴。

静默 Silence

计划内维护时临时屏蔽匹配的告警,按标签匹配、带到期时间,自动失效。

告警收敛

手段作用
分组同规则同类告警合并成一条
抑制高优故障屏蔽衍生告警
静默维护期临时屏蔽
去重相同告警只发一次
降噪基于历史判定是否为已知噪声

8. 通知分发与值班

分发链路

告警事件 → 路由(按严重级别与团队)→ 聚合(合并同类)→ 通道选择 → 送达 → 回执与升级。

值班与升级

  • 轮班表定义谁在什么时段负责哪个服务
  • 未在时限内认领则升级到上一级或下一班
  • 电话通道用于最高优先级,短信/IM 用于中低优先级

通知渠道对比

渠道时效打扰度适用级别
电话秒级极高P0
短信秒级高P1
IM 群秒级中P2
邮件分钟级低P3/日报

防打扰

非工作时间的低优告警只进 IM 不打电话;同一告警合并后只发一次;提供一键认领与静默入口。多渠道触达的模板化与频控可参考 通知系统设计。

削峰与缓冲

采集洪峰用消息队列削峰填谷,具体可参考 消息队列设计。

9. 可观测性三支柱

指标、日志、追踪

  • 指标(Metrics):低成本、高聚合,回答「系统整体是否正常」
  • 日志(Logs):高细节、高成本,回答「具体发生了什么错误」
  • 追踪(Traces):请求级全链路,回答「慢在哪一跳」

三者用统一的 trace_id 与标签体系串联,从指标发现异常 → 追踪定位链路 → 日志确认根因。

关联设计

  • 指标与追踪共享服务名、实例等标签,可互相跳转
  • 日志中嵌入 trace_id,追踪中可下钻到对应日志
  • 告警触发时自动附带相关追踪与日志入口

采集成本权衡

指标全量采集,日志采样或分级(错误全留、调试采样),追踪按比例采样(如 1%)并对慢请求加权采样。

10. 面试常见问题

Q: 拉模式和推模式到底选哪个?
长驻服务用拉(可做健康检测、配置集中),短任务与跨网段用推,生产系统几乎都是推拉结合。

Q: 时序数据量太大怎么存?
三重手段:时序专用压缩(差分 + XOR)、按时间分块存储、分级降采样。核心是让「越老的数据粒度越粗」。

Q: 告警风暴怎么解决?
抑制 + 分组 + 静默 + 去重。一次机房级故障应只产生一条根因告警,衍生告警被抑制。

Q: 怎么减少误报?
设置 for 持续时间过滤毛刺、用多条件组合而非单阈值、基于历史基线做动态阈值、上线前用回放数据验证规则。

Q: 监控系统自己挂了怎么办?
多副本 + 独立部署 + 与被监控系统隔离(不同机房/账号),并提供轻量的黑盒拨测做兜底。

Q: 高基数标签为什么致命?
每个标签组合是一个独立 series,基数爆炸会让索引与内存线性膨胀,最终拖垮整个存储。应限制标签维度,高基数需求改用日志或追踪。

Q: 三支柱必须都上吗?
不必一步到位。中小团队先做指标 + 告警(性价比最高),再补日志,最后按需上追踪。关键是用统一标签体系为后续关联预留空间。

总结

监控与告警系统的答题主线是采集—存储—告警—通知四段:采集用推拉结合覆盖全场景,存储靠压缩与降采样控制成本,告警靠 for 抑制与静默收敛风暴,通知靠值班与升级保证有人响应。最后用「指标、日志、追踪」三支柱收口,说明它们如何用统一标签串联。能把「告警风暴」和「高基数」这两个坑讲透,就是这道题的高分点。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页