第 5 章 度量数据的聚合与展示

本章讲述如何聚合和存储度量数据,如何进行清洗和处理, 然后重点讲解度量的可视化技术和常用的技术栈。

5.1 度量数据的聚合和存储

5.1.1 聚合方式

度量数据的聚合方式:

  • 时间聚合: 按时间窗口聚合 (1 分钟、5 分钟、1 小时)

  • 空间聚合: 按维度聚合 (按服务、按实例、按区域)

  • 层次聚合: 从底层到高层逐步汇总

原始数据 (每秒)
    │
    ▼ 时间聚合
1 分钟粒度
    │
    ▼ 时间聚合
5 分钟粒度
    │
    ▼ 降采样
1 小时粒度
    │
    ▼ 降采样
1 天粒度

5.1.2 时序数据库

度量数据本质上是 时间序列数据 (Time Series Data), 推荐使用专门的时序数据库:

时序数据库对比

数据库

语言

特点

适用场景

InfluxDB

Go

类 SQL 查询、易用

中小规模度量

Prometheus

Go

Pull 模型、告警集成

Kubernetes 生态

TimescaleDB

C (PostgreSQL 扩展)

SQL 兼容、成熟

需要关系查询的场景

VictoriaMetrics

Go

高压缩、高性能

大规模度量存储

ClickHouse

C++

列式存储、极高吞吐

分析型查询

5.1.3 数据保留策略

保留策略示例:

高精度数据 (1s 粒度)  → 保留 7 天
中精度数据 (1min 粒度) → 保留 30 天
低精度数据 (1h 粒度)  → 保留 1 年
汇总数据 (1d 粒度)    → 永久保留

5.2 度量数据的清洗和处理

5.2.1 数据清洗

原始度量数据可能包含噪声和异常,需要清洗:

  • 去重: 去除重复的度量数据点

  • 补值: 填充缺失的数据点 (线性插值、前值填充)

  • 去噪: 使用移动平均或中位数滤波去除噪声

  • 异常检测: 识别并标记异常数据点

5.2.2 数据处理

常用的数据处理方法:

  • 移动平均 (Moving Average): 平滑数据波动

  • 指数加权移动平均 (EWMA): 近期数据权重更高

  • 差分 (Derivative): 计算变化率

  • 积分 (Integral): 计算累积量

  • 百分位计算: 计算 P50, P90, P95, P99

Python (Pandas)

import pandas as pd

# 移动平均
df['latency_ma'] = df['latency'].rolling(window=5).mean()

# 指数加权移动平均
df['latency_ewma'] = df['latency'].ewm(span=5).mean()

# 百分位计算
p50 = df['latency'].quantile(0.50)
p90 = df['latency'].quantile(0.90)
p99 = df['latency'].quantile(0.99)

Go (实时流处理)

// Go 中使用滑动窗口计算 P99
type LatencyTracker struct {
    mu      sync.Mutex
    window  []float64
    maxSize int
}

func (t *LatencyTracker) Record(latency float64) {
    t.mu.Lock()
    defer t.mu.Unlock()
    t.window = append(t.window, latency)
    if len(t.window) > t.maxSize {
        t.window = t.window[1:]
    }
}

func (t *LatencyTracker) Percentile(p float64) float64 {
    t.mu.Lock()
    sorted := make([]float64, len(t.window))
    copy(sorted, t.window)
    t.mu.Unlock()
    sort.Float64s(sorted)
    idx := int(float64(len(sorted)-1) * p)
    return sorted[idx]
}

PromQL 查询示例

# 计算 P99 延迟
histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))

# 计算错误率
sum(rate(http_requests_total{status=~"5.."}[5m]))
/ sum(rate(http_requests_total[5m]))

# 计算 QPS
sum(rate(http_requests_total[1m]))

5.3 度量数据的可视化

5.3.1 常用图表类型

度量可视化图表

图表类型

适用场景

示例

折线图 (Line Chart)

趋势变化

CPU 使用率随时间的变化

柱状图 (Bar Chart)

分类比较

各服务的错误数比较

面积图 (Area Chart)

累积量变化

各状态码请求的堆叠分布

饼图 (Pie Chart)

占比分布

错误类型分布

热力图 (Heatmap)

二维分布

请求延迟的时间-频率分布

仪表盘 (Gauge)

单一指标

当前 CPU 使用率

表格 (Table)

精确数值

Top N 慢查询

5.3.2 仪表盘设计原则

一个好的度量仪表盘应该:

  1. 一目了然: 关键指标一眼可见

  2. 层次清晰: 从总览到详情逐层钻取

  3. 实时更新: 数据自动刷新

  4. 可交互: 支持时间范围选择、维度筛选

  5. 告警集成: 异常指标高亮显示

┌─────────────────────────────────────────────────────┐
│                 系统总览 Dashboard                    │
├─────────────┬─────────────┬─────────────────────────┤
│  可用性      │  错误率      │  平均响应时间            │
│  99.95%     │   0.3%      │  125ms                  │
│  ●(绿)      │  ●(绿)      │  ●(绿)                  │
├─────────────┴─────────────┴─────────────────────────┤
│                                                      │
│  ┌──────────────────────────────────────────────┐   │
│  │  请求量趋势 (折线图)                           │   │
│  │  ~~~~~~~~~~~~~~~~~~~~~~~~                     │   │
│  └──────────────────────────────────────────────┘   │
│                                                      │
│  ┌──────────────────┐  ┌────────────────────────┐   │
│  │ 延迟分布 (热力图) │  │ 错误类型分布 (饼图)     │   │
│  │                   │  │                         │   │
│  └──────────────────┘  └────────────────────────┘   │
└─────────────────────────────────────────────────────┘

5.4 常用度量聚合与展示方案

5.4.1 TIG 技术栈

Telegraf + InfluxDB + Grafana

┌──────────┐    ┌───────────┐    ┌──────────┐
│ Telegraf  │───>│ InfluxDB  │───>│ Grafana  │
│ (采集)    │    │ (存储)     │    │ (展示)   │
└──────────┘    └───────────┘    └──────────┘

Telegraf 配置示例:

# telegraf.conf
[agent]
  interval = "10s"
  round_interval = true

# 输入: 系统指标
[[inputs.cpu]]
  percpu = true
  totalcpu = true

[[inputs.mem]]

[[inputs.disk]]
  ignore_fs = ["tmpfs", "devtmpfs"]

# 输入: Docker 容器指标
[[inputs.docker]]
  endpoint = "unix:///var/run/docker.sock"

# 输出: InfluxDB
[[outputs.influxdb]]
  urls = ["http://influxdb:8086"]
  database = "telegraf"

5.4.2 ELKK 技术栈

Elasticsearch + Logstash + Kibana + Kafka

┌──────────┐    ┌──────────┐    ┌───────────────┐    ┌──────────┐
│ Filebeat  │───>│  Kafka   │───>│  Logstash     │───>│  Elastic │
│ (采集)    │    │ (缓冲)   │    │ (处理/转换)    │    │  search  │
└──────────┘    └──────────┘    └───────────────┘    └────┬─────┘
                                                           │
                                                           ▼
                                                     ┌──────────┐
                                                     │  Kibana  │
                                                     │  (展示)   │
                                                     └──────────┘

Logstash 配置示例:

# logstash.conf
input {
  kafka {
    bootstrap_servers => "kafka:9092"
    topics => ["app-logs"]
    codec => json
  }
}

filter {
  if [type] == "access_log" {
    grok {
      match => { "message" => "%{COMBINEDAPACHELOG}" }
    }
    date {
      match => [ "timestamp", "dd/MMM/yyyy:HH:mm:ss Z" ]
    }
    mutate {
      convert => { "response" => "integer" }
      convert => { "bytes" => "integer" }
    }
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "app-logs-%{+YYYY.MM.dd}"
  }
}

5.4.3 Prometheus 技术栈

Prometheus + Grafana + Alertmanager

┌──────────────────────────────┐
│        微服务集群             │
│  ┌─────┐ ┌─────┐ ┌─────┐   │
│  │svc-1│ │svc-2│ │svc-3│   │
│  │/met │ │/met │ │/met │   │
│  └──┬──┘ └──┬──┘ └──┬──┘   │
└─────┼───────┼───────┼──────┘
      │       │       │  Pull
      ▼       ▼       ▼
┌──────────────────────────┐
│      Prometheus          │
│   (抓取、存储、查询)       │
└──────┬──────────┬────────┘
       │          │
       ▼          ▼
┌──────────┐  ┌──────────────┐
│ Grafana  │  │ Alertmanager │
│ (展示)    │  │ (告警)        │
└──────────┘  └──────────────┘

Prometheus 配置示例:

# prometheus.yml
global:
  scrape_interval: 15s
  evaluation_interval: 15s

rule_files:
  - "rules/*.yml"

alerting:
  alertmanagers:
    - static_configs:
        - targets: ['alertmanager:9093']

scrape_configs:
  - job_name: 'potato-server'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['potato-server:9003']

  - job_name: 'potato-scheduler'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['potato-scheduler:9002']

5.4.4 OpenTelemetry (新增)

OpenTelemetry 是 CNCF 的统一可观测性框架, 将 Metrics、Traces、Logs 三大信号统一收集:

┌──────────────────────────────────────┐
│           应用程序                     │
│  ┌──────────────────────────────┐    │
│  │   OpenTelemetry SDK          │    │
│  │  ┌────────┬───────┬────────┐ │    │
│  │  │Metrics │Traces │ Logs   │ │    │
│  │  └────────┴───────┴────────┘ │    │
│  └──────────────┬───────────────┘    │
└─────────────────┼────────────────────┘
                  │ OTLP
                  ▼
┌──────────────────────────────────────┐
│      OTel Collector                   │
│  ┌─────────┬──────────┬───────────┐  │
│  │Receivers│Processors│ Exporters │  │
│  └─────────┴──────────┴───────────┘  │
└──────┬──────────┬──────────┬─────────┘
       │          │          │
       ▼          ▼          ▼
┌──────────┐ ┌────────┐ ┌────────┐
│Prometheus│ │ Jaeger │ │  Loki  │
│(Metrics) │ │(Traces)│ │ (Logs) │
└──────────┘ └────────┘ └────────┘

5.5 土豆微服务的度量聚合与展示

在土豆微服务中,我们使用以下方案:

  • 系统指标: Telegraf 采集 → InfluxDB 存储 → Grafana 展示

  • 应用日志: Filebeat 采集 → Kafka 缓冲 → Logstash 处理 → Elasticsearch 存储 → Kibana 展示

  • 业务度量: Micrometer → InfluxDB → Grafana

# docker-compose.yml (度量相关组件)
services:
  influxdb:
    image: influxdb:2.7
    ports:
      - "8086:8086"
    volumes:
      - influxdb-data:/var/lib/influxdb2

  grafana:
    image: grafana/grafana:10.0
    ports:
      - "3000:3000"
    depends_on:
      - influxdb

  telegraf:
    image: telegraf:1.27
    volumes:
      - ./telegraf.conf:/etc/telegraf/telegraf.conf
    depends_on:
      - influxdb

5.6 本章小结

本章介绍了度量数据从采集到展示的完整流程:

  • 度量数据的聚合方式和时序数据库选型

  • 数据清洗和处理的常用方法

  • 度量可视化的图表类型和仪表盘设计原则

  • 四种主流的度量技术栈: TIG、ELKK、Prometheus、OpenTelemetry

注解

关键要点:

  • 时序数据库是度量数据存储的首选

  • 使用百分位数而非均值来展示延迟

  • 仪表盘应该层次清晰、一目了然

  • OpenTelemetry 正在成为可观测性的统一标准