VictoriaMetrics 核心概念深度指南:数据模型、采集模式与查询 API
2026/9/14 12:09:24 网站建设 项目流程

VictoriaMetrics 核心概念深度指南:数据模型、采集模式与查询 API

【免费下载链接】VictoriaMetricsVictoriaMetrics: fast, cost-effective monitoring solution and time series database项目地址: https://gitcode.com/GitHub_Trending/vi/VictoriaMetrics

本文以 VictoriaMetrics 官方核心概念文档 keyConcepts.md 为骨架,系统讲解监控系统中最基础也最重要的知识体系:指标数据模型与时间序列、Cardinality(基数)、四种常用指标类型(Counter/Gauge/Histogram/Summary)、Push 与 Pull 两种数据采集模型、瞬时查询与范围查询两类查询 API,以及 MetricsQL 查询语言的核心语法。读完本文,你将能正确设计指标命名与标签、选型采集架构、精准调用查询接口,并结合仓库源码理解 VictoriaMetrics 底层实现的关键取舍。

数据模型(Data Model)

什么是指标(Metric)

简单来说,metric(指标)是对某个事物的数值化度量或观测。监控系统中使用指标最常见的场景包括:

  • 检查系统在特定时间段内的行为表现;
  • 将行为变化与其他测量指标关联起来定位问题;
  • 观察或预测趋势;
  • 当指标超过阈值时触发事件(告警)。

指标的结构

以一个追踪应用处理请求数量的指标为例,定义名为requests_total的指标。为了让指标更精确,可以命名为requests_success_total(仅统计成功请求)或request_errors_total(统计失败请求)。指标命名非常重要,它应当像编程中的变量名一样,让每一位阅读者都能清楚知道度量的是什么。

Labels(标签)

每个指标都可以携带额外的元信息,即「标签-值」对:

requests_total{path="/", code="200"} requests_total{path="/", code="403"}

花括号内的一组labels提供了上下文——说明请求是在哪个path、以什么code被处理的。标签值对永远是string类型。VictoriaMetrics 的数据模型是无模式(schemaless)的,无需预先定义指标名或标签,用户可以随时添加或更改写入的指标。

实际上,指标名也是一个标签,只是它的名字特殊,叫__name__。自 v1.111.0 起,__name__键可以省略以简化书写,因此下面三种写法表示的序列是完全相同的:

requests_total{path="/", code="200"} {__name__="requests_total", path="/", code="200"} {"requests_total", path="/", code="200"}

标签可以由 vmagent 或 Prometheus 在采集时自动附加到时间序列上。VictoriaMetrics 支持在查询 API 上强制标签过滤来模拟数据隔离;但真正的数据隔离需要借助集群版的多租户特性实现。

时间序列(Time Series)

指标名 + 标签组合共同定义了一条time series(时间序列)。例如requests_total{path="/", code="200"}requests_total{path="/", code="403"}因为code标签值不同,是两条不同的时间序列。

时间序列的唯一数量会影响数据库资源占用。可参阅 FAQ 中关于「什么是活跃时间序列」和「什么是高 churn rate(序列变更率)」的说明。

基数(Cardinality)

唯一时间序列的数量称为cardinality(基数)。拥有过多唯一时间序列被称为high cardinality(高基数),它可能导致 VictoriaMetrics 资源占用上升。

排查与治理手段包括:Cardinality Explorer(基数浏览器,用于定位高基数来源)以及vmestimator基数估算工具。

原始样本(Raw Samples)

每条唯一时间序列可以由任意数量的(value, timestamp)数据点(即raw samples,原始样本)组成,并按timestamp排序。VictoriaMetrics 将所有valuefloat64存储并施加额外压缩,因此可以精确存储最多 12 位十进制数字的整数值、以及最多 12 位有效十进制数字的浮点值;如果数值的有效十进制数字超过 12 位,则存储时低位数字可能丢失。timestamp是毫秒精度的 Unix 时间戳。

下面是 Prometheus 文本暴露格式(外部资料,仅作背景说明)下的单个原始样本示例:

requests_total{path="/", code="200"} 123 4567890
  • requests_total{path="/", code="200"}标识该样本所属的时间序列;
  • 123是样本值;
  • 4567890是该样本的可选时间戳;若缺失,则在写入 VictoriaMetrics 时使用当前时间戳。
时间序列分辨率(Resolution)

Resolution(分辨率)是指时间序列中原始样本之间的最小间隔。例如:

---------------------------------------------------------------------- | <time series> | <value> | <timestamp> | | requests_total{path="/health", code="200"} | 1 | 1676297640 | | requests_total{path="/health", code="200"} | 2 | 1676297670 | | requests_total{path="/health", code="200"} | 3 | 1676297700 | | requests_total{path="/health", code="200"} | 4 | 1676297730 | ....

上例中requests_total{path="/health", code="200"}的值每30s更新一次,因此其分辨率也是30s

在 Pull 模型下,分辨率等于scrape_interval(抓取间隔),由监控系统(服务端)控制;在 Push 模型下,分辨率是样本时间戳之间的间隔,由客户端(指标采集器)控制。

请尽量保持时间序列分辨率的一致性,因为部分 MetricsQL 函数会假设它是一致的。

指标类型(Types of Metrics)

VictoriaMetrics 内部并没有「指标类型」的概念——TSDB 只能看到指标名、标签、值和时间戳。指标类型这一概念是为了帮助用户理解指标是如何被测量的。常见的指标类型有 4 种。

Counter

Counter(计数器)用于统计事件发生的次数。它的值随时间只增不减,一般情况下不会下降,唯一的例外是counter reset(计数器重置,例如服务重启后归零)。因此,counter指标展示的是「自服务启动以来观察到的事件总数」。

在编程中,counter就是每当事件发生时你执行自增操作的变量。

vm_http_requests_total是典型的 counter 示例。它常被用于统计请求数、错误数、日志条数、消息数等事件。与 counter 搭配最常用的 MetricsQL 函数有:

  • rate——计算指标变化的平均每秒速率。例如rate(requests_total)表示平均每秒处理多少个请求;
  • increase——计算指标在指定时间范围(方括号内)内的增长量。例如increase(requests_total[1h])表示过去一小时内服务的请求总数。

Counter 允许出现小数。例如request_duration_seconds_sum累加所有请求的耗时,而每个耗时可能是0.5秒这样的小数,所以累计和也允许是小数。

建议给 counter 指标名加上_total_sum_count后缀,便于人类快速区分其与其他指标类型。

Gauge

Gauge(仪表盘/瞬时值)用于测量可升可降的数值,例如process_resident_memory_anon_bytes反映应用每一时刻的内存占用,会随进程分配/释放内存而频繁上下波动。在编程中,gauge就是随着数值变化你不断设置新值的变量。

Gauge 的典型使用场景:

  • 测量温度、内存占用、磁盘占用等;
  • 保存某进程的状态。例如config_reloaded_successful在配置重载成功时设为1、失败时设为0
  • 保存事件发生的时间戳。例如config_last_reload_success_timestamp_seconds保存最后一次配置重载成功的时间戳。

与 gauge 最常搭配的是聚合函数和 rollup(滚动)函数。

Histogram

Histogram(直方图)是一组带vmrangele标签的 counter 指标。vmrangele标签定义了某个 bucket(桶)的测量边界;当观测值落入某个 bucket 时,对应的 counter 就会自增。

直方图桶通常以_bucket后缀命名。例如 VictoriaMetrics 使用vm_rows_read_per_query直方图跟踪每次查询处理的行数分布,其暴露格式如下:

vm_rows_read_per_query_bucket{vmrange="4.084e+02...4.642e+02"} 2 vm_rows_read_per_query_bucket{vmrange="5.275e+02...5.995e+02"} 1 vm_rows_read_per_query_bucket{vmrange="8.799e+02...1.000e+03"} 1 vm_rows_read_per_query_bucket{vmrange="1.468e+03...1.668e+03"} 3 vm_rows_read_per_query_bucket{vmrange="1.896e+03...2.154e+03"} 4 vm_rows_read_per_query_sum 15582 vm_rows_read_per_query_count 11

其中vm_rows_read_per_query_bucket{vmrange="4.084e+02...4.642e+02"} 2表示自上次 VictoriaMetrics 启动以来,有 2 次查询的行数落在(408.4 - 464.2]区间内。

_bucket后缀的计数器可以借助histogram_quantile函数估算观测值的任意百分位数。例如下面的查询返回过去一小时(方括号中的1h)内每次查询读取行数的第 99 百分位估算值:

histogram_quantile(0.99, sum(increase(vm_rows_read_per_query_bucket[1h])) by (vmrange))

该查询的工作方式:

  1. increase(vm_rows_read_per_query_bucket[1h])计算过去一小时内每个实例每个 bucket 的事件数;
  2. sum(...) by (vmrange)将具有相同vmrange值的各实例 bucket 求和,得到每个 bucket 的总事件数;
  3. histogram_quantile(0.99, ...)基于第 2 步得到的vmrangebuckets 计算第 99 百分位数。

直方图类型还暴露两个额外的_sum_count后缀计数器:

  • vm_rows_read_per_query_sum是所有观测值的总和,例如自启动以来所有查询服务的行数总和;
  • vm_rows_read_per_query_count是观测事件的总数,例如自启动以来观测到的查询总数。

这两个计数器可以用来计算指定回看窗口内的平均测量值。例如下面查询计算最近 5 分钟(方括号中的5m)内每次查询读取的平均行数:

increase(vm_rows_read_per_query_sum[5m]) / increase(vm_rows_read_per_query_count[5m])

vm_rows_read_per_query直方图在 Go 应用中可以通过 VictoriaMetrics/metrics 客户端库这样使用:

// 定义直方图 rowsReadPerQuery := metrics.NewHistogram(`vm_rows_read_per_query`) // 在处理过程中使用直方图 for _, query := range queries { rowsReadPerQuery.Update(float64(len(query.Rows))) }

每次调用rowsReadPerQuery.Update时会发生:

  • countervm_rows_read_per_query_sum增加len(query.Rows)的值;
  • countervm_rows_read_per_query_count自增 1;
  • 仅当观测值落在某个vmrange定义的范围内时,对应的vm_rows_read_per_query_bucketcounter 才会自增。

这种 counter 组合可以用于在 Grafana 中绘制热力图(Heatmap)以及计算分位数。注意:Grafana 无法识别带vmrange标签的 bucket,因此在 Grafana 中构建热力图前,必须先使用prometheus_buckets函数把vmrange标签的 bucket 转换为le标签的 bucket。

直方图通常用于测量延迟分布、元素尺寸(如批次大小)分布等。VictoriaMetrics 支持两种直方图实现:

  1. Prometheus histogram——主流指标埋点客户端库普遍支持的标准实现,需要用户静态地预先定义 bucket 边界;
  2. VictoriaMetrics histogram——由 VictoriaMetrics/metrics 埋点库支持,自动处理 bucket 边界,用户无需关心。
Summary

Summary(摘要)与 histogram 类似,也用于分位数计算。主要区别在于:Summary 的计算发生在客户端,因此指标暴露格式中已经包含了预先计算好的分位数:

go_gc_duration_seconds{quantile="0"} 0 go_gc_duration_seconds{quantile="0.25"} 0 go_gc_duration_seconds{quantile="0.5"} 0 go_gc_duration_seconds{quantile="0.75"} 8.0696e-05 go_gc_duration_seconds{quantile="1"} 0.001222168 go_gc_duration_seconds_sum 0.015077078 go_gc_duration_seconds_count 83

这种方式让 Summary 使用起来更简单,但也比 histogram 有显著局限:

  • 无法跨多个 summary 指标聚合计算分位数。例如sum(go_gc_duration_seconds{quantile="0.75"})avg(...)max(...)都不会返回从多实例收集的go_gc_duration_seconds的正确 75 百分位;
  • 无法计算预计算分位数以外的分位数
  • 无法对任意时间范围内收集的测量值计算分位数。通常 Summary 的分位数只覆盖固定时间范围(如最近 5 分钟)。

Summary 通常用于跟踪延迟、元素尺寸(如批次大小)等场景的预定义百分位数。

为应用埋点(Instrumenting)

如前所述,指标类型定义的是「如何被测量」;VictoriaMetrics TSDB 本身并不了解指标类型,它只看到指标名、标签、值和时间戳。这些指标是什么、测量什么、如何测量,完全取决于发出它们的应用。

官方推荐使用VictoriaMetrics/metrics 客户端库对应用进行埋点,同时也兼容各类Prometheus 客户端库(其埋点格式完全兼容 VictoriaMetrics 数据模型)。

命名

官方建议遵循 Prometheus 的指标命名规范。VictoriaMetrics 没有严格限制,任何指标名和标签都会被接受,但遵循规范有助于保持名称有意义、描述性强且对他人清晰易读。

标签

每次测量可以携带任意数量的key="value"标签。好的实践是限制标签数量,否则将难以处理携带大量标签的测量数据。默认情况下,VictoriaMetrics 将每条测量数据的标签数限制为40,超出部分会被丢弃;该限制可通过-maxLabelsPerTimeseries命令行标志调整(但官方不建议调大)。

从源码可以验证这些默认值:在 lib/timeserieslimits/timeseries_limits.go 中定义了maxLabelsPerTimeseries = 40maxLabelNameLen = 256(标签名最大长度,默认 256 字节)、maxLabelValueLen = 4 * 1024(标签值最大长度,默认 4KiB)。当写入的样本超过任一限制时,IsExceeding 会将其忽略,并累加vm_rows_ignored_total{reason="too_many_labels"}vm_rows_ignored_total{reason="too_long_label_name"}vm_rows_ignored_total{reason="too_long_label_value"}等指标,便于观测被丢弃的数据量。

每个标签值可以包含任意字符串,但好的实践是使用简短而有意义的标签值来描述指标的属性,而不是「讲故事」。例如environment="prod"是合适的,而log_message="long log message with a lot of details..."则不合适。默认情况下标签值限制为 4KiB,可通过-maxLabelValueLen标志修改。

必须严格控制唯一标签值的数量,因为每个唯一的标签值都会衍生出一条新的时间序列。应尽量避免使用 session ID、query ID 这类易变的标签值,以免造成资源浪费和数据库性能下降。

多租户(Multi-tenancy)

VictoriaMetrics 的集群版支持多租户,用于数据隔离。而单机版可以通过在写入路径上附加标签、并在读取路径上强制标签过滤来模拟多租户。

写入数据(Write Data)

现代监控应用中常见的两种采集模型——Push 与 Pull——VictoriaMetrics 都支持。

Push 模型

在 Push 模型中,客户端定期将采集到的指标发送给服务端。由客户端(应用)决定何时、向哪里发送指标。VictoriaMetrics 支持多种数据接入协议(即push protocols),完整列表见单机版文档。所有协议都与 VictoriaMetrics 数据模型完全兼容,可安全用于生产环境。

官方推荐使用 VictoriaMetrics/metrics 客户端库向 VictoriaMetrics 推送应用指标;也可以使用与上述协议兼容的既有客户端,例如使用 InfluxDB line protocol 的 Telegraf。

编写自定义客户端或为应用埋点写指标,简单到只需要发送一个 POST 请求:

curl -d '{"metric":{"__name__":"foo","job":"node_exporter"},"values":[0,1,2],"timestamps":[1549891472010,1549891487724,1549891503438]}' -X POST 'http://localhost:8428/api/v1/import'

指标可以推送到单节点 VictoriaMetrics、集群版组件 vminsert,以及 vmagent。

Push 模型的优点:

  • VictoriaMetrics 侧配置更简单——无需配置被监控应用的位置,也无需复杂的服务发现机制;
  • 安全设置更简单——无需为 VictoriaMetrics 配置到每个被监控应用的访问通道。

Push 协议的缺点:

  • 被监控应用的配置复杂度增加——每个应用都需要单独配置监控系统地址,还需要配置推送间隔以及推送失败时的策略;
  • 向多个监控系统投递指标的非平凡配置
  • 难以判断应用是宕机了还是仅仅停止发送指标
  • 应用可能以过短的间隔推送指标,导致监控系统过载。

Pull 模型

Pull 模型由 Prometheus 推广开来:由监控系统决定何时、从哪里拉取指标。在 Pull 模型中,监控系统需要知晓所有需要监控的应用;指标通过 HTTP 协议定期(scrape_interval)从已知应用(即scrape targets,抓取目标)抓取。

VictoriaMetrics 支持像 Prometheus 一样发现 Prometheus 兼容的目标并抓取指标,详见单机版抓取文档。抓取由单节点 VictoriaMetrics 和 vmagent 支持。

Pull 模型的优点:

  • 更易调试——VictoriaMetrics 知道所有被监控应用(scrape targets),查询up == 0可以立即发现不可达的抓取目标;抓取目标的实际信息可在http://victoriametrics:8428/targetshttp://vmagent:8429/targets查看;
  • 监控系统控制指标抓取频率,更容易控制自身负载;
  • 应用不感知监控系统的存在,无需实现指标投递逻辑。

Pull 模型的缺点:

  • 安全设置更复杂——监控系统需要能够访问它监控的应用;
  • 需要非平凡的服务发现机制。

常见的数据采集架构

许多部署只使用两种模型之一,也有不少部署同时使用两者。最常见的做法是两者并用:引入一个额外的组件——vmagent。vmagent 是一个轻量级代理,核心职责是采集、过滤、relabel(重新标记)并向 VictoriaMetrics 投递指标,它支持上文提到的所有 push 和 pull 协议。

基础的监控部署可参考仓库中的 docker-compose 示例:vmagent 在 prometheus-vm-single.yml 中定义了一组抓取目标,并在 compose-vm-single.yml 中把采集到的数据转发给 VictoriaMetrics;VictoriaMetrics 再作为 Grafana 的数据源(provisioning 配置)供查询使用。

VictoriaMetrics 组件还支持构建更高级的拓扑。例如,多个数据中心的 vmagents 可以把指标推送到中心的 VictoriaMetrics(此时中心的 VictoriaMetrics 既可以是单机版也可以是集群版);vmagent 还支持把同一份数据复制到多个目标,用于复制与高可用场景。

查询数据(Query Data)

VictoriaMetrics 提供 HTTP API 处理读查询,该 API 被 Grafana、VMUI 等各种集成使用。API 由两个主要处理器组成,分别服务于瞬时查询与范围查询。

瞬时查询(Instant Query)

瞬时查询在给定的time时刻执行query表达式:

GET | POST /api/v1/query?query=...&time=...&step=...&timeout=...

参数说明:

  • query——MetricsQL 表达式;
  • time——可选,用于计算query的毫秒级时间戳。省略时取now()(当前时间)。支持多种时间格式;
  • step——可选,执行查询时在过去的时间区间内搜索原始样本的间隔(当指定的time处缺少样本时使用)。例如/api/v1/query?query=up&step=1m会在(now()-1m, now()]区间内为up指标查找最后写入的原始样本(首毫秒不包含)。省略时默认5m
  • timeout——可选的查询超时时间,例如timeout=5s,达到超时后查询会被取消。默认取-search.maxQueryDuration命令行标志的值。从源码 app/vmselect/searchutil/searchutil.go 可以看到该标志默认值为 30 秒,且可被timeout参数按查询覆盖为更小的值。单机版该标志传给单节点 VictoriaMetrics,集群版传给vmselect组件。

瞬时查询的结果是与query表达式中过滤条件匹配的时间序列列表。每条返回的序列恰好包含一个(timestamp, value)条目,其中timestamp等于time查询参数,valuequery在该时刻的结果。

为了理解瞬时查询的工作方式,先看一组数据样本:

foo_bar 1.00 1652169600000 # 2022-05-10T08:00:00Z foo_bar 2.00 1652169660000 # 2022-05-10T08:01:00Z foo_bar 3.00 1652169720000 # 2022-05-10T08:02:00Z foo_bar 5.00 1652169840000 # 2022-05-10T08:04:00Z, one point missed foo_bar 5.50 1652169960000 # 2022-05-10T08:06:00Z, one point missed foo_bar 5.50 1652170020000 # 2022-05-10T08:07:00Z foo_bar 4.00 1652170080000 # 2022-05-10T08:08:00Z foo_bar 3.50 1652170260000 # 2022-05-10T08:11:00Z, two points missed foo_bar 3.25 1652170320000 # 2022-05-10T08:12:00Z foo_bar 3.00 1652170380000 # 2022-05-10T08:13:00Z foo_bar 2.00 1652170440000 # 2022-05-10T08:14:00Z foo_bar 1.00 1652170500000 # 2022-05-10T08:15:00Z foo_bar 4.00 1652170560000 # 2022-05-10T08:16:00Z

以上数据包含foo_bar序列的若干样本,样本间间隔从 1m 到 3m 不等。若要在2022-05-10T08:03:00Z时刻获取foo_bar的值,需要发起瞬时查询

curl "http://<victoria-metrics-addr>/api/v1/query?query=foo_bar&time=2022-05-10T08:03:00.000Z"
{ "status": "success", "data": { "resultType": "vector", "result": [ { "metric": { "__name__": "foo_bar" }, "value": [ 1652169780, // 2022-05-10T08:03:00Z "3" ] } ] } }

响应中,VictoriaMetrics 为foo_bar返回了一个值为3的样本-时间戳对。但如果回看原始数据,2022-05-10T08:03:00Z处并没有原始样本。当请求的时间戳处没有原始样本时,VictoriaMetrics 会尝试定位请求时间戳之前最近的样本

VictoriaMetrics 为缺失样本寻找替代值的时间范围默认是5m,可通过step参数覆盖。

瞬时查询可以返回多条时间序列,但每条序列永远只有一个数据样本。瞬时查询常用于以下场景:

  • 获取最后记录的数值;
  • 配合 rollup 函数(如count_over_time);
  • 告警与预计算规则的评估;
  • 在 Grafana 中绘制 Stat 或 Table 面板。

范围查询(Range Query)

范围查询在给定的[start...end]时间范围内、以给定的step执行query表达式:

GET | POST /api/v1/query_range?query=...&start=...&end=...&step=...&timeout=...

参数说明:

  • query——MetricsQL 表达式;
  • start——query求值时间范围的起始时间戳;
  • end——query求值时间范围的结束时间戳;若未设置则自动取当前时间;
  • step——范围查询返回的数据点之间的间隔。query会在startstart+stepstart+2*step、……、start+N*step时刻执行,其中Nstartend之间能容纳的整步数;end仅当它恰好等于start+N*step时才被包含。若未设置则默认5m
  • timeout——可选的查询超时时间,默认取-search.maxQueryDuration标志值(见上文源码出处)。

范围查询的结果是与query匹配的时间序列列表,每条序列包含在startstart+step、……、start+N*step时刻执行查询得到的(timestamp, value)结果。换句话说,范围查询就是在startstart+step、……、start+N*step时刻独立执行的瞬时查询,唯一区别是瞬时查询不返回ephemeral(临时的)样本(详见下文):若数据库在请求时刻没有样本,瞬时查询会尝试用之前的样本填充;而范围查询在相应时刻与 step 下没有样本时则直接返回空。

例如,要获取foo_bar2022-05-10T07:59:00Z2022-05-10T08:17:00Z范围内的值:

curl "http://<victoria-metrics-addr>/api/v1/query_range?query=foo_bar&step=1m&start=2022-05-10T07:59:00.000Z&end=2022-05-10T08:17:00.000Z"
{ "status": "success", "data": { "resultType": "matrix", "result": [ { "metric": { "__name__": "foo_bar" }, "values": [ [1652169600, "1"], [1652169660, "2"], [1652169720, "3"], [1652169780, "3"], [1652169840, "5"], [1652169900, "5"], [1652169960, "5.5"], [1652170020, "5.5"], [1652170080, "4"], [1652170140, "4"], [1652170260, "3.5"], [1652170320, "3.25"], [1652170380, "3"], [1652170440, "2"], [1652170500, "1"], [1652170560, "4"], [1652170620, "4"] ] } ] } }

响应中,VictoriaMetrics 在2022-05-10T07:59:00Z2022-05-10T08:17:00Z区间内为foo_bar返回了17个样本-时间戳对。但原始数据只有 13 个原始样本——这正是因为范围查询本质上是瞬时查询在startend上执行了1 + (start-end)/step次。

上图中蓝色虚线是瞬时查询执行时刻。由于瞬时查询保留了对缺失点返回替代值的能力,图中包含两类数据点:real(真实的)和ephemeral(临时的)。ephemeral数据点总是重复之前最近的原始样本(见图中红色箭头)。

产生临时数据点的行为源于 Pull 模型的特定性:

  • 指标按固定间隔抓取;
  • 监控系统过载时可能跳过某次抓取;
  • 网络问题可能导致抓取失败。

因此范围查询假设:如果缺少原始样本,很可能是某次抓取被跳过,于是用之前的样本填充。当step小于样本实际间隔时同理——事实上,若将step=1s用于同一请求,响应中会有约 1 千个数据点,其中绝大多数是ephemeral的。

有时用于定位数据点的回看窗口不够大,图表中会出现缺口。对于范围查询,回看窗口并不等于step参数,而是按请求时间范围内最近 20 个原始样本的间隔中位数自动计算。这样,VictoriaMetrics 在自动调整回看窗口填补缺口的同时,还能检测到过期序列。源码中-search.maxLookback标志的注释印证了这一点:默认值 0 表示未显式设置时从时间序列数据点间隔动态检测(见 app/vmselect/prometheus/prometheus.go)。

范围查询主要用于在指定时间范围内绘制时序数据,典型场景:

  • 跟踪某指标在给定时间区间内的状态;
  • 关联同一时间区间内多个指标的变化;
  • 观察指标变化的趋势与动态。

如果需要从 VictoriaMetrics 导出原始样本,请参阅单机版文档中的导出 API。

查询延迟与数据可见性(Query Latency)

默认情况下,VictoriaMetrics不会立即返回最近写入的样本,而是检索-search.latencyOffset命令行标志指定时间之前写入的最新结果。该标志默认偏移 30 秒,对queryquery_range均生效,因此可能给人「数据写入 VM 有 30 秒延迟」的印象。

该标志用于避免因「最后一个抓取间隔内只有部分值被抓取」而产生不一致的结果。当-search.latencyOffset设为 0 时,可能出现下图所示的问题:由于最新抓取到的数据不完整,查询结果中的最后几个点会出现抖动或空洞。

设置该标志后,在整个-search.latencyOffset时长内,VM 都会返回-search.latencyOffset之前收集到的最后一个指标值,从而保证最近时间窗口内查询结果的稳定一致。

-search.latencyOffset可以通过latency_offset查询参数按查询覆盖。从源码 app/vmselect/prometheus/prometheus.go 可以看到该标志的完整定义,同时还可看到相关的-search.maxStepForPointsAdjustment(默认 1 分钟,控制/api/v1/query_range对接近当前时间且可能包含不完整数据的点做调整的最大 step)。

另外需要注意:VictoriaMetrics 会把最近几秒内摄入的样本缓存在内存中,再周期性刷盘。这种缓冲提升了数据摄入性能,但缓冲中的样本即使-search.latencyOffsetlatency_offset设为 0 也不会出现在查询结果中。可以给单节点 VictoriaMetrics(或集群版的vmstorage)发送 GET 请求到/internal/force_flushHTTP 处理器,强制将缓冲样本刷盘使其可见。从源码 app/vmstorage/main.go 可以看到该处理器的实现,它支持通过-forceFlushAuthKey设置访问鉴权(app/vmstorage/main.go)。该处理器仅用于调试和测试,请勿在生产环境调用,否则可能显著降低数据摄入性能并增加资源占用。

MetricsQL

VictoriaMetrics 提供专门的查询语言用于执行读查询——MetricsQL。它是一种类 PromQL 的查询语言,内置针对时间序列数据的功能强大的函数集。MetricsQL 与 PromQL 向后兼容,因此共享了大部分查询概念。

过滤

在前面的瞬时查询与范围查询章节中,我们已经用 MetricsQL 查询过foo_bar指标,只需直接写指标名:

foo_bar

一个指标名可能对应多条标签集不同的时间序列,例如:

requests_total{path="/", code="200"} requests_total{path="/", code="403"}

要只选择具有特定标签值的时间序列,在花括号中指定匹配过滤器:

requests_total{code="200"}

上面的查询返回所有名为requests_total且带code="200"标签的时间序列。使用=运算符匹配标签值,负向匹配用!=;过滤器还支持正则正向匹配=~和正则负向匹配!~

requests_total{code=~"2.*"}

过滤器也可以组合:

requests_total{code=~"200", path="/home"}

上面的查询返回所有名为requests_total、同时带有code="200"path="/home"标签的时间序列。

按指标名过滤

有时需要返回多个指标名的所有时间序列。如前文数据模型所述,指标名只是一个特殊的标签——__name__。因此可以对其应用正则:

{__name__=~"requests_(error|success)_total"}

该查询返回requests_error_totalrequests_success_total两个指标的所有序列。

多「or」过滤

MetricsQL 支持选择至少匹配多个「or」过滤器之一的时间序列,此类过滤器在花括号内用or分隔。例如,下面的查询选择带{job="app1",env="prod"}{job="app2",env="dev"}标签的时间序列:

{job="app1",env="prod" or job="app2",env="dev"}

or组的数量任意,每个or组内用,分隔的标签过滤器数量也任意;组内过滤器以and语义应用(同时匹配组内所有过滤器)。

这一功能允许把选中的序列直接传给 rollup 函数(如rate())而无需使用子查询:

rate({job="app1",env="prod" or job="app2",env="dev"}[5m])

如果需要对同一个标签选择匹配多个过滤器的序列,从性能角度考虑,用正则过滤器{label=~"value1|...|valueN"}优于{label="value1" or ... or label="valueN"}

算术运算

MetricsQL 支持所有基本算术运算:

  • 加法+、减法-、乘法*、除法/、取模%、幂^

这使得可以跨多个指标进行计算。例如,下面查询计算错误请求的百分比:

(requests_error_total / (requests_error_total + requests_success_total)) * 100
组合多个序列

用算术运算组合多个时间序列,需要理解匹配规则,否则查询可能报错或产生错误结果。匹配规则的基础很简单:

  • MetricsQL 引擎会剥离算术运算两侧所有时间序列的指标名,但不触碰标签;
  • 对左侧每条时间序列,引擎会在右侧寻找具有相同标签集的时间序列,对每个数据点应用运算,并返回具有相同标签集的结果序列;如果没有匹配则从结果中丢弃该序列;
  • 匹配规则可以通过ignoringongroup_leftgroup_right修饰符增强(向量匹配规则)。
比较运算

MetricsQL 支持以下比较运算符:

  • 等于==、不等于!=、大于>、大于等于>=、小于<、小于等于<=

这些运算符可以像算术运算符一样应用于任意 MetricsQL 表达式,比较运算的结果是只包含匹配数据点的时间序列。例如,下面查询只返回内存占用超过100MB的进程对应序列:

process_resident_memory_bytes > 100*1024*1024
聚合与分组函数

MetricsQL 支持对时间序列进行聚合和分组:先按给定标签集对时间序列分组,再对每组单独应用聚合函数。例如,下面查询返回每个job的内存占用总和:

sum(process_resident_memory_bytes) by (job)

完整的聚合函数列表参见 MetricsQL 文档。

计算速率(rate)

counter 最常用的函数之一是rate,它逐条计算每个匹配时间序列的平均每秒增长率。例如,下面查询展示每个被监控的node_exporter实例暴露的node_network_receive_bytes_total指标的平均每秒数据接收速度:

rate(node_network_receive_bytes_total)

默认情况下,VictoriaMetrics 在step参数(无论传给瞬时查询还是范围查询)指定的回看窗口内、基于原始样本计算rate。也可以在方括号内显式指定计算rate的时间区间:

rate(node_network_receive_bytes_total[5m])

此时 VictoriaMetrics 使用指定的回看窗口5m(5 分钟)计算平均每秒增长率。更大的回看窗口通常带来更平滑的曲线。

rate会剥离内部时间序列的指标名但保留所有标签。如需保留指标名,可在rate(..)后加keep_metric_names修饰符:

rate(node_network_receive_bytes_total) keep_metric_names

rate()只能应用于 counter,对 gauge 应用rate()的结果是未定义的。

可视化时间序列

VictoriaMetrics 内置了查询与可视化指标的图形化界面——VMUI。打开http://victoriametrics:8428/vmui页面,输入查询即可查看结果:

VictoriaMetrics 支持 Prometheus HTTP API,因此也可以像查询 Prometheus 一样,用 Grafana 以相同方式查询它。

修改数据(Modify Data)

VictoriaMetrics 将时间序列数据存储在类 MergeTree(LSM 树)的数据结构中。这种结构对写密集的数据库非常高效,但对数据更新施加了一些限制:修改已写入的时间序列需要重写其所在的整个数据块。由于这一限制,VictoriaMetrics 不支持直接修改数据。

删除(Deletion)

删除时间序列的方法参见单机版文档的「如何删除时间序列」章节。

Relabeling

Relabeling 是在时间序列写入数据库之前对其进行修改的强大机制,可同时应用于 push 与 pull 两种模型。更多细节参见relabeling 文档。

去重(Deduplication)

VictoriaMetrics 支持数据去重,详见单机版文档的去重章节。

降采样(Downsampling)

VictoriaMetrics 支持数据降采样,详见单机版文档的降采样章节。


小结:从核心概念到实战落地

本文围绕 VictoriaMetrics 的核心概念建立了一条完整的知识链路:

  1. 数据模型是根基——指标名 + 标签唯一确定一条时间序列,唯一序列数量即基数(cardinality),它是影响资源占用与性能的最关键变量;四种指标类型(Counter/Gauge/Histogram/Summary)决定「如何测量」,而 TSDB 本身并不感知类型;
  2. 采集模式决定架构——Push 与 Pull 各有优劣,生产中常通过 vmagent 将两者结合,构建「采集 → 过滤/relabel → 转发 → 存储 → 查询可视化」的标准链路;
  3. 查询 API 与 MetricsQL是日常使用的核心——瞬时查询定位单点、范围查询绘制曲线,理解ephemeral数据点与回看窗口机制有助于正确解读图形;-search.latencyOffset/internal/force_flush解释了「30 秒数据延迟」的真相;
  4. 修改数据受限于 LSM 存储模型——不支持直接更新,而是通过删除、relabeling、去重与降采样等机制管理数据生命周期。

阅读完本文后,你可以直接结合仓库中的 keyConcepts.md、MetricsQL.md 与 Single-server-VictoriaMetrics.md 继续深入,也可以对照 lib/timeserieslimits/timeseries_limits.go 与 app/vmselect/prometheus/prometheus.go 等源码,从实现层面验证本文所述的各种行为与默认值。

【免费下载链接】VictoriaMetricsVictoriaMetrics: fast, cost-effective monitoring solution and time series database项目地址: https://gitcode.com/GitHub_Trending/vi/VictoriaMetrics

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询