概述

Prometheus 作为云原生监控的事实标准,几乎成为了微服务和 Kubernetes 监控的默认选择。然而,随着业务规模增长,Prometheus 的本地存储架构逐渐暴露出明显瓶颈:单机存储容量有限(默认 15 天保留期)、缺乏原生水平扩展能力、高基数场景下内存飙升、历史数据查询困难。很多团队在时间序列突破百万级别后,开始面临磁盘 IO 压力、存储成本膨胀和查询延迟增长的"三重困境"。

为解决这些问题,社区涌现了 Thanos、Cortex、VictoriaMetrics 等多种长期存储方案。其中 VictoriaMetrics(以下简称 VM)凭借卓越的压缩率、极简的运维复杂度和出色的查询性能,成为越来越多团队的首选。

将详细梳理 VictoriaMetrics 的架构设计、部署模式、数据迁移、性能调优和生产环境好的实践,帮助你在实际项目中做出正确的技术选型和落地。

为什么选择 VictoriaMetrics

Prometheus 的存储瓶颈

理解 VictoriaMetrics 的价值,首先需要明白 Prometheus 的存储瓶颈在哪里:

问题原因影响
数据保留期短默认 TSDB 仅保留 15 天无法做长期趋势分析
无法水平扩展单实例架构,无分片机制单机内存和磁盘成为硬上限
高基数内存膨胀标签组合爆炸导致内存索引激增OOM 频发,监控不可用
全局查询困难多实例数据分散跨集群查询需额外方案
远程存储延迟remote_write 同步模型网络问题导致数据丢失

VictoriaMetrics 的核心优势

VictoriaMetrics 的设计理念是:在完全兼容 Prometheus 生态的前提下,提供更高的性能和更低的资源消耗。

1. 卓越的数据压缩

VM 采用自研的列式存储引擎,针对时间序列数据特征深度优化。根据官方基准测试和大量社区实践,在存储相同监控数据时,VM 所需磁盘空间通常只有 Prometheus TSDB 的 1/5 到 1/7。

# 数据压缩效果对比(基于 100 万活跃时间序列,30 天数据)

存储方案              磁盘占用      压缩比     内存占用
─────────────────────────────────────────────────────────
Prometheus (本地)     350 GB       1x        8 GB
Thanos (S3)           120 GB       2.9x      6 GB (含 Sidecar)
VictoriaMetrics        55 GB       6.4x      3 GB
VictoriaMetrics (集群)  58 GB       6.0x      3.2 GB (总计)

数据来源:VictoriaMetrics 官方基准测试及社区实践报告。实际效果因数据特征而异,建议根据自身场景测试。

2. 极简的运维复杂度

与 Thanos 的多组件架构(Sidecar、Store、Compactor、Query、Receiver、Rule)和 Cortex 的微服务架构(Distributor、Ingester、Querier、Compactor、Store Gateway、Ruler、Alertmanager)相比,VM 的架构极其简洁:

方案核心组件数外部依赖运维复杂度
Thanos6+对象存储 (S3/MinIO)
Cortex7+对象存储 + DynamoDB/etcd很高
VictoriaMetrics (单节点)1极低
VictoriaMetrics (集群)3

3. 高性能查询

VM 高效利用所有可用 CPU 核心进行并行处理,单实例可处理每秒数百万数据点的写入和数十亿行的查询扫描。

4. 多协议兼容

# VictoriaMetrics 支持的数据接入协议

Pull 模式(兼容 Prometheus:
  └── vmagent  抓取 Prometheus exporters  写入 VM

Push 模式:
  ├── Prometheus remote_write  VM(最常用)
  ├── Graphite plaintext protocol
  ├── OpenTSDB telnet/HTTP
  ├── InfluxDB line protocol
  ├── OpenTelemetry
  └── CSV import

查询兼容:
  ├── PromQL(完全兼容)
  ├── MetricsQLPromQL 超集,扩展功能)
  └── Grafana 原生集成

架构设计深入分析

存储引擎

VictoriaMetrics 的核心是其自研的列式存储引擎,理解其内部机制有助于优化使用方式。

数据摄入流程

数据摄入 → Protobuf 编码序列化
         → 内存缓冲区(batch flush)
         → 标签索引构建(倒排索引 + TSID)
         → 列式压缩写入磁盘
         → 后台合并与降采样

关键设计:

  1. TSID(Time Series ID):VM 将标签组合映射为内部的高效 TSID,避免每次查询都扫描全部标签。这比 Prometheus 的 label index 更高效。

  2. 共享字符串池:相同的标签值在内存中只存储一份,通过引用复用,大幅降低高基数场景下的内存消耗。

  3. 延迟加载:查询时只加载所需的数据块,而非整个时间序列,减少 IO 开销。

  4. 列式压缩:每个数据列(时间戳、值、标签)独立压缩,针对不同数据类型使用最优压缩算法。

存储目录结构

/var/lib/victoria-metrics-data/
├── data/
   ├── small/               # 小表分区(近期数据)
      ├── 2024_01/        # 按月分区的数据块
         ├── index.bin   # 倒排索引
         ├── timestamps.bin
         └── values.bin
      └── ...
   ├── big/                 # 大表分区(历史数据)
      └── ...
   └── indexdb/            # 索引数据库
       ├── index.bin
       └── metadata.json
├── metadata/
   └── ...
└── snapshots/               # 快照(备份用)
    └── ...

集群架构组件

VictoriaMetrics 集群版由三个核心组件组成:

                    ┌──────────────┐
                    │   vmagent    │  (可选:数据采集/分片/复制)
                    │   (N 个实例)  │
                    └──────┬───────┘
          ┌────────────────┼────────────────┐
          │                │                │
          ▼                ▼                ▼
  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
  │  vminsert    │ │  vminsert    │ │  vminsert    │
  │  (实例 1)    │ │  (实例 2)    │ │  (实例 N)    │
  └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
         │                │                │
         └────────────────┼────────────────┘
          ┌───────────────┼───────────────┐
          │               │               │
          ▼               ▼               ▼
  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
  │ vmstorage    │ │ vmstorage    │ │ vmstorage    │
  │  (节点 1)    │ │  (节点 2)    │ │  (节点 N)    │
  │  数据存储     │ │  数据存储     │ │  数据存储     │
  └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
         │                │                │
         └────────────────┼────────────────┘
          ┌───────────────┼───────────────┐
          │               │               │
          ▼               ▼               ▼
  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐
  │  vmselect    │ │  vmselect    │ │  vmselect    │
  │  (实例 1)    │ │  (实例 2)    │ │  (实例 N)    │
  └──────┬───────┘ └──────┬───────┘ └──────┬───────┘
         │                │                │
         └────────────────┼────────────────┘
                    ┌─────┴──────┐
                    │  Grafana    │
                    │  / Client   │
                    └────────────┘

组件职责

组件职责无状态可水平扩展
vminsert接收写入请求,路由到 vmstorage 节点
vmstorage数据存储和查询执行否(有状态)是(分片)
vmselect接收查询请求,从 vmstorage 获取并合并结果
vmagent数据采集、分片、复制(可选)
vmalert告警规则评估(可选)
vmbackup数据备份(可选)-

关键设计决策

  • vminsert 和 vmselect 是无状态的,可以自由扩展
  • vmstorage 是有状态的,通过一致性哈希分片,扩容时需要 rebalance
  • vminsert 到 vmstorage 的路由使用一致性哈希,保证相同时间序列始终写入同一 storage 节点
  • vmselect 查询时需要向所有 vmstorage 节点发起查询并合并结果

部署实践

单节点部署

适合中小规模(100 万以下活跃时间序列)或测试环境。

#!/bin/bash
# VictoriaMetrics 单节点部署脚本

# 下载二进制
VM_VERSION="v1.115.0"
wget "https://github.com/VictoriaMetrics/VictoriaMetrics/releases/download/${VM_VERSION}/victoria-metrics-linux-amd64-v${VM_VERSION#v}.tar.gz"
tar -xzf "victoria-metrics-linux-amd64-v${VM_VERSION#v}.tar.gz"

# 创建数据目录
mkdir -p /var/lib/victoria-metrics-data

# 启动单节点
./victoria-metrics-prod \
  -storageDataPath=/var/lib/victoria-metrics-data \
  -retentionPeriod=90d \
  -httpListenAddr=:8428 \
  -memory.allowedBytes=4GB \
  -search.maxConcurrentQueries=8 \
  > /var/log/victoria-metrics.log 2>&1 &

echo "VictoriaMetrics 单节点已启动,监听 8428 端口"

systemd 服务配置

# /etc/systemd/system/victoria-metrics.service
[Unit]
Description=VictoriaMetrics Single Node
After=network.target

[Service]
Type=simple
User=victoria-metrics
Group=victoria-metrics
ExecStart=/usr/local/bin/victoria-metrics-prod \
  -storageDataPath=/var/lib/victoria-metrics-data \
  -retentionPeriod=90d \
  -httpListenAddr=:8428 \
  -memory.allowedBytes=4GB
Restart=on-failure
RestartSec=5
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

配置 Prometheus 远程写入

# prometheus.yml — 添加 remote_write 配置
global:
  scrape_interval: 15s
  evaluation_interval: 15s

remote_write:
  - url: "http://victoria-metrics:8428/api/v1/write"
    queue_config:
      capacity: 10000
      max_samples_per_send: 2000
      batch_send_deadline: 5s
      min_backoff: 1s
      max_backoff: 30s
    # 高可用场景可配置多个远程写入端点
    # remote_timeout: 30s

# 保留原有抓取配置不变
scrape_configs:
  - job_name: "node-exporter"
    static_configs:
      - targets: ["node-exporter:9100"]

  - job_name: "kubernetes-pods"
    kubernetes_sd_configs:
      - role: pod

集群部署

适合大规模(100 万以上活跃时间序列)或需要高可用场景。

Docker Compose 部署示例

# docker-compose.yml
version: '3.8'

services:
  # --- vmstorage 节点(有状态,需持久化) ---
  vmstorage-1:
    image: victoriametrics/vmstorage:v1.115.0-cluster
    command:
      - '--storageDataPath=/storage'
      - '--retentionPeriod=180d'
      - '--httpListenAddr=:8482'
    volumes:
      - vmstorage-1-data:/storage
    ports:
      - "8482"
    restart: unless-stopped

  vmstorage-2:
    image: victoriametrics/vmstorage:v1.115.0-cluster
    command:
      - '--storageDataPath=/storage'
      - '--retentionPeriod=180d'
      - '--httpListenAddr=:8482'
    volumes:
      - vmstorage-2-data:/storage
    ports:
      - "8482"
    restart: unless-stopped

  # --- vminsert 节点(无状态) ---
  vminsert-1:
    image: victoriametrics/vminsert:v1.115.0-cluster
    command:
      - '--httpListenAddr=:8480'
      - '--storageNode=vmstorage-1:8400'
      - '--storageNode=vmstorage-2:8400'
    ports:
      - "8480"
    depends_on:
      - vmstorage-1
      - vmstorage-2
    restart: unless-stopped

  vminsert-2:
    image: victoriametrics/vminsert:v1.115.0-cluster
    command:
      - '--httpListenAddr=:8480'
      - '--storageNode=vmstorage-1:8400'
      - '--storageNode=vmstorage-2:8400'
    ports:
      - "8480"
    depends_on:
      - vmstorage-1
      - vmstorage-2
    restart: unless-stopped

  # --- vmselect 节点(无状态) ---
  vmselect-1:
    image: victoriametrics/vmselect:v1.115.0-cluster
    command:
      - '--httpListenAddr=:8481'
      - '--storageNode=vmstorage-1:8401'
      - '--storageNode=vmstorage-2:8401'
    ports:
      - "8481"
    depends_on:
      - vmstorage-1
      - vmstorage-2
    restart: unless-stopped

  vmselect-2:
    image: victoriametrics/vmselect:v1.115.0-cluster
    command:
      - '--httpListenAddr=:8481'
      - '--storageNode=vmstorage-1:8401'
      - '--storageNode=vmstorage-2:8401'
    ports:
      - "8481"
    depends_on:
      - vmstorage-1
      - vmstorage-2
    restart: unless-stopped

  # --- 负载均衡 ---
  lb-insert:
    image: nginx:alpine
    volumes:
      - ./nginx-insert.conf:/etc/nginx/nginx.conf:ro
    ports:
      - "8480:8480"
    depends_on:
      - vminsert-1
      - vminsert-2
    restart: unless-stopped

  lb-select:
    image: nginx:alpine
    volumes:
      - ./nginx-select.conf:/etc/nginx/nginx.conf:ro
    ports:
      - "8481:8481"
    depends_on:
      - vmselect-1
      - vmselect-2
    restart: unless-stopped

  # --- Grafana ---
  grafana:
    image: grafana/grafana:latest
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin
    ports:
      - "3000:3000"
    restart: unless-stopped

volumes:
  vmstorage-1-data:
  vmstorage-2-data:

Nginx 负载均衡配置(写入端)

# nginx-insert.conf
events {}

http {
    upstream vminsert {
        least_conn;
        server vminsert-1:8480;
        server vminsert-2:8480;
    }

    server {
        listen 8480;

        location / {
            proxy_pass http://vminsert;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_connect_timeout 10s;
            proxy_send_timeout 30s;
            proxy_read_timeout 30s;
        }
    }
}

Nginx 负载均衡配置(查询端)

# nginx-select.conf
events {}

http {
    upstream vmselect {
        least_conn;
        server vmselect-1:8481;
        server vmselect-2:8481;
    }

    server {
        listen 8481;

        location / {
            proxy_pass http://vmselect;
            proxy_set_header Host $host;
            proxy_connect_timeout 10s;
            proxy_send_timeout 60s;
            proxy_read_timeout 60s;
        }
    }
}

Kubernetes 部署(VictoriaMetrics Operator)

生产环境推荐使用 VictoriaMetrics Operator 管理 Kubernetes 上的 VM 集群。

# vmcluster.yaml — 使用 VictoriaMetrics Operator
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMCluster
metadata:
  name: vm-cluster
  namespace: monitoring
spec:
  retentionPeriod: "180d"
  replicationFactor: 2

  # vmstorage 配置
  vmstorage:
    replicaCount: 3
    storageDataPath: "/vm-data"
    storage:
      volumeClaimTemplate:
        spec:
          storageClassName: fast-ssd
          resources:
            requests:
              storage: 500Gi
    resources:
      limits:
        cpu: 4
        memory: 16Gi
      requests:
        cpu: 2
        memory: 8Gi

  # vminsert 配置
  vminsert:
    replicaCount: 2
    resources:
      limits:
        cpu: 2
        memory: 4Gi
      requests:
        cpu: 1
        memory: 2Gi

  # vmselect 配置
  vmselect:
    replicaCount: 2
    resources:
      limits:
        cpu: 2
        memory: 4Gi
      requests:
        cpu: 1
        memory: 2Gi
    cacheMountPath: "/cache"
    storage:
      volumeClaimTemplate:
        spec:
          resources:
            requests:
              storage: 10Gi
---
# vmagent 配置 — 替代 Prometheus 抓取
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMAgent
metadata:
  name: vm-agent
  namespace: monitoring
spec:
  replicaCount: 2
  serviceScrapeNamespaceSelector: {}
  podScrapeNamespaceSelector: {}
  nodeScrapeNamespaceSelector: {}
  staticScrapeNamespaceSelector: {}
  remoteWrite:
    - url: "http://vm-cluster-vminsert.monitoring.svc:8480/insert/0/prometheus/api/v1/write"
  resources:
    limits:
      cpu: 1
      memory: 1Gi
    requests:
      cpu: 500m
      memory: 512Mi
---
# vmalert 配置 — 告警规则评估
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMAlert
metadata:
  name: vm-alert
  namespace: monitoring
spec:
  replicaCount: 2
  datasource:
    url: "http://vm-cluster-vmselect.monitoring.svc:8481/select/0/prometheus"
  notifier:
    url: "http://alertmanager.monitoring.svc:9093"
  evaluationInterval: "30s"
  ruleNamespaceSelector: {}
  resources:
    limits:
      cpu: 1
      memory: 1Gi
    requests:
      cpu: 500m
      memory: 512Mi

参考文档:VictoriaMetrics Operator 官方文档

数据采集与迁移

使用 vmagent 替代 Prometheus 抓取

vmagent 是 VictoriaMetrics 的数据采集组件,可以直接抓取 Prometheus exporters,支持分片和复制,是生产环境中替代 Prometheus 的理想方案。

# vmagent 配置示例
global:
  scrape_interval: 15s
  external_labels:
    cluster: "production"
    region: "us-east-1"

# 抓取配置(与 Prometheus 完全兼容)
scrape_configs:
  - job_name: "kubernetes-nodes"
    kubernetes_sd_configs:
      - role: node
    relabel_configs:
      - source_labels: [__address__]
        regex: "(.*):.*"
        target_label: __address__
        replacement: "${1}:9100"

  - job_name: "kubernetes-pods"
    kubernetes_sd_configs:
      - role: pod
    relabel_configs:
      - source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
        action: keep
        regex: "true"

# 远程写入 VictoriaMetrics
remote_write:
  - url: "http://vminsert:8480/insert/0/prometheus/api/v1/write"
    # 高可用:写入多个 vminsert 实例
    # vmagent 会自动处理重复数据

vmagent 的高级特性——分片抓取

# 多实例 vmagent 分片抓取(每个实例只抓取一部分目标)
# 实例 1
./vmagent-prod \
  -promscrape.config=/etc/vmagent/scrape.yml \
  -remoteWrite.url=http://vminsert:8480/insert/0/prometheus/api/v1/write \
  -promscrape.cluster.membersCount=2 \
  -promscrape.cluster.memberNum=0

# 实例 2
./vmagent-prod \
  -promscrape.config=/etc/vmagent/scrape.yml \
  -remoteWrite.url=http://vminsert:8480/insert/0/prometheus/api/v1/write \
  -promscrape.cluster.membersCount=2 \
  -promscrape.cluster.memberNum=1

vmagent 从 v1.86 开始原生支持 -remoteWrite.shardByURL,从 v1.138.0 进一步将数据分发算法升级为一致性哈希,大幅降低节点变更时的数据重分配比例。

从 Prometheus 迁移历史数据

如果要从已有 Prometheus 实例迁移历史数据到 VictoriaMetrics,有几种方式:

方式一:使用 vmrestore 从快照恢复

# 1. 在 Prometheus 端创建 TSDB 快照
curl -XPOST http://prometheus:9090/api/v1/admin/tsdb/snapshot

# 2. 使用 vmrestore 将快照导入 VM
./vmrestore-prod \
  -src=s3://my-bucket/prom-snapshots/ \
  -dst=/var/lib/victoria-metrics-data/

方式二:使用 vmctl 工具迁移

# vmctl 是官方数据迁移工具,支持多种数据源

# 从 Prometheus TSDB 迁移
./vmctl-prod prometheus \
  -src.address=http://prometheus:9090 \
  -dst.url=http://victoria-metrics:8428 \
  -dst.addr=http://victoria-metrics:8428

# 从 InfluxDB 迁移
./vmctl-prod influxdb \
  -src.addr=http://influxdb:8086 \
  -src.database=monitoring \
  -dst.url=http://victoria-metrics:8428

# 从 OpenTSDB 迁移
./vmctl-prod opentsdb \
  -src.addr=http://opentsdb:4242 \
  -dst.url=http://victoria-metrics:8428

# 从远程 Prometheus 兼容存储迁移
./vmctl-prod remote \
  -src.addr=http://remote-storage:9090 \
  -dst.url=http://victoria-metrics:8428 \
  -time-filter='{"start":"2025-01-01T00:00:00Z","end":"2026-07-01T00:00:00Z"}'

方式三:双写过渡期

# 过渡期:同时写入 Prometheus 本地和 VictoriaMetrics
# prometheus.yml
remote_write:
  - url: "http://victoria-metrics:8428/api/v1/write"
    queue_config:
      capacity: 10000

# 步骤:
# 1. 配置 Prometheus remote_write 到 VM(开始双写)
# 2. 观察数据一致性,确认无丢失
# 3. 迁移历史数据(vmctl)
# 4. 验证历史数据完整
# 5. 切换 Grafana 数据源到 VM
# 6. 确认稳定后停用 Prometheus remote_write
# 7. 最终用 vmagent 完全替代 Prometheus

性能调优

写入性能优化

1. 调整 remote_write 批量参数

# Prometheus remote_write 调优
remote_write:
  - url: "http://victoria-metrics:8428/api/v1/write"
    queue_config:
      capacity: 25000          # 队列容量(默认 10000)
      max_samples_per_send: 5000  # 每批发送样本数(默认 100)
      batch_send_deadline: 2s   # 批量发送超时(默认 5s)
      min_backoff: 500ms       # 最小重试间隔(默认 1s)
      max_backoff: 10s         # 最大重试间隔(默认 30s)
    remote_timeout: 30s

2. 调整 VM 内存限制

# vmstorage 内存分配
./victoria-metrics-prod \
  -memory.allowedBytes=8GB \
  # VM 会使用允许内存的约 60% 作为缓存
  # 剩余留给操作系统和其他进程
  
  # 缓存大小调优
  -cacheExpireDuration=6h \
  -dedup.minScrapeInterval=30s
  
  # 写入优化
  -insert.maxQueueDuration=1m \
  -insert.maxBlockDuration=5m

3. 控制高基数指标

高基数是监控系统的头号杀手。以下是需要严格控制的标签:

# 检查高基数指标
# 按时间序列数量排序,找出最占资源的指标
topk(20, count by (__name__)({__name__=~".+"}))

# 检查标签基数
topk(20, count by (__name__, job)({__name__=~".+"}))

# 找出标签组合爆炸的指标
topk(10, count by (__name__)({__name__=~"http_request_.*"}))

高基数治理建议

场景问题解决方案
HTTP 请求带 path 标签每个 URL 路径生成一条时间序列归一化路径,移除 ID 和参数
容器带 container_id每个容器实例一条序列使用 container_name 替代
用户级监控带 user_id每个用户一条序列聚合到租户/团队级别
异常追踪带 stack_trace每个异常堆栈一条序列只保留异常类型和消息

查询性能优化

1. 使用 MetricsQL 优化查询

MetricsQL 是 VictoriaMetrics 对 PromQL 的扩展,提供了更多优化函数:

# 标准 PromQL 查询
rate(http_requests_total[5m])

# MetricsQL 优化版 — 使用 range_first / range_last
# 只取窗口起始和结束值,减少计算量
# 适合 counter 类型的大范围查询
rate(http_requests_total[5m] @ end())

# MetricsQL 的 keep_metric_names 修饰符
# 保留原始指标名,便于聚合后识别
sum by (job) (rate(http_requests_total[5m])) keep_metric_names

# MetricsQL 的 lag() 函数
# 处理延迟到达的数据,避免计算偏差
rate(http_requests_total[5m] lag(30s))

2. 降采样查询

对于长期数据的查询,使用降采样减少数据量:

# 原始查询(30 天数据,每 15s 一个点 = 172800 个点)
rate(cpu_usage[30d])

# 使用 MetricsQL 的 downgrade
# 将 30 天数据降采样到 1 小时粒度 = 720 个点
# 大幅减少查询数据量和响应时间
rate(cpu_usage[30d:1h])

# 手动降采样
# 每小时取一个平均值
avg_over_time(rate(cpu_usage[5m])[30d:1h])

3. 查询缓存

# vmselect 启用查询缓存
./vmselect-prod \
  -cacheDataPath=/cache \
  # 查询结果缓存
  -search.cacheSize=2GB \
  -search.cacheTTL=5m \
  # 索引缓存
  -search.indexCacheSize=1GB \
  # 过滤缓存
  -search.filterCacheSize=1GB

存储优化

# vmstorage 存储优化参数
./vmstorage-prod \
  -storageDataPath=/vm-data \
  -retentionPeriod=180d \
  
  # 降采样配置
  -downsampling.period=30d:5m,180d:1h \
  # 含义:30天以上数据降采样到5分钟粒度,180天以上降采样到1小时
  
  # 数据去重
  -dedup.minScrapeInterval=30s \
  # 当多个 vmagent 采集相同目标时,自动去重
  
  # 索引优化
  -index.maxSeriesPerIndexBlock=300000 \
  
  # 内存管理
  -memory.allowedBytes=16GB

高可用架构设计

复制与容灾

VictoriaMetrics 集群版支持数据复制,确保单节点故障不丢数据:

# vminsert 配置数据复制
# replicationFactor=2 表示每条数据写入 2 个 vmstorage 节点
./vminsert-prod \
  -httpListenAddr=:8480 \
  -storageNode=vmstorage-1:8400 \
  -storageNode=vmstorage-2:8400 \
  -storageNode=vmstorage-3:8400 \
  -replicationFactor=2
# 复制架构(replicationFactor=2, 3 个 storage 节点)

写入请求 → vminsert
              ├──→ vmstorage-1 (主)  ──→ vmstorage-2 (副本)  ✓ 成功
              ├──→ vmstorage-2 (主)  ──→ vmstorage-3 (副本)  ✓ 成功
              └──→ vmstorage-3 (主)  ──→ vmstorage-1 (副本)  ✓ 成功

# vmstorage-1 宕机后
# vminsert 自动将数据写入 vmstorage-2 和 vmstorage-3
# 查询时 vmselect 从存活的节点获取数据,无感知

备份与恢复

#!/bin/bash
# VictoriaMetrics 备份脚本

# 1. 创建快照
SNAPSHOT_PATH=$(curl -s -X POST http://localhost:8428/snapshot/create | jq -r '.snapshot')
echo "创建快照: ${SNAPSHOT_PATH}"

# 2. 使用 vmbackup 推送到 S3
./vmbackup-prod \
  -storageDataPath=/var/lib/victoria-metrics-data \
  -snapshotName="${SNAPSHOT_PATH}" \
  -dst=s3://monitoring-backup/vm-snapshots/$(date +%Y%m%d)/

# 3. 验证备份完整性
./vmbackupmanager-prod verify \
  -dst=s3://monitoring-backup/vm-snapshots/$(date +%Y%m%d)/

# 4. 清理旧快照(保留最近 7 天)
curl -X POST "http://localhost:8428/snapshot/delete?keep=7"

echo "备份完成"
# 恢复流程
./vmrestore-prod \
  -src=s3://monitoring-backup/vm-snapshots/20260711/ \
  -dst=/var/lib/victoria-metrics-data/

Grafana 集成

数据源配置

# Grafana 数据源配置(Provisioning)
apiVersion: 1
datasources:
  # VictoriaMetrics 作为 Prometheus 数据源
  - name: VictoriaMetrics
    type: prometheus
    access: proxy
    url: http://vmselect:8481/select/0/prometheus/
    isDefault: true
    jsonData:
      timeInterval: "15s"
      httpMethod: "POST"
      # 启用 MetricsQL 扩展
      customQueryParameters: "extra_label=cluster=production"

  # 如果使用多租户
  - name: VictoriaMetrics (tenant-a)
    type: prometheus
    access: proxy
    url: http://vmselect:8481/select/tenant-a/prometheus/
    jsonData:
      timeInterval: "15s"

推荐的 Dashboard

VictoriaMetrics 官方提供了丰富的 Grafana Dashboard 模板:

# 导入官方 Dashboard(Grafana → Import → ID)

Dashboard ID    说明
────────────────────────────────────
11176          VictoriaMetrics 单节点概览
14289          VictoriaMetrics 集群概览
14592          vmagent 状态概览
14594          vmalert 规则执行概览
14595          vmrestore 备份概览
14596          vmbackup 备份状态

生产环境好的实践

容量规划

根据 VictoriaMetrics 官方文档和社区经验,以下容量规划参考:

规模活跃时间序列写入速率单节点配置集群配置
小型<100 万<20K samples/s4C/8GB/100GB SSD不需要
中型100-500 万20K-100K8C/16GB/500GB SSD3 storage × 8C/16GB
大型500-2000 万100K-500K不推荐3-5 storage × 16C/64GB
超大>2000 万>500K不推荐5-10 storage × 32C/128GB

存储容量估算公式

#!/usr/bin/env python3
"""
VictoriaMetrics 存储容量估算工具
基于活跃时间序列数量、保留期和采样间隔进行估算
"""

def estimate_storage(
    active_series: int,
    retention_days: int,
    scrape_interval_sec: int = 15,
    avg_label_size: int = 100,  # 每条序列的平均标签字节数
):
    """
    估算 VictoriaMetrics 所需的磁盘空间
    
    参数:
        active_series: 活跃时间序列数量
        retention_days: 数据保留天数
        scrape_interval_sec: 采集间隔(秒)
        avg_label_size: 平均标签字节数
    """
    # 每个数据点大小(VM 压缩后约 1-2 字节)
    bytes_per_point = 1.5
    
    # 计算总数据点数
    points_per_series_per_day = 86400 / scrape_interval_sec
    total_points = active_series * points_per_series_per_day * retention_days
    
    # 数据点存储空间
    data_storage = total_points * bytes_per_point
    
    # 索引空间(约为数据空间的 20-30%)
    index_storage = data_storage * 0.25
    
    # 标签存储空间
    label_storage = active_series * avg_label_size * 2  # 压缩后
    
    # 总存储空间
    total_storage = data_storage + index_storage + label_storage
    
    # 额外开销( WAL、临时文件等,约 10%)
    total_with_overhead = total_storage * 1.1
    
    return {
        "active_series": active_series,
        "retention_days": retention_days,
        "scrape_interval_sec": scrape_interval_sec,
        "total_points": int(total_points),
        "data_storage_gb": data_storage / 1024**3,
        "index_storage_gb": index_storage / 1024**3,
        "label_storage_gb": label_storage / 1024**3,
        "total_storage_gb": total_with_overhead / 1024**3,
    }

# 示例计算
configs = [
    ("小型", 100_000, 90, 15),
    ("中型", 1_000_000, 180, 15),
    ("大型", 5_000_000, 180, 15),
    ("超大", 20_000_000, 365, 30),
]

print(f"{'规模':<8} {'序列数':>12} {'保留天数':>8} {'存储(GB)':>10} {'存储(TB)':>10}")
print("-" * 55)

for name, series, days, interval in configs:
    result = estimate_storage(series, days, interval)
    print(f"{name:<8} {series:>12,} {days:>8} {result['total_storage_gb']:>10.1f} {result['total_storage_gb']/1024:>10.2f}")

输出示例:

规模       序列数    保留天数   存储(GB)   存储(TB)
-------------------------------------------------------
小型        100,000       90        1.2       0.00
中型      1,000,000      180       25.6       0.02
大型      5,000,000      180      128.0       0.13
超大     20,000,000      365      768.0       0.80

注意:以上为理论估算值,实际存储消耗受数据特征(标签基数、数据分布等)影响较大,建议以实际测试为准。

监控 VictoriaMetrics 自身

# vmagent 抓取 VictoriaMetrics 自身指标的配置
scrape_configs:
  - job_name: "victoria-metrics"
    static_configs:
      - targets: ["victoria-metrics:8428"]
    # VM 在 /metrics 端点暴露自身指标

  - job_name: "vmstorage"
    static_configs:
      - targets: ["vmstorage-1:8482", "vmstorage-2:8482"]

  - job_name: "vminsert"
    static_configs:
      - targets: ["vminsert-1:8480", "vminsert-2:8480"]

  - job_name: "vmselect"
    static_configs:
      - targets: ["vmselect-1:8481", "vmselect-2:8481"]

关键告警规则

# vmalert 规则 — VictoriaMetrics 自身健康监控
groups:
  - name: victoriametrics-alerts
    rules:
      # vmstorage 磁盘使用率
      - alert: VMStorageDiskUsageHigh
        expr: |
          100 * (1 - vm_data_disk_free_bytes / vm_data_disk_total_bytes) > 85          
        for: 10m
        labels:
          severity: warning
        annotations:
          summary: "VM storage 磁盘使用率超过 85%"
          description: "节点 {{ $labels.instance }} 磁盘使用率: {{ $value }}%"

      # 写入速率异常
      - alert: VMIngestionRateDrop
        expr: |
          rate(vm_rows_ingested_total[5m]) < 100          
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "VM 数据写入速率异常下降"
          description: "当前写入速率: {{ $value }} rows/s"

      # 查询延迟过高
      - alert: VMSlowQueries
        expr: |
          histogram_quantile(0.95, 
            rate(vm_select_query_duration_seconds_bucket[5m])
          ) > 10          
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "VM 查询 P95 延迟超过 10 秒"
          description: "P95 延迟: {{ $value }}s"

      # 内存使用过高
      - alert: VMHighMemoryUsage
        expr: |
          100 * vm_memory_bytes / vm_memory_allowed_bytes > 90          
        for: 5m
        labels:
          severity: critical
        annotations:
          summary: "VM 内存使用率超过 90%"
          description: "内存使用: {{ $value }}%"

      # 节点不可达
      - alert: VMNodeDown
        expr: up{job=~"victoria-metrics|vmstorage|vminsert|vmselect"} == 0
        for: 1m
        labels:
          severity: critical
        annotations:
          summary: "VM 节点不可达"
          description: "{{ $labels.instance }} 不可达"

Thanos vs VictoriaMetrics 选型对比

维度ThanosVictoriaMetrics
架构复杂度高(6+ 组件)低(1-3 组件)
外部依赖对象存储 (S3/MinIO)
数据压缩率中等 (2-3x)高 (5-7x)
查询性能中等
运维成本
全局查询原生支持原生支持
降采样Compactor 组件内置
高可用Sidecar + Receiver集群复制
生态兼容完全兼容 Prometheus完全兼容 + MetricsQL 扩展
学习曲线陡峭平缓
适用规模中大型中小型到超大

选型建议

  • 选 VictoriaMetrics:希望极简运维、追求高压缩率和查询性能、没有对象存储基础设施、团队规模有限
  • 选 Thanos:已有对象存储基础设施、希望利用 S3 做冷数据归档、需要与现有 Prometheus 深度集成、对对象存储生态有经验
  • 混合方案:VictoriaMetrics 做热数据存储(近 3 个月),Thanos 做冷数据归档(3 个月以上)到 S3

参考对比:VictoriaMetrics vs Thanos社区选型讨论

总结

VictoriaMetrics 作为 Prometheus 长期存储方案,在压缩率、查询性能和运维复杂度方面都有显著优势。以下是选择和落地 VM 的关键要点:

  1. 从单节点开始:对于大多数中小规模场景,VM 单节点已经足够强大,单实例可处理 100 万活跃时间序列和每秒 200 万样本写入。无需一开始就上集群版。

  2. vmagent 是采集利器:使用 vmagent 替代 Prometheus 做数据采集,可以获得分片、复制和协议转换能力,同时完全兼容 Prometheus 配置格式。

  3. 复制保证可用性:集群版配置 replicationFactor=2 即可实现数据冗余,单节点故障不影响读写。配合 vmbackup 做异地备份,实现完整的容灾方案。

  4. 治理高基数是前提:再好的存储方案也扛不住无限制的标签爆炸。在迁移到 VM 之前,先治理高基数指标,这是监控系统健康的基础。

  5. 善用 MetricsQL:MetricsQL 的降采样、延迟处理等扩展函数,可以在不修改采集配置的情况下优化查询性能。

  6. 监控监控系统:VictoriaMetrics 自身的健康同样需要监控。磁盘使用率、写入速率、查询延迟和内存使用是四大核心指标。

  7. 迁移路径清晰:双写过渡 → 历史数据迁移 → 数据源切换 → 停用旧 Prometheus,每一步都可以验证,风险可控。

参考文档:

参考资料与致谢

本文在撰写过程中参考了以下资料,感谢原作者的贡献:

  1. VictoriaMetrics Operator 官方文档 — VictoriaMetrics 文档,参考了VictoriaMetrics Operator 官方文档相关内容
  2. VictoriaMetrics vs Thanos — VictoriaMetrics 文档,参考了VictoriaMetrics vs Thanos相关内容
  3. 社区选型讨论 — CSDN,参考了社区选型讨论相关内容
  4. docs.victoriametrics.com — VictoriaMetrics 文档,参考了相关技术内容
  5. github.com — GitHub 开源社区,参考了VictoriaMetrics相关内容
  6. docs.victoriametrics.com — VictoriaMetrics 文档,参考了FAQ.html相关内容