mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
2643 字
7 分钟
可观测性规模:5 亿指标/秒
2025-10-31

当你的集群从 100 个服务增长到 5000 个,活跃时间序列从 100 万增长到 1 亿,可观测性系统本身就成了最大的挑战。Prometheus 单机无法承载、存储成本线性增长、查询延迟从秒级变成分钟级、基数爆炸导致 OOM——你需要为规模而设计。

一、规模挑战全景#

1.1 规模增长曲线#

graph TB subgraph 规模级别 S["小型<br/>100 服务<br/>100 万序列<br/>Prometheus 单机"] M["中型<br/>500 服务<br/>1000 万序列<br/>VictoriaMetrics 集群"] L["大型<br/>2000 服务<br/>1 亿序列<br/>Mimir"] XL["超大型<br/>5000+ 服务<br/>10 亿序列<br/>Mimir + 自研"] end S --> M --> L --> XL style S fill:#c8e6c9,stroke:#2e7d32 style M fill:#fff3e0,stroke:#e65100 style L fill:#ffcdd2,stroke:#c62828 style XL fill:#e1bee7,stroke:#6a1b9a

1.2 各规模的关键挑战#

规模活跃序列关键挑战解决方案
小型< 100 万基础监控覆盖Prometheus 单机
中型100 万-1000 万基数控制、存储成本VictoriaMetrics 集群
大型1000 万-1 亿查询性能、降采样Mimir + 降采样
超大型> 1 亿全局视图、多集群Mimir 多集群 + 自研

二、5 亿指标/秒的架构#

2.1 采集层#

graph TB subgraph 应用[" 5000+ 应用"] APP["OTel SDK"] end subgraph 采集[" 采集层"] AGENT["OTel Collector Agent<br/>DaemonSet × 100 节点"] GW["OTel Collector Gateway<br/>Deployment × 10"] end subgraph 存储[" 存储层"] MIMIR["Mimir 集群<br/>distributor × 10<br/>ingester × 30<br/>querier × 20<br/>store-gateway × 15"] end APP -->|"OTLP"| AGENT --> GW --> MIMIR style 应用 fill:#e8eaf6,stroke:#283593 style 采集 fill:#e0f2f1,stroke:#00695c style 存储 fill:#fff3e0,stroke:#e65100

2.2 关键参数#

组件实例数单实例吞吐总吞吐内存
OTel Agent1005M samples/s500M samples/s512Mi
OTel Gateway1050M samples/s500M samples/s2Gi
Mimir Distributor1050M samples/s500M samples/s1Gi
Mimir Ingester3017M samples/s500M samples/s16Gi
Mimir Querier20--8Gi

2.3 大规模架构的详细设计#

5 亿指标/秒的架构不只是”多加几台机器”,它需要解决一系列分布式系统问题:

写入路径

graph LR SDK["OTel SDK"] -->|"OTLP"| AGENT["Agent<br/>本地缓冲"] AGENT -->|"批量发送"| GW["Gateway<br/>路由+限流"] GW -->|"hash(service)"| DIST["Distributor<br/>一致性哈希"] DIST -->|"写入"| ING["Ingester<br/>内存写入+WAL"] ING -->|"刷盘"| S3["对象存储<br/>长期存储"] style SDK fill:#e8eaf6,stroke:#283593 style AGENT fill:#e0f2f1,stroke:#00695c style GW fill:#e0f2f1,stroke:#00695c style DIST fill:#fff3e0,stroke:#e65100 style ING fill:#fff3e0,stroke:#e65100 style S3 fill:#fce4ec,stroke:#880e4f
写入阶段关键设计失败处理
SDK → Agent本地缓冲 + 重试Agent 不可用时降级到直接发送
Agent → Gateway批量发送 + 压缩Gateway 不可用时本地排队
Gateway → Distributor限流 + 路由超限返回 429,Agent 重试
Distributor → Ingester一致性哈希 + 3 副本1 副本失败不影响写入
Ingester → 对象存储定期刷盘 + 压缩刷盘失败重试

查询路径

graph LR USER["用户查询"] --> QF["Query-Frontend<br/>查询排队+拆分"] QF --> QR["Querier<br/>并行查询"] QR --> ING["Ingester<br/>最近数据"] QR --> SG["Store-Gateway<br/>历史数据"] SG --> S3["对象存储"] style USER fill:#e8eaf6,stroke:#283593 style QF fill:#e0f2f1,stroke:#00695c style QR fill:#e0f2f1,stroke:#00695c style ING fill:#fff3e0,stroke:#e65100 style SG fill:#fff3e0,stroke:#e65100 style S3 fill:#fce4ec,stroke:#880e4f
查询阶段关键设计优化手段
Query-Frontend查询排队 + 超时控制拆分大查询为子查询
Querier并行查询 + 结果合并缓存查询结果
Ingester 查询内存查询最近数据无需读磁盘
Store-Gateway对象存储索引 + 缓存预取 + 布隆过滤器

三、VictoriaMetrics vs Mimir 集群对比#

3.1 核心对比#

维度VictoriaMetrics 集群Grafana Mimir
架构vmselect/vminsert/vmstoragedistributor/ingester/querier/store-gateway
多租户账号 ID 隔离Org ID 隔离 + 细粒度配额
存储后端本地磁盘(推荐)对象存储(S3/GCS)
压缩比~10x(原生)~5x(对象存储)
查询性能极快(本地磁盘)快(对象存储 + 缓存)
运维复杂度低(3 个组件)高(10+ 个微服务)
社区活跃度高(Grafana Labs 支持)
企业功能企业版(付费)开源 + Grafana Cloud

3.2 选型决策#

graph TB START{活跃序列数?} --> |"< 5000 万"| VM["VictoriaMetrics 集群"] START --> |"> 5000 万"| Q1{需要多租户?} Q1 -->|"是"| MIMIR["Grafana Mimir"] Q1 -->|"否"| Q2{存储成本敏感?} Q2 -->|"是"| VM Q2 -->|"否"| MIMIR style START fill:#e3f2fd,stroke:#1565c0 style VM fill:#c8e6c9,stroke:#2e7d32 style MIMIR fill:#fff3e0,stroke:#e65100
场景推荐理由
< 5000 万序列,单团队VictoriaMetrics运维简单,性能好
> 5000 万序列,多团队Mimir多租户 + 细粒度配额
已在 Grafana CloudMimir生态一致
存储成本优先VictoriaMetrics压缩比更高
合规要求(数据隔离)Mimir物理隔离支持

3.3 VictoriaMetrics 集群配置#

# VictoriaMetrics 集群部署
# vminsert — 写入入口
apiVersion: apps/v1
kind: Deployment
metadata:
name: vminsert
spec:
replicas: 5
template:
spec:
containers:
- name: vminsert
image: victoriametrics/vminsert:v1.93.0
args:
- --storageNode=vmstorage-0:8400,vmstorage-1:8400,vmstorage-2:8400
- --maxLabelsPerTimeseries=30
- --maxLabelValueLen=2048
- --rpc.tls=true
resources:
requests:
memory: "2Gi"
cpu: "2"
limits:
memory: "4Gi"
cpu: "4"
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: vmstorage
spec:
replicas: 3
template:
spec:
containers:
- name: vmstorage
image: victoriametrics/vmstorage:v1.93.0
args:
- --storageDataPath=/vm-data
- --retentionPeriod=6m
- --downsampling.period=5m:30d,1h:180d
resources:
requests:
memory: "32Gi"
cpu: "8"
volumeMounts:
- name: data
mountPath: /vm-data

3.4 Mimir 集群配置#

# Mimir 分布式部署(Helm Values)
mimir:
structured_config:
distributor:
ring:
heartbeat_period: 5s
rate_limiting_strategy: "global"
ingester:
ring:
replication_factor: 3
chunks_encoding: "snappy"
store_gateway:
sharding_ring:
replication_factor: 3
querier:
max_concurrent: 20
timeout: 2m
limits:
max_global_series_per_user: 5000000
max_query_length: 720h
max_query_parallelism: 16
# 资源配置
distributor:
replicas: 10
resources:
requests:
memory: 1Gi
cpu: 2
ingester:
replicas: 30
resources:
requests:
memory: 16Gi
cpu: 8
querier:
replicas: 20
resources:
requests:
memory: 8Gi
cpu: 4
store_gateway:
replicas: 15
resources:
requests:
memory: 8Gi
cpu: 4

四、高基数治理#

4.1 高基数的来源#

来源基数治理策略
user_id 标签百万级移到日志/追踪
pod_name 标签千级(每次部署变化)deployment 替代
ip_address 标签百万级聚合为地区
request_path 标签万级(含 ID)模板化路由
error_message 标签千级error_code 替代

4.2 基数监控#

# 监控每个指标的序列数
count by (__name__) ({__name__=~".+"})
# 找出高基数指标
topk(10, count by (__name__) ({__name__=~".+"}))
# 监控标签基数
count by (service) (http_server_request_duration_bucket)

4.3 基数限制#

# Mimir 基数限制
limits:
max_global_series_per_user: 5000000 # 每用户最大序列数
max_global_metadata_per_user: 500000 # 每用户最大元数据数
max_label_names_per_series: 30 # 每序列最大标签数
max_label_value_length: 2048 # 标签值最大长度
creation_rate_limit: 50000 # 每秒新序列创建速率

4.4 高基数治理的实战策略#

策略 1:在 OTel Collector 中过滤高基数标签

这是最有效的治理手段——在数据进入存储之前就过滤掉高基数标签:

# OTel Collector: 过滤高基数标签
processors:
transform/remove_high_cardinality:
error_mode: ignore
metric_statements:
- context: metric
statements:
# 移除 user_id 标签
- delete_key(attributes, "user_id")
# 移除 request_path 中的动态 ID
- set(attributes["request_path"], Replace(attributes["request_path"], "\\d+", ":id")) where attributes["request_path"] != nil
# 将 IP 地址聚合为地区
- set(attributes["region"], "unknown") where attributes["client_ip"] != nil
- delete_key(attributes, "client_ip")
filter/drop_debug_metrics:
error_mode: ignore
traces:
span:
# 丢弃 debug 级别的追踪
- 'attributes["log.level"] == "debug"'

策略 2:用 Recording Rules 预聚合高基数查询

如果你需要按 user_id 维度查询,不要直接查询原始指标,而是用 Recording Rules 预聚合:

# 预聚合:按服务统计请求率(不保留 user_id 维度)
groups:
- name: preaggregation
interval: 30s
rules:
- record: http:requests:rate5m
expr: sum(rate(http_requests_total[5m])) by (service, method, status)
# 原始指标有 user_id 标签,但预聚合后不再保留

策略 3:基数预算

像错误预算一样,给每个团队设定基数预算:

团队序列预算当前使用使用率
支付团队500,000320,00064%
订单团队1,000,000850,00085%
搜索团队2,000,0001,200,00060%
推荐团队200,000180,00090%
Warning

高基数指标是可观测性成本的头号杀手。一个包含 user_id 标签的指标,在 100 万用户下会产生 100 万条时间序列,每条序列 3-5 KB 内存,总计 3-5 GB——仅一个指标。务必在采集层(OTel Collector)就过滤掉高基数标签。

五、降采样策略#

5.1 降采样原理#

降采样将高分辨率数据聚合为低分辨率数据,降低长期存储成本:

graph LR RAW["原始数据<br/>15s 分辨率<br/>30 天保留"] --> DS5M["降采样 5m<br/>30-90 天保留"] DS5M --> DS1H["降采样 1h<br/>90-365 天保留"] DS1H --> DS1D["降采样 1d<br/>365+ 天保留"] style RAW fill:#ffcdd2,stroke:#c62828 style DS5M fill:#fff3e0,stroke:#e65100 style DS1H fill:#e8f5e9,stroke:#2e7d32 style DS1D fill:#e3f2fd,stroke:#1565c0

5.2 降采样配置#

# VictoriaMetrics 降采样
downsampling:
- resolution: 5m
retention: 90d
filter:
exclude: ['go_*', 'process_*'] # 排除不需要降采样的指标
- resolution: 1h
retention: 365d
- resolution: 1d
retention: 0 # 永久保留

5.3 降采样的实现细节#

降采样不是简单的”取平均值”——不同类型的指标需要不同的聚合方式:

指标类型降采样聚合方式原因
Counter求和(Sum)Counter 是递增的,求和保留总量
Gauge平均值(Avg)+ 最小值 + 最大值Gauge 可增可减,平均值保留趋势
Histogram桶求和(Sum of buckets)Histogram 需要保留分桶信息才能计算分位数
Summary通常不降采样Summary 的分位数是服务端计算的,无法聚合
# Mimir 降采样配置
compactor:
compaction_interval: 30m
# 降采样块配置
downsampling:
- resolution: 5m
retention: 90d
- resolution: 1h
retention: 365d
# 降采样时保留的指标(排除不需要降采样的)
downsampling_disable:
- 'go_*'
- 'process_*'
- 'scrape_*'

5.4 降采样对查询的影响#

查询时间范围使用的数据分辨率查询速度
最近 30 天原始数据15s
30-90 天5m 降采样5m
90-365 天1h 降采样1h
365+ 天1d 降采样1d

5.5 降采样的注意事项#

Note

降采样会丢失细节——5m 降采样后,你无法看到 5 分钟内的波动。对于 SLO 计算来说,这通常是可以接受的,因为 SLO 看的是 30 天的总体趋势。但对于排障来说,你可能需要原始数据来定位具体问题。因此,建议保留至少 7 天的原始数据。

六、成本模型#

6.1 可观测性成本构成#

成本项占比优化杠杆
存储40%降采样、压缩、对象存储
计算25%采样、过滤、预计算
网络15%批量发送、压缩传输
人力15%平台化、自助服务
其他5%

6.2 成本优化策略#

策略存储节省实施难度风险
降采样80-95%低分辨率数据可能不够精确
采样(追踪/日志)50-90%可能丢失关键事件
高基数标签移除50-80%丢失维度信息
数据保留期缩短30-50%历史数据不可查
指标合并/删除20-40%丢失部分可见性

6.3 成本/价值矩阵#

graph TB subgraph 高价值["高价值"] HV_LC["低成本低价值<br/>保留"] HV_HC["高成本高价值<br/> 优化"] end subgraph 低价值["低价值"] LV_LC["低成本低价值<br/> 评估"] LV_HC["高成本低价值<br/>删除"] end style HV_LC fill:#c8e6c9,stroke:#2e7d32 style HV_HC fill:#fff3e0,stroke:#e65100 style LV_LC fill:#e3f2fd,stroke:#1565c0 style LV_HC fill:#ffcdd2,stroke:#c62828

6.4 成本计算模型#

# 可观测性成本估算模型
def estimate_monthly_cost(
active_series: int, # 活跃时间序列数
samples_per_second: int, # 每秒样本数
retention_days: int, # 数据保留天数
storage_cost_per_gb: float = 0.023, # S3 存储成本 ($/GB/月)
):
# 每个样本约 2 字节(压缩后)
bytes_per_sample = 2
# 每天存储量
daily_storage_gb = (samples_per_second * 86400 * bytes_per_sample) / (1024**3)
# 总存储量(考虑降采样)
# 原始数据 7 天 + 5m 降采样 30 天 + 1h 降采样 retention_days 天
raw_storage = daily_storage_gb * 7
ds_5m_storage = daily_storage_gb * 0.05 * 30 # 5m 降采样约为原始的 5%
ds_1h_storage = daily_storage_gb * 0.005 * retention_days # 1h 降采样约为原始的 0.5%
total_storage_gb = raw_storage + ds_5m_storage + ds_1h_storage
# 月度存储成本
storage_cost = total_storage_gb * storage_cost_per_gb
# 计算成本(Mimir 集群)
compute_cost = active_series * 0.0001 # 约 $0.1/千序列/月
return {
"storage_gb": total_storage_gb,
"storage_cost": storage_cost,
"compute_cost": compute_cost,
"total_cost": storage_cost + compute_cost,
}
# 示例:1000 万序列,50 万样本/秒
result = estimate_monthly_cost(
active_series=10_000_000,
samples_per_second=500_000,
retention_days=365,
)
# 预估:存储 ~2TB,月度成本 ~$2000-3000
规模活跃序列月度存储成本月度计算成本月度总成本
小型100 万$200$100$300
中型1000 万$2,000$1,000$3,000
大型1 亿$20,000$10,000$30,000
超大型10 亿$200,000$100,000$300,000

七、Recording Rules 与查询优化#

7.1 Recording Rules#

# 预计算常用查询
groups:
- name: slo:rules
interval: 30s
rules:
- record: slo:availability:rate5m
expr: |
1 - (
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
sum(rate(http_requests_total[5m])) by (service)
)
- record: slo:latency:p99:rate5m
expr: |
histogram_quantile(0.99,
sum(rate(http_request_duration_bucket[5m])) by (le, service)
)

7.2 Recording Rules 的设计原则#

Recording Rules 是大规模可观测性的关键优化手段——它将昂贵的实时查询转化为预计算结果:

原则说明反模式
只预计算高频查询面板/告警中使用的查询预计算所有查询
保持标签一致预计算结果的标签与原始查询一致预计算丢失标签
命名规范level:metric:operations 格式随意命名
定期清理删除不再使用的 Recording Rules规则无限增长

Recording Rules 的命名约定:

# 格式:level:metric:operations
# level: 聚合级别(slo, cluster, namespace, service)
# metric: 指标名
# operations: 聚合操作
slo:availability:rate5m # SLO 级别,可用性,5m 速率
cluster:cpu:utilization:rate5m # 集群级别,CPU 利用率
service:latency:p99:rate5m # 服务级别,P99 延迟

7.3 查询优化技巧#

技巧说明效果
使用 Recording Rules预计算常用查询10-100x 加速
缩小时间范围只查询必要的时间范围减少扫描量
减少标签聚合只按必要的标签聚合减少计算量
使用 count_over_time替代 count(metric)更高效
避免正则匹配= 替代 =~减少匹配开销

7.4 慢查询诊断#

# 查询 Mimir 的慢查询日志
# 查看最近 1 小时的慢查询
sum(rate(cortex_query_seconds_bucket{le="30"}[1h])) by (query_type)
/
sum(rate(cortex_query_seconds_count[1h])) by (query_type)
# 查看查询队列深度
cortex_query_frontend_queue_length
# 查看 Ingester 查询延迟
histogram_quantile(0.99, rate(cortex_ingester_query_seconds_bucket[5m]))
慢查询原因诊断方法解决方案
时间范围太大检查查询的 start/end 参数缩小时间范围
标签匹配太宽检查 =~.* 匹配用精确匹配替代
高基数聚合检查 by (label) 的基数预计算为 Recording Rule
大量时间序列检查 count(metric)count_over_time 替代

八、多集群联邦#

8.1 多集群架构#

当你的服务部署在多个 Kubernetes 集群(或多个数据中心)时,需要一个联邦查询层:

graph TB subgraph 集群1["集群 1 (us-east)"] P1["Prometheus/Agent"] M1["Mimir"] end subgraph 集群2["集群 2 (eu-west)"] P2["Prometheus/Agent"] M2["Mimir"] end subgraph 集群3["集群 3 (ap-south)"] P3["Prometheus/Agent"] M3["Mimir"] end subgraph 全局层["全局查询层"] GF["Grafana<br/>多数据源"] RQ["Recording Rules<br/>全局聚合"] end P1 --> M1 P2 --> M2 P3 --> M3 M1 --> GF M2 --> GF M3 --> GF GF --> RQ style 集群1 fill:#e3f2fd,stroke:#1565c0 style 集群2 fill:#e8f5e9,stroke:#2e7d32 style 集群3 fill:#fff3e0,stroke:#e65100 style 全局层 fill:#fce4ec,stroke:#880e4f

8.2 联邦查询策略#

策略说明适用场景
全局 Mimir所有集群写入同一个 Mimir集群在同一区域
联邦 Prometheus全局 Prometheus 从集群 Prometheus 拉取集群在不同区域
Grafana 多数据源Grafana 查询多个 Mimir 数据源混合场景
Recording Rules 联邦每个集群预计算,全局聚合大规模场景

8.3 联邦 Recording Rules#

# 全局 Recording Rules:聚合多个集群的数据
groups:
- name: global:rules
interval: 60s
rules:
# 全局请求率 = 各集群请求率之和
- record: global:http:requests:rate5m
expr: |
sum(http:requests:rate5m) by (service, method, status)
# 从多个集群的 Mimir 数据源查询
# 全局可用性
- record: global:slo:availability:rate5m
expr: |
1 - (
sum(global:http:errors:rate5m) by (service)
/
sum(global:http:requests:rate5m) by (service)
)

九、动手实践#

9.1 基数审计#

# 查询高基数指标
curl 'http://localhost:9090/api/v1/query?query=topk(10,count%20by%20(__name__)%20({__name__=~%22.%2B%22}))'

9.2 配置降采样#

# VictoriaMetrics 降采样
downsampling:
- resolution: 5m
retention: 30d

9.3 配置 Recording Rules#

# 预计算 SLO 指标
groups:
- name: slo:rules
interval: 30s
rules:
- record: slo:availability:rate5m
expr: |
1 - (
sum(rate(http_requests_total{status=~"5.."}[5m])) by (service)
/
sum(rate(http_requests_total[5m])) by (service)
)

9.4 验证清单#

检查项验证方式预期结果
基数可控count(metric) 查询< 预期阈值
降采样生效查询历史数据低分辨率数据可查
成本可控监控存储使用量增长率可控
Recording Rules查询预计算指标返回结果
查询性能面板加载时间< 5s

十、本章小结#

上一章探讨了SLO 驱动的可观测性。 这一章覆盖了大规模可观测性的架构与挑战的关键设计。

主题核心要点关键词
规模级别小型(Prometheus)→ 中型(VM)→ 大型(Mimir)→ 超大型(Mimir + 自研)。规模级别
VictoriaMetrics vs MimirVM 运维简单、性能好、压缩比高;Mimir 多租户、细粒度配额、对象存储。选型取决于规模和需求。VictoriaMetrics vs Mimir
高基数治理在采集层过滤高基数标签,用 error_code 替代 error_message,用 deployment 替代 pod_name。基数预算是有效的治理手段。高基数治理
降采样15s → 5m → 1h → 1d,存储节省 80-95%。不同指标类型需要不同的聚合方式。降采样
成本模型存储占 40%,降采样和采样是最大的优化杠杆。成本/价值矩阵帮助决策哪些数据值得保留。成本模型
Recording Rules预计算高频查询,10-100x 加速。命名规范和定期清理是关键。Recording Rules
多集群联邦全局查询层 + 联邦 Recording Rules,实现跨集群的可观测性。多集群联邦

支持与分享

如果这篇文章对你有帮助,欢迎支持作者或分享给更多人

可观测性规模:5 亿指标/秒
https://blog.souloss.com/posts/observability/observability-at-scale/
作者
Souloss
发布于
2025-10-31
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时