当你的集群从 100 个服务增长到 5000 个,活跃时间序列从 100 万增长到 1 亿,可观测性系统本身就成了最大的挑战。Prometheus 单机无法承载、存储成本线性增长、查询延迟从秒级变成分钟级、基数爆炸导致 OOM——你需要为规模而设计。
一、规模挑战全景
1.1 规模增长曲线
1.2 各规模的关键挑战
| 规模 | 活跃序列 | 关键挑战 | 解决方案 |
|---|---|---|---|
| 小型 | < 100 万 | 基础监控覆盖 | Prometheus 单机 |
| 中型 | 100 万-1000 万 | 基数控制、存储成本 | VictoriaMetrics 集群 |
| 大型 | 1000 万-1 亿 | 查询性能、降采样 | Mimir + 降采样 |
| 超大型 | > 1 亿 | 全局视图、多集群 | Mimir 多集群 + 自研 |
二、5 亿指标/秒的架构
2.1 采集层
2.2 关键参数
| 组件 | 实例数 | 单实例吞吐 | 总吞吐 | 内存 |
|---|---|---|---|---|
| OTel Agent | 100 | 5M samples/s | 500M samples/s | 512Mi |
| OTel Gateway | 10 | 50M samples/s | 500M samples/s | 2Gi |
| Mimir Distributor | 10 | 50M samples/s | 500M samples/s | 1Gi |
| Mimir Ingester | 30 | 17M samples/s | 500M samples/s | 16Gi |
| Mimir Querier | 20 | - | - | 8Gi |
2.3 大规模架构的详细设计
5 亿指标/秒的架构不只是”多加几台机器”,它需要解决一系列分布式系统问题:
写入路径:
| 写入阶段 | 关键设计 | 失败处理 |
|---|---|---|
| SDK → Agent | 本地缓冲 + 重试 | Agent 不可用时降级到直接发送 |
| Agent → Gateway | 批量发送 + 压缩 | Gateway 不可用时本地排队 |
| Gateway → Distributor | 限流 + 路由 | 超限返回 429,Agent 重试 |
| Distributor → Ingester | 一致性哈希 + 3 副本 | 1 副本失败不影响写入 |
| Ingester → 对象存储 | 定期刷盘 + 压缩 | 刷盘失败重试 |
查询路径:
| 查询阶段 | 关键设计 | 优化手段 |
|---|---|---|
| Query-Frontend | 查询排队 + 超时控制 | 拆分大查询为子查询 |
| Querier | 并行查询 + 结果合并 | 缓存查询结果 |
| Ingester 查询 | 内存查询 | 最近数据无需读磁盘 |
| Store-Gateway | 对象存储索引 + 缓存 | 预取 + 布隆过滤器 |
三、VictoriaMetrics vs Mimir 集群对比
3.1 核心对比
| 维度 | VictoriaMetrics 集群 | Grafana Mimir |
|---|---|---|
| 架构 | vmselect/vminsert/vmstorage | distributor/ingester/querier/store-gateway |
| 多租户 | 账号 ID 隔离 | Org ID 隔离 + 细粒度配额 |
| 存储后端 | 本地磁盘(推荐) | 对象存储(S3/GCS) |
| 压缩比 | ~10x(原生) | ~5x(对象存储) |
| 查询性能 | 极快(本地磁盘) | 快(对象存储 + 缓存) |
| 运维复杂度 | 低(3 个组件) | 高(10+ 个微服务) |
| 社区活跃度 | 高 | 高(Grafana Labs 支持) |
| 企业功能 | 企业版(付费) | 开源 + Grafana Cloud |
3.2 选型决策
| 场景 | 推荐 | 理由 |
|---|---|---|
| < 5000 万序列,单团队 | VictoriaMetrics | 运维简单,性能好 |
| > 5000 万序列,多团队 | Mimir | 多租户 + 细粒度配额 |
| 已在 Grafana Cloud | Mimir | 生态一致 |
| 存储成本优先 | VictoriaMetrics | 压缩比更高 |
| 合规要求(数据隔离) | Mimir | 物理隔离支持 |
3.3 VictoriaMetrics 集群配置
# VictoriaMetrics 集群部署# vminsert — 写入入口apiVersion: apps/v1kind: Deploymentmetadata: name: vminsertspec: 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/v1kind: StatefulSetmetadata: name: vmstoragespec: 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-data3.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,000 | 320,000 | 64% |
| 订单团队 | 1,000,000 | 850,000 | 85% |
| 搜索团队 | 2,000,000 | 1,200,000 | 60% |
| 推荐团队 | 200,000 | 180,000 | 90% |
高基数指标是可观测性成本的头号杀手。一个包含 user_id 标签的指标,在 100 万用户下会产生 100 万条时间序列,每条序列 3-5 KB 内存,总计 3-5 GB——仅一个指标。务必在采集层(OTel Collector)就过滤掉高基数标签。
五、降采样策略
5.1 降采样原理
降采样将高分辨率数据聚合为低分辨率数据,降低长期存储成本:
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 降采样的注意事项
降采样会丢失细节——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 成本/价值矩阵
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 集群(或多个数据中心)时,需要一个联邦查询层:
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: 30d9.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 Mimir | VM 运维简单、性能好、压缩比高;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,实现跨集群的可观测性。 | 多集群联邦 |
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






