菜单
开源

Grafana Mimir 查询分片

Mimir 具备在多台机器上运行单个查询的能力。这是通过将数据集分解成更小的片段来实现的。这些更小的片段称为分片(shards)。每个分片都会在一个局部查询中被查询,这些局部查询由 query-frontend 分发到不同的 queriers 上并行运行。query-frontend 会聚合这些局部查询的结果,以返回完整的查询结果。

查询分片仅应用于 queryquery_range API。

查询分片一览

并非所有查询都可分片。虽然完整查询不可分片,但查询的内部部分仍然可以分片。

特别是关联聚合(如 summinmaxcountavg)可分片,而一些查询函数(如 absentabsent_over_timehistogram_quantilesort_descsort)则不可分片。

在下面的示例中,我们将看一个分片计数为 3 的具体示例。所有包含标签选择器 __query_shard__ 的局部查询都并行执行。concat() 注释用于显示 query-frontend 何时连接/合并局部查询结果。

示例 1:完整查询可分片

promql
sum(rate(metric[1m]))

执行为(假设分片计数为 3)

promql
sum(
  concat(
    sum(rate(metric{__query_shard__="1_of_3"}[1m]))
    sum(rate(metric{__query_shard__="2_of_3"}[1m]))
    sum(rate(metric{__query_shard__="3_of_3"}[1m]))
  )
)

示例 2:内部部分可分片

promql
histogram_quantile(0.99, sum by(le) (rate(metric[1m])))

执行为(假设分片计数为 3)

promql
histogram_quantile(0.99, sum by(le) (
  concat(
    sum by(le) (rate(metric{__query_shard__="1_of_3"}[1m]))
    sum by(le) (rate(metric{__query_shard__="2_of_3"}[1m]))
    sum by(le) (rate(metric{__query_shard__="3_of_3"}[1m]))
  )
))

示例 3:包含两个可分片部分的查询

promql
sum(rate(failed[1m])) / sum(rate(total[1m]))

执行为(假设分片计数为 3)

promql
sum(
  concat(
    sum (rate(failed{__query_shard__="1_of_3"}[1m]))
    sum (rate(failed{__query_shard__="2_of_3"}[1m]))
    sum (rate(failed{__query_shard__="3_of_3"}[1m]))
  )
)
/
sum(
  concat(
    sum (rate(total{__query_shard__="1_of_3"}[1m]))
    sum (rate(total{__query_shard__="2_of_3"}[1m]))
    sum (rate(total{__query_shard__="3_of_3"}[1m]))
  )
)

Flow of a query with two shardable portions

如何启用查询分片

要启用查询分片,您需要通过设置 -query-frontend.parallelize-shardable-queriestrue 来选择启用。

查询的每个可分片部分都会被分割成 -query-frontend.query-sharding-total-shards 个局部查询。如果一个查询有多个可以分片的内部部分,则每个部分都会被分片 -query-frontend.query-sharding-total-shards 次。在某些情况下,这可能导致查询数量爆炸。因此,有一个参数可以修改单个输入查询可以生成的局部查询总数的默认硬限制 128:-query-frontend.query-sharding-max-sharded-queries

当在较大的时间范围内运行查询并且启用了 -query-frontend.split-queries-by-interval 时,-query-frontend.query-sharding-max-sharded-queries 限制适用于按时间分割(首先)和按分片分割(其次)的总查询数量。

例如,如果 -query-frontend.query-sharding-max-sharded-queries=128-query-frontend.split-queries-by-interval=24h,并且您运行了一个跨越 8 天的查询,则每个每日查询最多会有 128 / 8 天 = 每天 16 个局部查询。

在微服务部署中启用查询分片后,query frontends 将开始处理局部查询的聚合。因此,在 query-frontend 上配置一些 PromQL 引擎特定参数也很重要

  • -querier.max-concurrent
  • -querier.timeout
  • -querier.max-samples
  • -querier.default-evaluation-interval
  • -querier.lookback-delta

操作注意事项

将单个查询拆分成多个分片查询会增加需要处理的查询数量。并行化可以缩短查询处理时间,但会增加 querier 组件及其底层数据存储(ingesters 用于近期数据,store-gateway 用于历史数据)的负载。块和索引的缓存层也将承受更大的负载。

我们还建议增加 query-frontend 并行调度的最大查询数量,将之前设置的 -querier.max-query-parallelism 值乘以 -query-frontend.query-sharding-total-shards

查询分片的基数估算(实验性)

当并行分片查询的数量增加时,queriers 及其依赖项的负载也会增加。因此,为了平衡权衡,只在必要时才使用分片查询。返回更多序列的查询(例如基数较高的查询)需要获取更多数据,因此应该拆分成更多的分片。返回少量或不返回序列的查询应该使用更少或根本不使用分片来执行。在确定给定查询使用的分片数量时,分片逻辑可以选择考虑在先前针对相似时间范围执行同一查询时观察到的基数(序列数量)。

要启用此功能,将 -query-frontend.query-sharding-target-series-per-shard 设置为一个值,该值大致表示每个分片应该获取多少序列,并通过 query-frontend.results-cache.* 标志配置结果缓存。即使禁用结果缓存,这也是必需的,因为估算值存储在用于查询结果缓存的同一缓存中。您为此标志设置的值是分片逻辑用于确定查询适当分片数量的几个参数之一。因此,它并非在所有情况下都会被严格遵守,并且每个分片实际获取的序列数量可能会超过限制。这很可能发生在查询基数在短时间内快速变化的情况下。

查询基数的估算仅用于在与禁用基数估算的情况相比时减少分片数量。其他限制总分片数量的参数,例如 -query-frontend.query-sharding-total-shards,即使启用了基数估算并且可能会建议使用更多的分片,仍将为分片数量提供一个上限。

直方图指标 cortex_query_frontend_cardinality_estimation_difference 跟踪估算和实际获取的序列数量之间的差异。

验证

查询统计

query-frontend 记录的查询统计信息允许检查单个查询是否使用了查询分片。字段 sharded_queries 包含并行执行的局部查询数量。

sharded_queries0 时,表示查询不可分片,或者集群或租户禁用了查询分片。这是一个不可分片查询的日志行

sharded_queries=0  param_query="absent(up{job=\"my-service\"})"

sharded_queries 与配置的分片计数匹配时,查询分片正常运行,并且查询只有一个阶段(假设时间分割已禁用或查询未跨越多个天)。以下日志行表示分片计数为 16 的情况

sharded_queries=16 query="sum(rate(prometheus_engine_queries[5m]))"

sharded_queries 是配置分片计数的倍数时,查询分片正常运行,并且查询有多个阶段(假设时间分割已禁用或查询未跨越多个天)。以下日志行显示了一个具有两个阶段且配置分片计数为 16 的查询

sharded_queries=32 query="sum(rate(prometheus_engine_queries{engine=\"ruler\"}[5m]))/sum(rate(prometheus_engine_queries[5m]))"

query-frontend 还暴露了指标,这些指标有助于整体了解查询工作负载的并行性。

您可以运行以下查询来获取成功分片的查询比例

promql
sum(rate(cortex_frontend_query_sharding_rewrites_succeeded_total[$__rate_interval])) /
sum(rate(cortex_frontend_query_sharding_rewrites_attempted_total[$__rate_interval]))

直方图 cortex_frontend_sharded_queries_per_query 可以帮助了解每个查询生成了多少分片子查询。