数据查询

VictoriaMetrics 提供了一个 HTTP API 用于处理数据查询请求。这些 API 被用于各种联合使用,例如Grafana。相同的 API 也被 VMUI 使用,VMUI 是一个用于查询和可视化指标的图形用户界面。

系统包含两个主要的 API,用于处理 Instant Query(即时查询)Range Query(范围查询)

Instant Query(即时查询)

Instant Query 在指定的时间点上执行查询:

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

参数:

  • query: MetricsQL 查询语句.
  • time: 可选参数,以为精度来执行查询。如果省略,时间将设置为now()(当前时间)。time参数值可以用很多种方式指定。
  • step: 可选参数,在执行查询时,在最近多久的时间范围内查找 raw sample 数据(当在指定时间缺少样本时使用)。例如,请求/api/v1/query?query=up&step=1m表示在now()now()-1m之间的时间段内查找指标up的最近写入数据点。如果省略,step默认为5m(5分钟)。
  • timeout: 可选参数,查询超时时间。例如,timeout=5s会在5s后取消请求。默认的超时时间会使用启动参数-search.maxQueryDuration设定的值。 该参数在单机版 VictoriaMetrics 和 vmselect 组件都有支持。

即时查询的结果是一个符合查询表达式中过滤条件的 timeseries 列表。每个返回的 timeseries 都包含一个(timestamp,value)样本条目,即 sample(样本),其中timestamp等于查询参数中的时间,而value代表数据结果。

要了解即时查询的工作原理,让我们从一个原始数据样本开始:

foo_bar 1.00 1652169600000 # 2022-05-10 10:00:00
foo_bar 2.00 1652169660000 # 2022-05-10 10:01:00
foo_bar 3.00 1652169720000 # 2022-05-10 10:02:00
foo_bar 5.00 1652169840000 # 2022-05-10 10:04:00, 丢了 1 个数据点
foo_bar 5.50 1652169960000 # 2022-05-10 10:06:00, 丢了 1 个数据点
foo_bar 5.50 1652170020000 # 2022-05-10 10:07:00
foo_bar 4.00 1652170080000 # 2022-05-10 10:08:00
foo_bar 3.50 1652170260000 # 2022-05-10 10:11:00, 丢了 2 个数据点
foo_bar 3.25 1652170320000 # 2022-05-10 10:12:00
foo_bar 3.00 1652170380000 # 2022-05-10 10:13:00
foo_bar 2.00 1652170440000 # 2022-05-10 10:14:00
foo_bar 1.00 1652170500000 # 2022-05-10 10:15:00
foo_bar 4.00 1652170560000 # 2022-05-10 10:16:00

上面的数据包含了foo_bar时间序列的样本列表,样本之间的时间间隔从1m3m不等。如果我们将这个数据样本绘制在坐标图上,会表现出下面的形式:

为了获取foo_bar这个时间序列在特定时间的数值,比如2022-05-10 10:03:00,在 VictoriaMetrics 中我们使用即时查询:

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

作为返回值,VictoriaMetrics 返回了一个value3的 (timestamp, value) 数据,表示在给定时间2022-05-10 10:03:00foo_bar序列。
但是,如果我们再次查看原始数据样本,会发现2022-05-10 10:03:00并没有写入样本数据。
当请求的时间戳没有原始样本时,VictoriaMetrics 会尝试找到距离请求时间最近的一条样本数据:

VictoriaMetrics 尝试在step时间范围(默认为5m)内寻找最近样本数据作为替代。如果找不到就返回空数据。

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

  • 获取最后写入的值;
  • 用于count_over_time等汇总函数;
  • 用于警报规则,因为告警通常关注最新的数据;
  • 在 Grafana 中绘制 Stat 或 Table 面板。

Range Query(范围查询)

Range Query 在给定的[start...end]时间范围内使用step大小的间隔执行查询语句

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

请求参数:

  • query- MetricsQL 查询语句。
  • start- 执行查询语句的开始时间
  • end- 可选参数,执行查询语句的结束时间,如果没有指定,则自动使用当前时间。
  • step- 可选参数,查询语句返回的数据点之间的间隔。查询语句会在start, start+step,start+2*step, …, end这些时间点上执行查询语句。如果step参数没有传,则默认为5m(5分钟)。
  • timeout- 可选参数,查询超时时间。默认的超时时间会使用启动参数-search.maxQueryDuration指定的值。 该参数在单机版 VictoriaMetrics 和集群版的 vmselect 组件都有支持。

Range Query 的查询结果是一组匹配了query语句中过滤器的 timeseries 列表。结果中的每一个 series 都包含一组(timestamp, value)数据,这些数据就是查询语句在时间start,start+step,start+2*step, …,end这些时间点上执行的查询结果。
换句话说,Range Query 其实就是在start,start+step, …, end这些时间点上独立执行了 Instant Query,然后把执行结果数据放到一块返回。

比如,获取foo_bar指标在时间2022-05-10 09:59:002022-05-10 10:17:00范围内的数据,我们可以发送范围查询请求:

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

在返回结果中,VictoriaMetrics 为foo_bar指标在时间2022-05-10 09:59:002022-05-10 10:17:00范围返回了17个(时间戳,样本值)的数据。但是,我们再看一下原始样本数据,会发现原始数据只有13个数据点。

其原因是 Range Query 实际上在时间范围[start...end]内执行了1 + (start-end)/stepInstant Query。如果我们将请求的返回结果绘制成坐标图,则会表现出如下样子:

蓝色的虚线代表的是系统在这些时间点上执行了 Instant Query。 由于 Instant Query 具有向前追溯的补点能力,所以图中包含两种类型的数据点:真实数据点和临近数据点。临近数据点总是使用最近写入的raw sample(见上图中的红色箭头)。

这种向前追溯补点的特性可追溯到 PULL 模型的客观现状:

  • Metrics 数据是以固定间隔抓取采集的。
  • 如果监控系统负载过高,抓取动作可能会被跳过。
  • 抓取动作可能因为网络原因失败。

根据这些客观现状,假设范围查询缺少raw sample,那么很可能是一次抓取遗漏了,因此它会用之前的raw sample填充。
这种逻辑也适用于step小于raw sample数据间隔的情况。实际上,如果我们为该请求使用参数step=1s,我们就会在返回数据中得到大约1000个数据点,但其中大多数是临时数据点。

有时,用于定位数据点的回溯窗口不够大,图表中会出现空白。对于 Range Query,回溯窗口不等于step参数。它是根据请求时间范围内的前20raw samples之间的时间间隔的中位数得来的。通过这种方式,VictoriaMetrics 就能自动调整回溯窗口以填补空白,并同时可以检测出漏点的情况。

Range Query 主要用于绘制指定时间范围内的时间序列数据。这些查询在以下场景中非常有用:

  • 跟踪给定时间范围内的指标状态;
  • 关联多个指标在时间范围内的变化;
  • 观察指标变化的趋势和动态。

如果你需要导出原始写入指标,可以使用导出接口

数据延时

默认情况下,VictoriaMetrics 不会立即返回最近写入的样本数据。相反,它会检索在-search.latencyOffset启动参数指定的时间之前写入的数据,该参数的默认值为30秒。该参数会影响于queryquery_range两个接口,这可能会给人带来一种数据写入有以30秒延迟的感觉。

此参数的目的是为了防止最近一次采集的数据,并没有在同一时间入库;当只有一部分指标写入到了数据库时,统计类的查询结果就会不准确。

下面我们用几个示意图来解释下将-search.latencyOffset设置为0会出现什么问题:

如果设置了该参数,VM 将在整个-search.latencyOffset持续时间内返回在-search.latencyOffset持续时间之前写入的最后一个指标数据。

可以通过latency_offset查询参数在每个查询请求中覆盖-search.latencyOffset启动参数给的默认值。

VictoriaMetrics 将最近写入的样本数据缓存在内存几秒钟,然后定期将这些样本刷新到磁盘。这种缓冲机制提高了数据写入性能。即使将-search.latencyOffset启动参数设置为0,或者将latency_offset查询参数设置为0,缓存的样本在查询结果中也是看不到的。

你可以向单机版的 VictoriaMetrics/internal/force_flush发送 HTTP 请求,或向集群版的 VictoriaMetrics 的 vmstorage 发送请求,让其强制将缓存的样本刷新到磁盘,使数据能够被立刻查到。
但通常/internal/force_flush接口仅用于调试和测试目的。不要在生产环境中调用它,因为这可能会显著降低数据写入性能并增加资源消耗。

可观测场景,因为数据源头采集一般就是按照30s一次的频率,数据天然就会至少有30s的延时。所以几秒的缓存带来的延时对于观测业务场景是完全可接受的,这对上层业务的感知影响微乎其微,但对 TSDB 带来的性能收益却是非常大的。

查询优化技巧

  • VictoriaMetrics 接收extra_label=<label_name>=<label_value>查询参数(可选),可以强制将额外 Label 过滤器注入到查询语句里。例如,/api/v1/query_range?extra_label=user_id=123&extra_label=group_id=456&query=<query>会自动将{user_id="123",group_id="456"}Label 过滤器添加到查询语句中。
    此功能可用于限制给定租户可见的 timeseries 范围。一般extra_label查询参数由位于 VictoriaMetrics 前面的查询代理服务自动设置。
  • VictoriaMetrics 接收extra_filters[]=series_selector查询参数(可选),可以强制将额外 Label 过滤器注入到查询语句里。例如,/api/v1/query_range?extra_filters[]={env=~"prod|staging",user="xyz"}&query=<query>会自动把{env=~"prod|staging",user="xyz"}Label 过滤器添加到查询语句中。此功能可用于限制给定租户可见的 timeseries 范围。我们建议在 VictoriaMetrics 前面的查询代理自动设置extra_filters[]查询参数。它其实等同于extra_label参数的增强版,因为可以用正则等。
  • VictoriaMetrics 接收多种格式的timestartend查询参数,可参考这些文档
  • VictoriaMetrics 对于/api/v1/query/api/v1/query_range接口支持round_digits查询参数。它可用于指定返回的指标值的保留小数点位数。例如,/api/v1/query?query=avg_over_time(temperature[1h])&round_digits=2会将让返回的指标值保留小数点后面 2 位。
  • VictoriaMetrics 允许在/api/v1/labels/api/v1/label/<labelName>/values接口中使用limit查询参数来限制返回的条目数量。例如,对/api/v1/labels?limit=5的查询请求最多返回 5 个唯一的 Label 值,并忽略其他 Label。如果提供的limit值超过了相应的启动参数-search.maxTagKeys-search.maxTagValues,则会使用启动参数中指定的限制。
  • 默认情况下,VictoriaMetrics 从/api/v1/series/api/v1/labels/api/v1/label/<labelName>/values返回最近一天从00:00 UTC开始的 series 数据,而 Prometheus API 默认返回所有时间的数据。
    如果要选择特定的时间范围的 series 数据,可使用startend参数指定。
    出于性能优化的考虑,VictoriaMetrics 会将指定的start..end时间范围舍入到天的粒度。如果你需要在给定时间范围内获取精确的 Label 集合,请将查询发送到/api/v1/query/api/v1/query_range
  • VictoriaMetrics 在 /api/v1/series 中接收limit查询参数,用于限制返回唯一 series 的数量。例如,对/api/v1/series?limit=5的查询将最多返回 5 个 series,并忽略其余的时间序列。如果提供的limit值超过了相应的启动参数-search.maxSeries的值,则会使用命令行中指定的限制。
  • 此外,VictoriaMetrics还提供了以下接口:
    • /vmui- 基本的 Web UI 界面,阅读这些文档
    • /api/v1/series/count- 返回数据库中 timeseries 的总数量。注意:
      • 该接口扫描了整个数据库的倒排索引,所以如果数据库包含数千万个 series 时间序列,响应速度可能会很慢。
      • 该接口可能把删除 time series 计算在内,这是内部实现机制导致的。
    • /api/v1/status/active_queries- 返回当前正在执行的查询。
    • /api/v1/status/top_queries- 返回下面几个查询列表:
      • 执行最频繁的查询列表 - topByCount
      • 平均执行时间最长的查询列表 - topByAvgDuration
      • 执行时间最长的查询列表 - topBySumDuration
      • 返回的查询个数可以使用topN参数进行限制。历史查询可以使用maxLifetime参数过滤掉。比如,请求/api/v1/status/top_queries?topN=5&maxLifetime=30s返回最近 30 秒内每个类型的 Top5 个查询列表。VictoriaMetrics 会跟踪统计最近-search.queryStats.lastQueriesCount时间内,且执行时间大于search.queryStats.minQueryDuration的查询。

Timestamp 格式

VictoriaMetrics 在查询和导出接口中会接收time,start,end参数。系统这些时间参数支持如下几种写法:

  • Unix 秒级时间戳,float 类型,小数部分代表的是毫秒。比如,1562529662.678
  • Unix 毫秒级时间戳。比如,1562529662678
  • RFC3339。比如, 2022-03-29T01:02:03Zor 2022-03-29T01:02:03+02:30.
  • RFC3339 的省略格式。比如:2022, 2022-03, 2022-03-29, 2022-03-29T01, 2022-03-29T01:02, 2022-03-29T01:02:03。该 RFC3339 格式默认是使用 UTC 时区的。可以使用+hh:mm-hh:mm后缀来指定时区。比如,2022-03-01+08:00代表2022-03-0108:00(中国)时区。
  • 基于当前时间的相对时间。比如1h5m,-1h5mnow-1h5m均代表1小时5分钟之前,这里的now表示当前时间。