3. 聚合粒度定义:时间窗口与空间聚合对数据量的影响

在可观测性系统中,聚合粒度直接决定了存储成本、查询性能以及 SLI 的精度。聚合粒度分为两个维度:时间窗口(时间聚合)和 分组维度(空间聚合)。理解这两个维度如何影响数据量,是设计高效采集与存储策略的基础。

3.1 时间窗口:采样频率与数据压缩比

时间窗口定义了指标数据在时间轴上的聚合间隔。常见的窗口包括 1 秒、10 秒、1 分钟、5 分钟等。窗口越大,数据点越少,存储压力越小,但会丢失高频波动细节。

时间窗口 每秒数据点数(单指标) 每日数据点数(单指标) 典型场景
1s 1 86,400 实时监控、高频交易、容器 CPU 突发
10s 0.1 8,640 常规微服务延迟、错误率监控
1m 0.0167 1,440 基础设施健康、长期趋势分析
5m 0.0033 288 容量规划、月度报表、历史归档
关键权衡: 从 1s 窗口切换到 1m 窗口,数据量减少约 60 倍。但若 SLI 要求检测 5 秒内的错误突发,1m 聚合将完全掩盖该事件,导致误报或漏报。

3.2 空间聚合:分组维度与基数爆炸

空间聚合定义了指标按哪些标签(Label)进行分组。常见的分组维度包括 host、service、endpoint、status_code 等。每个维度组合都会产生一条独立的时间序列。

数据量计算公式:

总时间序列数 = 基数(维度1) × 基数(维度2) × … × 基数(维度N)
每日数据点数 = 总时间序列数 × 每日每序列点数
示例: 假设有 10 台主机、5 个服务、20 个端点、4 个状态码(2xx/4xx/5xx/其他),且全部维度组合均存在:
总时间序列数 = 10 × 5 × 20 × 4 = 4,000 条
每日数据点数(1s 窗口)= 4,000 × 86,400 ≈ 3.456 亿点
若仅按 service 聚合,则只有 5 条序列,每日数据点降至 432,000 点,减少 800 倍。

3.3 时间与空间聚合的联合影响

实际系统中,时间窗口和空间聚合同时作用。下图展示了不同聚合粒度组合下的数据量相对比例:

时间窗口 × 空间聚合 对数据量的联合影响 按 host 聚合(10 个) 按 service 聚合(5 个) 按 endpoint 聚合(20 个) 每日数据点(百万) 1s 10s 1m 5m 864 86.4 14.4 2.88 43.2 4.32 0.72 0.14 172.8 17.28 2.88 0.58 注:图中数值基于 10 台主机、5 个服务、20 个端点的示例计算,单位为百万数据点/天。 实际生产中,全维度组合(host × service × endpoint)可达 1,000 条序列,数据量再乘 10 倍。

3.4 聚合策略选择建议

根据 SLI 的精度要求和基础设施规模,推荐以下策略:

  • 高精度 SLI(如 P99 延迟、错误率突发检测): 使用 1s–10s 时间窗口,按 service + endpoint 分组,避免按 host 或 instance 展开。
  • 中等精度 SLI(如平均延迟、可用性): 使用 1m 时间窗口,按 service 分组,必要时增加 status_code 维度。
  • 长期趋势与容量规划: 使用 5m 或更粗窗口,仅保留 service 维度,或使用降采样存储。
避坑提示: 避免将高基数标签(如 user_id、request_id、session_id)直接作为指标标签。这会导致基数爆炸,使存储和查询成本失控。应使用直方图或日志记录替代。

3.5 OpenTelemetry 中的聚合配置

在 OpenTelemetry Collector 中,可以通过 metricstransform 或 filter 处理器控制聚合粒度:

# 示例:将 1s 粒度的指标聚合为 1m 输出
processors:
  metricstransform:
    transforms:
      - include: ^http_server_duration_ms$
        match_type: regexp
        action: aggregate
        aggregation: mean
        aggregation_interval: 60s
        new_name: http_server_duration_ms_1m

# 示例:按 service 分组,丢弃 host 标签
  filter:
    metrics:
      include:
        match_type: strict
        metric_names:
          - http_server_duration_ms
      exclude:
        match_type: strict
        resource_attributes:
          - key: host.name
            value: "*"