第 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 仪表盘设计原则
一个好的度量仪表盘应该:
一目了然: 关键指标一眼可见
层次清晰: 从总览到详情逐层钻取
实时更新: 数据自动刷新
可交互: 支持时间范围选择、维度筛选
告警集成: 异常指标高亮显示
┌─────────────────────────────────────────────────────┐
│ 系统总览 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 正在成为可观测性的统一标准